Passed
Push — master ( 4f4918...3bf43b )
by Francis
01:10
created

JWT::payloadArray()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 2
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 0
dl 0
loc 2
rs 10
c 0
b 0
f 0
1
<?php
2
defined('BASEPATH') OR exit('No direct script access allowed');
3
4
class JWT {
5
6
  const JWT = "jwt";
7
8
  // Signing Algorithms.
9
  const NONE  = 'none';
10
  const HS256 = 'HS256';
11
  const HS512 = 'HS512';
12
  const HS384 = 'HS384';
13
14
  // JWT Standard Algs to PHP Algs.
15
  const ALGOS = [
16
    self::HS256 => 'sha256',
17
    self::HS512 => 'sha512',
18
    self::HS384 => 'sha384'
19
  ];
20
21
  // Internal Variables.
22
  /**
23
   * [private Default Signing Secret]
24
   * @var string
25
   */
26
  private $secret = "ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890";
27
  /**
28
   * [private JWT header array]
29
   * @var array
30
   */
31
  private $header = [];
32
  /**
33
   * [private JWT payload array]
34
   * @var string
35
   */
36
  private $payload = [];
37
  /**
38
   * [private Allow Unsigned JWT]
39
   * @var bool
40
   */
41
  private $allow_unsigned = false;
42
  /**
43
   * [private Set Issued at Time]
44
   * @var bool
45
   */
46
  private $set_iat = true;
47
  /**
48
   * [private Auto expire at. Argument for PHP 'strtotime' function.]
49
   * @var string
50
   */
51
  private $auto_expire;
52
  /**
53
   * [private Default Signing Algorithm]
54
   * @var string
55
   */
56
  private $algorithm;
57
58
  function __construct($params=null) {
59
    if ($params != null) $this->init($params);
60
    get_instance()->load->splint("francis94c/ci-jwt", "%base64");
61
  }
62
  /**
63
   * [init For setting config variables.]
64
   * @param  array  $config Config Options.
65
   */
66
  public function init(array $config) {
67
    $this->secret = $config["secret"] ?? $this->secret;
68
    $this->allow_unsigned = $config["allow_unsigned"] ?? $this->allow_unsigned;
69
    $this->auto_expire = $config["auto_expire"] ?? $this->auto_expire;
70
    $this->algorithm = $config["algorithm"] ?? $this->algorithm;
71
  }
72
  /**
73
   * [header Add an item to the header array.]
74
   * @param  string     $key   Key of the item. e.g "alg", "typ".
75
   * @param  string|int $value Value of the item.
76
   */
77
  public function header(string $key, $value):void {
78
    $this->header[$key] = $value;
79
  }
80
  /**
81
   * [headerArray Returns the header array.]
82
   * @return array Header Array.
83
   */
84
  public function headerArray(): array {
85
    return $this->header;
86
  }
87
  /**
88
   * [payload Adds an item/claim with a key to the payload array.]
89
   * @param string     $key   JWT Claim
90
   * @param string|int $value JWT Claim Value.
91
   */
92
  public function payload(string $key, $value):void {
93
    $this->payload[$key] = $value;
94
  }
95
  /**
96
   * [payloadArray Get the payload array.]
97
   * @return array The payload array.
98
   */
99
  public function payloadArray(): array {
100
    return $this->payload;
1 ignored issue
show
Bug Best Practice introduced by
The expression return $this->payload returns the type string which is incompatible with the type-hinted return array.
Loading history...
101
  }
102
  /**
103
   * [create Start afresh, empty/reset header and ]
104
   * @return [type] [description]
0 ignored issues
show
Documentation Bug introduced by
The doc comment [type] at position 0 could not be parsed: Unknown type name '[' at position 0 in [type].
Loading history...
105
   */
106
  public function create():void {
107
    $this->header = [];
108
    $this->payload = [];
0 ignored issues
show
Documentation Bug introduced by
It seems like array() of type array is incompatible with the declared type string of property $payload.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
109
  }
110
  /**
111
   * [sign description]
112
   * @param  [type] $secret [description]
0 ignored issues
show
Documentation Bug introduced by
The doc comment [type] at position 0 could not be parsed: Unknown type name '[' at position 0 in [type].
Loading history...
113
   * @return [type]         [description]
0 ignored issues
show
Documentation Bug introduced by
The doc comment [type] at position 0 could not be parsed: Unknown type name '[' at position 0 in [type].
Loading history...
114
   */
115
  public function sign(string $secret=null):?string {
116
    // Checks.
117
    if  (count($this->payload) == 0) return null;
1 ignored issue
show
Bug introduced by
$this->payload of type string is incompatible with the type Countable|array expected by parameter $var of count(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

117
    if  (count(/** @scrutinizer ignore-type */ $this->payload) == 0) return null;
Loading history...
118
    // $key is $secret.
119
    $key = $secret ?? $this->secret;
120
    $this->header["alg"] = $this->header["alg"] ?? ($this->algorithm ?? self::HS512);
121
    $this->header["typ"] = $this->header["typ"] ?? self::JWT;
122
    // Generate Issued At Time.
123
    if ($this->set_iat) $this->payload["iat"] = $this->payload["iat"] ?? time();
124
    // Auto Expire.
125
    if ($this->auto_expire != null && !isset($this->payload["exp"])) $this->payload["exp"] = strtotime($this->auto_expire);
126
    $jwt = base64url_encode(json_encode($this->header));
127
    if ($jwt === false) return null;
128
    if ($jwt != "") $jwt .= ".";
129
    $payload = base64url_encode(json_encode($this->payload));
130
    $jwt .= $payload;
131
    if ($key != "") return $this->sign_token($jwt, $key, $this->header["alg"]);
132
    return $jwt . ".";
133
  }
134
  /**
135
   * [token description]
136
   * @return string [description]
137
   */
138
  public function token():?string {
139
    // Checks.
140
    if  (count($this->payload) == 0) return null;
1 ignored issue
show
Bug introduced by
$this->payload of type string is incompatible with the type Countable|array expected by parameter $var of count(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

140
    if  (count(/** @scrutinizer ignore-type */ $this->payload) == 0) return null;
Loading history...
141
    // Begin.
142
    $this->header["alg"] = self::NONE;
143
    if ($this->set_iat) $this->payload["iat"] = $this->payload["iat"] ?? time();
144
    if ($this->auto_expire != null) $this->payload["exp"] = strtotime($this->auto_expire);
145
    return base64url_encode(json_encode($this->header)) . "." . base64url_encode(json_encode($this->payload)) . ".";
146
  }
147
  /**
148
   * [verify description]
149
   * @param  string $jwt    [description]
150
   * @param  string $secret [description]
151
   * @return bool           [description]
152
   */
153
  public function verify(string $jwt, string $secret=null):bool {
154
    if (substr_count($jwt, ".") != 2) return false; // Invalid JWT.
155
    $key = $secret ?? $this->secret;
156
    $parts = explode(".", $jwt);
157
    $header = json_decode(base64url_decode($parts[0]) ,true);
158
    if ($header == null) return false;
159
    $alg = $header["alg"] ?? self::HS256;
160
    $payload = json_decode(base64url_decode($parts[1]) ,true);
161
    if ($payload == null) return false;
162
    if ($parts[2] == "") {
163
      return $this->allow_unsigned;
164
    }
165
    return $this->hashmac($alg, $parts[0] . "." . $parts[1], $parts[2], $key);
166
  }
167
  /**
168
   * [expire Sets expiry date of JWT. This basically assigns the return value of
169
   *         PHP's 'strtotime()' function to the 'exp' field of the payload,
170
   *         passing it the $when argument.
171
   *         see https://www.php.net/manual/en/function.strtotime.php]
172
   * @param string $when Future time e.g +1 Week, +1 week 2 days 4 hours 2 seconds.
173
   */
174
  public function expire(string $when):void {
175
    $this->payload["exp"] = strtotime($when);
176
  }
177
  /**
178
   * [decode description]
179
   * @param  string  $jwt [description]
180
   * @return boolean      [description]
181
   */
182
  public function decode(string $jwt):bool {
183
    $parts = explode(".", $jwt);
184
    $header = json_decode(base64url_decode($parts[0]), true);
185
    if ($header === false) return false;
186
    $payload = json_decode(base64url_decode($parts[1]), true);
187
    if ($payload === false) return false;
188
    $this->header = $header;
189
    $this->payload = $payload;
190
    return true;
191
  }
192
  /**
193
   * [expired description]
194
   * @param  string $jwt [description]
195
   * @return bool        [description]
196
   */
197
  public function expired(string $jwt=null):bool {
198
    $exp = $jwt == null ? ($this->payload["exp"] ?? time()) : $this->get_expired($jwt);
0 ignored issues
show
Bug introduced by
It seems like you are loosely comparing $jwt of type null|string against null; this is ambiguous if the string can be empty. Consider using a strict comparison === instead.
Loading history...
199
    return time() >= $exp;
200
  }
201
  /**
202
   * [hashmac description]
203
   * @param  string $alg       [description]
204
   * @param  string $data      [description]
205
   * @param  string $signature [description]
206
   * @param  string $secret    [description]
207
   * @return bool              [description]
208
   */
209
  private function hashmac(string $alg, string $data, string $signature, string $secret):bool {
210
    return $signature === hash_hmac(self::ALGOS[$alg], $data, $secret);
211
  }
212
  /**
213
   * [get_expired description]
214
   * @param  string $jwt [description]
215
   * @return int         [description]
216
   */
217
  private function get_expired(string $jwt):int {
218
    $parts = explode(".", $jwt);
219
    return json_decode(base64url_decode($parts[1]) ,true)["exp"] ?? time();
220
  }
221
  /**
222
   * [sign_token Sign JWT]
223
   * @param  string $token base64 url encoded header and payload token pair.
224
   * @param  string $key   The scecret used to sign the token.
225
   * @param  string $alg   The algorithm used to sign the token.
226
   * @return string        Complete JWT.
227
   */
228
  private function sign_token(string $token, string $key, string $alg):string {
229
    if ($alg == self::NONE) return $token . ".";
230
    $token = rtrim($token, ".");
231
    $signature = hash_hmac(self::ALGOS[$alg], $token, $key);
232
    return $token . "." . $signature;
233
  }
234
}
235