Completed
Push — master ( 5ae312...cdf4e7 )
by Francis
01:36 queued 10s
created

JWT::iss()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 3
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 2
c 0
b 0
f 0
nc 2
nop 1
dl 0
loc 3
rs 10
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 = array();
32
  /**
33
   * [private JWT payload array]
34
   * @var string
35
   */
36
  private $payload = array();
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
    $this->set_iat = $config["set_iat"] ?? $this->set_iat;
72
  }
73
  /**
74
   * [header Add an item to the header array.]
75
   * @param  string     $key   Key of the item. e.g "alg", "typ".
76
   * @param  string|int $value Value of the item.
77
   */
78
  public function header(string $key, $value=null) {
79
    if ($value === null) return $this->header[$key];
80
    $this->header[$key] = $value;
81
  }
82
  /**
83
   * [headerArray Returns the header array.]
84
   * @return array Header Array.
85
   */
86
  public function headerArray(): array {
87
    return $this->header;
88
  }
89
  /**
90
   * [payload Adds an item/claim with a key to the payload array.]
91
   * @param  string          $key   JWT Claim
92
   * @param  string|int      $value JWT Claim Value.
93
   * @return mixed                  Value of $key if $value == null, else
94
   *                                returns null.
95
   */
96
  public function payload(string $key, $value=null):?mixed {
0 ignored issues
show
Bug introduced by
The type mixed was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
97
    if ($value === null) return $this->payload[$key];
98
    $this->payload[$key] = $value;
99
  }
100
  /**
101
   * [iss Convinient function for setting the iss claim]
102
   * @param  mixed $iss Value to set the 'iss' claim to.
103
   * @return mixed      Value of the 'iss' claim, if the $iss argument wasn't
104
   *                    supplied. Otherwise, null.
105
   */
106
  public function iss(string $iss=null):?mixed {
107
    if ($iss === null) return $this->payload['iss'];
108
    $this->payload['iss'] = $iss;
109
  }
110
  /**
111
   * [payloadArray Get the payload array.]
112
   * @return array The payload array.
113
   */
114
  public function payloadArray(): array {
115
    return $this->payload;
116
  }
117
  /**
118
   * [create Start afresh, empty/reset header and ]
119
   * @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...
120
   */
121
  public function create():void {
122
    $this->header = [];
123
    $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...
124
  }
125
  /**
126
   * [sign description]
127
   * @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...
128
   * @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...
129
   */
130
  public function sign(string $secret=null):?string {
131
    // Checks.
132
    if  (count($this->payload) == 0) return null;
133
    // $key is $secret.
134
    $key = $secret ?? $this->secret;
135
    $this->header["alg"] = $this->header["alg"] ?? ($this->algorithm ?? self::HS512);
136
    $this->header["typ"] = $this->header["typ"] ?? self::JWT;
137
    // Generate Issued At Time.
138
    if ($this->set_iat) $this->payload["iat"] = $this->payload["iat"] ?? time();
139
    // Auto Expire.
140
    if ($this->auto_expire != null && !isset($this->payload["exp"])) $this->payload["exp"] = strtotime($this->auto_expire);
141
    $jwt = base64url_encode(json_encode($this->header));
142
    if ($jwt === false) return null;
143
    if ($jwt != "") $jwt .= ".";
144
    $payload = base64url_encode(json_encode($this->payload));
145
    $jwt .= $payload;
146
    if ($key != "") return $this->sign_token($jwt, $key, $this->header["alg"]);
147
    return $jwt . ".";
148
  }
149
  /**
150
   * [token description]
151
   * @return string [description]
152
   */
153
  public function token():?string {
154
    // Checks.
155
    if  (count($this->payload) == 0) return null;
156
    // Begin.
157
    $this->header["alg"] = self::NONE;
158
    if ($this->set_iat) $this->payload["iat"] = $this->payload["iat"] ?? time();
159
    if ($this->auto_expire != null) $this->payload["exp"] = strtotime($this->auto_expire);
160
    return base64url_encode(json_encode($this->header)) . "." . base64url_encode(json_encode($this->payload)) . ".";
161
  }
162
  /**
163
   * [verify description]
164
   * @param  string $jwt    [description]
165
   * @param  string $secret [description]
166
   * @return bool           [description]
167
   */
168
  public function verify(string $jwt, string $secret=null):bool {
169
    if (substr_count($jwt, ".") != 2) return false; // Invalid JWT.
170
    $key = $secret ?? $this->secret;
171
    $parts = explode(".", $jwt);
172
    $header = json_decode(base64url_decode($parts[0]) ,true);
173
    if ($header == null) return false;
174
    $alg = $header["alg"] ?? self::HS256;
175
    $payload = json_decode(base64url_decode($parts[1]) ,true);
176
    if ($payload == null) return false;
177
    if ($parts[2] == "") {
178
      return $this->allow_unsigned;
179
    }
180
    return $this->hashmac($alg, $parts[0] . "." . $parts[1], $parts[2], $key);
181
  }
182
  /**
183
   * [expire Sets expiry date of JWT. This basically assigns the return value of
184
   *         PHP's 'strtotime()' function to the 'exp' field of the payload,
185
   *         passing it the $when argument.
186
   *         see https://www.php.net/manual/en/function.strtotime.php]
187
   * @param string $when Future time e.g +1 Week, +1 week 2 days 4 hours 2 seconds.
188
   */
189
  public function expire(string $when):void {
190
    $this->payload["exp"] = strtotime($when);
191
  }
192
  /**
193
   * [decode description]
194
   * @param  string  $jwt [description]
195
   * @return boolean      [description]
196
   */
197
  public function decode(string $jwt):bool {
198
    $parts = explode(".", $jwt);
199
    $header = json_decode(base64url_decode($parts[0]), true);
200
    if ($header === false) return false;
201
    $payload = json_decode(base64url_decode($parts[1]), true);
202
    if ($payload === false) return false;
203
    $this->header = $header;
204
    $this->payload = $payload;
205
    return true;
206
  }
207
  /**
208
   * [expired description]
209
   * @param  string $jwt [description]
210
   * @return bool        [description]
211
   */
212
  public function expired(string $jwt=null):bool {
213
    $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...
214
    return time() >= $exp;
215
  }
216
  /**
217
   * [hashmac description]
218
   * @param  string $alg       [description]
219
   * @param  string $data      [description]
220
   * @param  string $signature [description]
221
   * @param  string $secret    [description]
222
   * @return bool              [description]
223
   */
224
  private function hashmac(string $alg, string $data, string $signature, string $secret):bool {
225
    return $signature === hash_hmac(self::ALGOS[$alg], $data, $secret);
226
  }
227
  /**
228
   * [get_expired description]
229
   * @param  string $jwt [description]
230
   * @return int         [description]
231
   */
232
  private function get_expired(string $jwt):int {
233
    $parts = explode(".", $jwt);
234
    return json_decode(base64url_decode($parts[1]) ,true)["exp"] ?? time();
235
  }
236
  /**
237
   * [sign_token Sign JWT]
238
   * @param  string $token base64 url encoded header and payload token pair.
239
   * @param  string $key   The scecret used to sign the token.
240
   * @param  string $alg   The algorithm used to sign the token.
241
   * @return string        Complete JWT.
242
   */
243
  private function sign_token(string $token, string $key, string $alg):string {
244
    if ($alg == self::NONE) return $token . ".";
245
    $token = rtrim($token, ".");
246
    $signature = hash_hmac(self::ALGOS[$alg], $token, $key);
247
    return $token . "." . $signature;
248
  }
249
}
250