1 | <?php |
||
2 | declare(strict_types=1); |
||
3 | use PHPUnit\Framework\TestCase; |
||
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)); |
||
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)) . "." . |
||
121 | base64url_encode(json_encode($payload)) . "." . $parts[2]; |
||
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 |
Adding explicit visibility (
private
,protected
, orpublic
) is generally recommend to communicate to other developers how, and from where this method is intended to be used.