Passed
Push — master ( cffd12...e758dd )
by Francis
01:08
created

JWT::get_expired()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 2
nc 1
nop 1
dl 0
loc 3
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
12
  // JWT Standard Algs to PHP Algs.
13
  const ALGOS = [
14
    self::HS256 => "sha256"
15
  ];
16
17
  // Internal Variables.
18
  /**
19
   * [private Default Signing Secret]
20
   * @var string
21
   */
22
  private $secret = "ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890";
23
  /**
24
   * [private JWT header array]
25
   * @var array
26
   */
27
  private $header = [];
28
  /**
29
   * [private JWT payload array]
30
   * @var string
31
   */
32
  private $payload = [];
33
  /**
34
   * [private Allow Unsigned JWT]
35
   * @var bool
36
   */
37
  private $allow_unsigned = false;
38
  /**
39
   * [private Set Issued at Time]
40
   * @var bool
41
   */
42
  private $set_iat = true;
43
44
  function __construct($params=null) {
45
    if ($params != null) $this->init($params);
46
    get_instance()->load->splint("francis94c/ci-jwt", "%base64");
47
  }
48
  /**
49
   * [init For setting config variables.]
50
   * @param  array  $config Config Options.
51
   */
52
  public function init(array $config) {
53
    $this->secret = $config["secret"] ?? $this->secret;
54
    $this->allow_unsigned = $config["allow_unsigned"] ?? $this->allow_unsigned;
55
  }
56
  /**
57
   * [header Add an item to the header array.]
58
   * @param  string     $key   Key of the item. e.g "alg", "typ".
59
   * @param  string|int $value Value of the item.
60
   */
61
  public function header(string $key, $value):void {
62
    $this->header[$key] = $value;
63
  }
64
  /**
65
   * [headerArray Returns the header array.]
66
   * @return array Header Array.
67
   */
68
  public function headerArray(): array {
69
    return $this->header;
70
  }
71
  /**
72
   * [payload description]
73
   * @param string $key   [description]
74
   * @param [type] $value [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...
75
   */
76
  public function payload(string $key, $value):void {
77
    $this->payload[$key] = $value;
78
  }
79
  /**
80
   * [payloadarray description]
81
   * @return array [description]
82
   */
83
  public function payloadarray(): array {
84
    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...
85
  }
86
  /**
87
   * [create Start afresh, empty/reset header and ]
88
   * @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...
89
   */
90
  public function create():void {
91
    $this->header = [];
92
    $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...
93
  }
94
  /**
95
   * [sign description]
96
   * @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...
97
   * @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...
98
   */
99
  public function sign(string $secret=null):?string {
100
    // Checks.
101
    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

101
    if  (count(/** @scrutinizer ignore-type */ $this->payload) == 0) return null;
Loading history...
102
    // $key is $secret.
103
    $key = $secret ?? $this->secret;
104
    $this->header["alg"] = $this->header["alg"] ?? self::HS256;
105
    $this->header["typ"] = $this->header["typ"] ?? self::JWT;
106
    $jwt = base64url_encode(json_encode($this->header));
107
    if ($jwt === false) return null;
108
    if ($jwt != "") $jwt .= ".";
109
    $payload = base64url_encode(json_encode($this->payload));
110
    $jwt .= $payload;
111
    if ($key != "") return $this->sign_token($jwt, $key, $this->header["alg"]);
112
    return $jwt . ".";
113
  }
114
  /**
115
   * [token description]
116
   * @return string [description]
117
   */
118
  public function token():?string {
119
    // Checks.
120
    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

120
    if  (count(/** @scrutinizer ignore-type */ $this->payload) == 0) return null;
Loading history...
121
    // Begin.
122
    $this->header["alg"] = self::NONE;
123
    if ($this->set_iat) $this->payload["iat"] = $this->payload["iat"] ?? time();
124
    return base64url_encode(json_encode($this->header)) . "." . base64url_encode(json_encode($this->payload)) . ".";
125
  }
126
  /**
127
   * [verify description]
128
   * @param  string $jwt    [description]
129
   * @param  string $secret [description]
130
   * @return bool           [description]
131
   */
132
  public function verify(string $jwt, string $secret=null):bool {
133
    if (substr_count($jwt, ".") != 2) return false; // Invalid JWT.
134
    $key = $secret ?? $this->secret;
135
    $parts = explode(".", $jwt);
136
    $header = json_decode(base64url_decode($parts[0]) ,true);
137
    if ($header == null) return false;
138
    $alg = $header["alg"] ?? self::HS256;
139
    $payload = json_decode(base64url_decode($parts[1]) ,true);
140
    if ($payload == null) return false;
141
    if ($parts[2] == "") {
142
      return $this->allow_unsigned;
143
    }
144
    return $this->hashmac($alg, $parts[0] . "." . $parts[1], $parts[2], $key);
145
  }
146
  /**
147
   * [decode description]
148
   * @param  string  $jwt [description]
149
   * @return boolean      [description]
150
   */
151
  public function decode(string $jwt):bool {
152
    $parts = explode(".", $jwt);
153
    $header = json_decode(base64url_decode($parts[0]), true);
154
    if ($header === false) return false;
155
    $payload = json_decode(base64url_decode($parts[1]), true);
156
    if ($payload === false) return false;
157
    $this->header = $header;
158
    $this->payload = $payload;
159
    return true;
160
  }
161
  /**
162
   * [expired description]
163
   * @param  string $jwt [description]
164
   * @return bool        [description]
165
   */
166
  public function expired(string $jwt=null):bool {
167
    $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...
168
    return time() >= $exp;
169
  }
170
  /**
171
   * [hashmac description]
172
   * @param  string $alg       [description]
173
   * @param  string $data      [description]
174
   * @param  string $signature [description]
175
   * @param  string $secret    [description]
176
   * @return bool              [description]
177
   */
178
  private function hashmac(string $alg, string $data, string $signature, string $secret):bool {
179
    return hash_hmac(self::ALGOS[$alg], $data, $secret) === $signature;
180
  }
181
  /**
182
   * [get_expired description]
183
   * @param  string $jwt [description]
184
   * @return int         [description]
185
   */
186
  private function get_expired(string $jwt):int {
187
    $parts = explode(".", $jwt);
188
    return json_decode(base64url_decode($parts[1]) ,true)["exp"] ?? time();
189
  }
190
  /**
191
   * [sign_token Sign JWT]
192
   * @param  string $token base64 url encoded header and payload token pair.
193
   * @param  string $key   The scecret used to sign the token.
194
   * @param  string $alg   The algorithm used to sign the token.
195
   * @return string        Complete JWT.
196
   */
197
  private function sign_token(string $token, string $key, string $alg):string {
198
    if ($alg == self::NONE) return $token . ".";
199
    $token = rtrim($token, ".");
200
    $signature = hash_hmac(self::ALGOS[$alg], $token, $key);
201
    return $token . "." . $signature;
202
  }
203
}
204