1 | <?php |
||||
2 | declare(strict_types=1); |
||||
3 | use PHPUnit\Framework\TestCase; |
||||
0 ignored issues
–
show
|
|||||
4 | |||||
5 | class JWTTest extends TestCase { |
||||
6 | /** |
||||
7 | * Code Igniter Instance. |
||||
8 | * @var object |
||||
9 | */ |
||||
10 | private static $ci; |
||||
11 | /** |
||||
12 | * Package name for simplicity |
||||
13 | * @var string |
||||
14 | */ |
||||
15 | private const PACKAGE = "francis94c/ci-jwt"; |
||||
16 | |||||
17 | /** |
||||
18 | * Prerquisites for the Unit Tests. |
||||
19 | * |
||||
20 | * @covers JWT::__construct |
||||
21 | */ |
||||
22 | public static function setUpBeforeClass(): void { |
||||
23 | self::$ci =& get_instance(); |
||||
24 | /** |
||||
25 | * [$params Config Items.] |
||||
26 | * |
||||
27 | * @var array |
||||
28 | * |
||||
29 | * Description:- |
||||
30 | * |
||||
31 | * secret: The secret strng used in signing the JWT. |
||||
32 | * alg: Signing Algorithm. |
||||
33 | * set_iat: Automatically set issued at time on payloads. |
||||
34 | */ |
||||
35 | $config = [ |
||||
36 | "secret" => "GHfsjfblsgo8r84nNOHHdgdgdgdyf758y8hyttjuoljlhkmjhgO8HOHLHLd", |
||||
37 | "alg" => "HS256", |
||||
38 | "set_iat" => true |
||||
39 | ]; |
||||
40 | self::$ci->load->package(self::PACKAGE); |
||||
41 | self::$ci->jwt->init($config); |
||||
42 | } |
||||
43 | /** |
||||
44 | * [testHelperMethodsExist Test Helper Method Exist.] |
||||
45 | * @testdox Helper Methods. |
||||
46 | */ |
||||
47 | function testHelperMethodsExist() { |
||||
0 ignored issues
–
show
|
|||||
48 | $this->assertTrue(function_exists("base64url_encode")); |
||||
49 | $this->assertTrue(function_exists("base64url_decode")); |
||||
50 | } |
||||
51 | /** |
||||
52 | * [testHeader test header section of JWT] |
||||
53 | * |
||||
54 | * @testdox Header Section. |
||||
55 | */ |
||||
56 | function testHeader():void { |
||||
0 ignored issues
–
show
|
|||||
57 | self::$ci->jwt->header("alg", JWT::HS256); |
||||
58 | self::$ci->jwt->header("typ", JWT::JWT); |
||||
59 | $header = self::$ci->jwt->headerArray(); |
||||
60 | $this->assertEquals(JWT::HS256, $header["alg"]); |
||||
61 | $this->assertEquals(JWT::JWT, $header["typ"]); |
||||
62 | } |
||||
63 | /** |
||||
64 | * [testPayload test payload section.] |
||||
65 | * |
||||
66 | * RFC7519 Section 4.1 |
||||
67 | * |
||||
68 | * @depends testHeader |
||||
69 | * |
||||
70 | * @testdox Payload Test. |
||||
71 | */ |
||||
72 | public function testPayload():void { |
||||
73 | self::$ci->jwt->payload("iss", "www.example.com"); |
||||
74 | self::$ci->jwt->payload("sub", "francis"); |
||||
75 | self::$ci->jwt->payload("aud", "my_server"); |
||||
76 | self::$ci->jwt->payload("exp", 23456789967); |
||||
77 | self::$ci->jwt->payload("iat", 12345677778); |
||||
78 | $payload = self::$ci->jwt->payloadArray(); |
||||
79 | $this->assertEquals("www.example.com", $payload["iss"]); |
||||
80 | $this->assertEquals("francis", $payload["sub"]); |
||||
81 | $this->assertEquals("my_server", $payload["aud"]); |
||||
82 | $this->assertEquals(23456789967, $payload["exp"]); |
||||
83 | $this->assertEquals(12345677778, $payload["iat"]); |
||||
84 | } |
||||
85 | /** |
||||
86 | * [testBase64Methods descriptio] |
||||
87 | * |
||||
88 | * @depends testHelperMethodsExist |
||||
89 | * |
||||
90 | * @testdox Test Base64 Function. |
||||
91 | */ |
||||
92 | public function testBase64Functions():void { |
||||
93 | $data = "The Quick Brown Fox Jumped over the Lazy Dog."; |
||||
94 | $b64 = base64url_encode($data); |
||||
95 | $this->assertEquals($data, base64url_decode($b64)); |
||||
0 ignored issues
–
show
It seems like
$b64 can also be of type false ; however, parameter $data of base64url_decode() does only seem to accept string , maybe add an additional type check?
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||
96 | } |
||||
97 | /** |
||||
98 | * [testSigning description] |
||||
99 | * |
||||
100 | * @depends testPayload |
||||
101 | * |
||||
102 | * @testdox Test Signing. |
||||
103 | */ |
||||
104 | public function testSigning():void { |
||||
105 | $jwt = self::$ci->jwt->sign(); |
||||
106 | $parts = explode(".", $jwt); |
||||
107 | $header = json_decode(base64url_decode($parts[0]), true); |
||||
108 | $this->assertEquals(JWT::HS256, $header["alg"]); |
||||
109 | $this->assertEquals(JWT::JWT, $header["typ"]); |
||||
110 | $payload = json_decode(base64url_decode($parts[1]), true); |
||||
111 | $this->assertEquals("www.example.com", $payload["iss"]); |
||||
112 | $this->assertEquals("francis", $payload["sub"]); |
||||
113 | $this->assertEquals("my_server", $payload["aud"]); |
||||
114 | $this->assertEquals(23456789967, $payload["exp"]); |
||||
115 | $this->assertEquals(12345677778, $payload["iat"]); |
||||
116 | // Verify Signature. |
||||
117 | $this->assertTrue(self::$ci->jwt->verify($jwt)); |
||||
118 | // Let's Tamper with the JWT's integrity. |
||||
119 | $payload["sub"] = "john"; |
||||
120 | $jwt = base64url_encode(json_encode($header)) . "." . |
||||
0 ignored issues
–
show
Are you sure
base64url_encode(json_encode($header)) of type false|string can be used in concatenation ?
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||
121 | base64url_encode(json_encode($payload)) . "." . $parts[2]; |
||||
0 ignored issues
–
show
Are you sure
base64url_encode(json_encode($payload)) of type false|string can be used in concatenation ?
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||
122 | $this->assertFalse(self::$ci->jwt->verify($jwt)); |
||||
123 | // Tamper by signing with a different secret. |
||||
124 | $signature = hash_hmac( |
||||
125 | "sha256", |
||||
126 | base64url_encode(json_encode($header)) . "." . |
||||
127 | base64url_encode(json_encode($payload)), |
||||
128 | "ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890"); |
||||
129 | $jwt = base64url_encode(json_encode($header)) . "." . |
||||
130 | base64url_encode(json_encode($payload)) . "." . $signature; |
||||
131 | // Verify with Original Secret. |
||||
132 | $this->assertFalse(self::$ci->jwt->verify($jwt)); |
||||
133 | // Verify with fake Secret. |
||||
134 | $this->assertTrue(self::$ci->jwt->verify($jwt, "ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890")); |
||||
135 | // Restore integrity. |
||||
136 | $payload["sub"] = "francis"; |
||||
137 | $jwt = base64url_encode(json_encode($header)) . "." . |
||||
138 | base64url_encode(json_encode($payload)) . "." . $parts[2]; |
||||
139 | $this->assertTrue(self::$ci->jwt->verify($jwt)); |
||||
140 | } |
||||
141 | /** |
||||
142 | * [testEmpty description] |
||||
143 | * |
||||
144 | * @depends testSigning |
||||
145 | * |
||||
146 | * @tesdox Test Empty. |
||||
147 | */ |
||||
148 | public function testEmpty():void { |
||||
149 | self::$ci->jwt->create(); |
||||
150 | $this->assertEmpty(self::$ci->jwt->headerArray()); |
||||
151 | $this->assertEmpty(self::$ci->jwt->payloadArray()); |
||||
152 | } |
||||
153 | /** |
||||
154 | * [testEmptyPayload description] |
||||
155 | * |
||||
156 | * @depends testEmpty |
||||
157 | * |
||||
158 | * @tesdox Test Signed Empty Payload. |
||||
159 | */ |
||||
160 | public function testUnsignedToken():void { |
||||
161 | $this->assertNull(self::$ci->jwt->token()); |
||||
162 | self::$ci->jwt->payload("iss", "server"); |
||||
163 | $jwt = self::$ci->jwt->token(); |
||||
164 | $this->assertFalse(self::$ci->jwt->verify($jwt)); |
||||
165 | self::$ci->jwt->init(["allow_unsigned" => true]); |
||||
166 | $this->assertTrue(self::$ci->jwt->verify($jwt)); |
||||
167 | // Test auto-generation of time stamp with unsigned token. |
||||
168 | $this->assertIsNumeric(json_decode(base64url_decode(explode(".", $jwt)[1]), true)["iat"]); |
||||
169 | self::$ci->jwt->init(["allow_unsigned" => false]); |
||||
170 | // Test auto-generation of time stamp with signed token. |
||||
171 | self::$ci->jwt->create(); |
||||
172 | self::$ci->jwt->payload("iss", "server"); |
||||
173 | $jwt = self::$ci->jwt->sign(); |
||||
174 | $this->assertIsNumeric(json_decode(base64url_decode(explode(".", $jwt)[1]), true)["iat"]); |
||||
175 | } |
||||
176 | /** |
||||
177 | * [testDecode description] |
||||
178 | * |
||||
179 | * @depends testUnsignedToken |
||||
180 | */ |
||||
181 | public function testDecode():void { |
||||
182 | self::$ci->jwt->create(); |
||||
183 | self::$ci->jwt->header("alg", JWT::HS256); |
||||
184 | self::$ci->jwt->header("typ", JWT::JWT); |
||||
185 | self::$ci->jwt->payload("iss", "www.example.com"); |
||||
186 | self::$ci->jwt->payload("sub", "francis"); |
||||
187 | self::$ci->jwt->payload("aud", "my_server"); |
||||
188 | self::$ci->jwt->payload("exp", 23456789967); |
||||
189 | self::$ci->jwt->payload("iat", 12345677778); |
||||
190 | // Get Token |
||||
191 | $jwt = self::$ci->jwt->sign(); |
||||
192 | self::$ci->jwt->create(); |
||||
193 | self::$ci->jwt->decode($jwt); |
||||
194 | $header = self::$ci->jwt->headerArray(); |
||||
195 | $this->assertEquals(JWT::HS256, $header["alg"]); |
||||
196 | $this->assertEquals(JWT::JWT, $header["typ"]); |
||||
197 | $payload = self::$ci->jwt->payloadArray(); |
||||
198 | $this->assertEquals("www.example.com", $payload["iss"]); |
||||
199 | $this->assertEquals("francis", $payload["sub"]); |
||||
200 | $this->assertEquals("my_server", $payload["aud"]); |
||||
201 | $this->assertEquals(23456789967, $payload["exp"]); |
||||
202 | $this->assertEquals(12345677778, $payload["iat"]); |
||||
203 | } |
||||
204 | /** |
||||
205 | * [testExpired Test expiry date of JWTs.] |
||||
206 | * |
||||
207 | * @depends testDecode |
||||
208 | */ |
||||
209 | public function testExpired():void { |
||||
210 | // Internal Check. |
||||
211 | self::$ci->jwt->payload("exp", time()); |
||||
212 | $this->assertTrue(self::$ci->jwt->expired()); |
||||
213 | self::$ci->jwt->payload("exp", time() + (7 * 24 * 60 * 60)); |
||||
214 | $this->assertFalse(self::$ci->jwt->expired()); |
||||
215 | |||||
216 | // Supplied Token Check. |
||||
217 | self::$ci->jwt->payload("exp", time()); |
||||
218 | $jwt = self::$ci->jwt->sign(); |
||||
219 | $this->assertTrue(self::$ci->jwt->expired($jwt)); |
||||
220 | self::$ci->jwt->payload("exp", time() + (7 * 24 * 60 * 60)); |
||||
221 | $jwt = self::$ci->jwt->sign(); |
||||
222 | $this->assertFalse(self::$ci->jwt->expired($jwt)); |
||||
223 | // Empty Expire. |
||||
224 | // Absent exp field means token cannot expire. |
||||
225 | self::$ci->jwt->create(); |
||||
226 | $this->assertFalse(self::$ci->jwt->expired()); |
||||
227 | } |
||||
228 | /** |
||||
229 | * [testSetExpired description] |
||||
230 | * |
||||
231 | * @depends testExpired |
||||
232 | * |
||||
233 | * @testdox Test Set Expiry Date on Token. |
||||
234 | */ |
||||
235 | public function testSetExpired():void { |
||||
236 | self::$ci->jwt->expire("+1 Day"); |
||||
237 | $this->assertFalse(self::$ci->jwt->expired()); |
||||
238 | } |
||||
239 | /** |
||||
240 | * [setDefaultExpiryDate description] |
||||
241 | * |
||||
242 | */ |
||||
243 | public function testSetDefaultExpiryDateForSignedToken():void { |
||||
244 | self::$ci->jwt->create(); |
||||
245 | self::$ci->jwt->payload("iss", "www.example.com"); |
||||
246 | self::$ci->jwt->init(["auto_expire" => "+1 Seconds"]); |
||||
247 | $jwt = self::$ci->jwt->sign(); |
||||
248 | $this->assertFalse(self::$ci->jwt->expired($jwt)); |
||||
249 | sleep(1); // Allow Token to expire. |
||||
250 | $this->assertTrue(self::$ci->jwt->expired($jwt)); |
||||
251 | } |
||||
252 | /** |
||||
253 | * [testSetDefaultExpiryDateForUnSignedToken description] |
||||
254 | */ |
||||
255 | public function testSetDefaultExpiryDateForUnSignedToken(): void { |
||||
256 | self::$ci->jwt->create(); |
||||
257 | self::$ci->jwt->payload("iss", "www.example.com"); |
||||
258 | self::$ci->jwt->init(["auto_expire" => "+1 Seconds"]); |
||||
259 | $jwt = self::$ci->jwt->token(); |
||||
260 | $this->assertFalse(self::$ci->jwt->expired($jwt)); |
||||
261 | sleep(1); // Allow Token to expire. |
||||
262 | $this->assertTrue(self::$ci->jwt->expired($jwt)); |
||||
263 | } |
||||
264 | } |
||||
265 | ?> |
||||
0 ignored issues
–
show
It is not recommended to use PHP's closing tag
?> in files other than templates.
Using a closing tag in PHP files that only contain PHP code is not recommended as you might accidentally add whitespace after the closing tag which would then be output by PHP. This can cause severe problems, for example headers cannot be sent anymore. A simple precaution is to leave off the closing tag as it is not required, and it also has no negative effects whatsoever. ![]() |
|||||
266 |
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:For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths