Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
0.00% |
0 / 38 |
|
0.00% |
0 / 5 |
CRAP | |
0.00% |
0 / 1 |
JWT | |
0.00% |
0 / 38 |
|
0.00% |
0 / 5 |
306 | |
0.00% |
0 / 1 |
createSignature | |
0.00% |
0 / 9 |
|
0.00% |
0 / 1 |
12 | |||
createJWT | |
0.00% |
0 / 8 |
|
0.00% |
0 / 1 |
12 | |||
getHeader | |
0.00% |
0 / 5 |
|
0.00% |
0 / 1 |
12 | |||
getPayload | |
0.00% |
0 / 5 |
|
0.00% |
0 / 1 |
12 | |||
validateJWT | |
0.00% |
0 / 11 |
|
0.00% |
0 / 1 |
30 |
1 | <?php |
2 | /** |
3 | * Jingga |
4 | * |
5 | * PHP Version 8.1 |
6 | * |
7 | * @package phpOMS\Auth |
8 | * @copyright Dennis Eichhorn |
9 | * @license OMS License 2.0 |
10 | * @version 1.0.0 |
11 | * @link https://jingga.app |
12 | */ |
13 | declare(strict_types=1); |
14 | |
15 | namespace phpOMS\Session; |
16 | |
17 | use phpOMS\Utils\Encoding\Base64Url; |
18 | |
19 | /** |
20 | * JWT class. |
21 | * |
22 | * Creates, parses and validates JWT tokens. |
23 | * |
24 | * Header: base64url([algo: ..., typ: jwt]) |
25 | * Payload: base64url([...]) |
26 | * Signature: hmac(Header . Payload, secret) |
27 | * |
28 | * @package phpOMS\Auth |
29 | * @license OMS License 2.0 |
30 | * @link https://jingga.app |
31 | * @since 1.0.0 |
32 | */ |
33 | final class JWT |
34 | { |
35 | /** |
36 | * Create JWT signature part |
37 | * |
38 | * @param string $secret Secret (at least 256 bit) |
39 | * @param array{alg:string, typ:string} $header Header |
40 | * @param array{sub:string, uid?:string, name?:string, iat:string} $payload Payload |
41 | * |
42 | * @return string hmac(Header64 . Payload64, secret) |
43 | * |
44 | * @since 1.0.0 |
45 | */ |
46 | private static function createSignature(string $secret, array $header, array $payload) : string |
47 | { |
48 | $headerJson = \json_encode($header); |
49 | $payloadJson = \json_encode($payload); |
50 | |
51 | if (!\is_string($headerJson) || !\is_string($payloadJson)) { |
52 | return ''; |
53 | } |
54 | |
55 | $header64 = Base64Url::encode($headerJson); |
56 | $payload64 = Base64Url::encode($payloadJson); |
57 | |
58 | $algorithm = ''; |
59 | $algorithm = 'sha256'; |
60 | |
61 | return \hash_hmac($algorithm, $header64 . '.' . $payload64, $secret, false); |
62 | } |
63 | |
64 | /** |
65 | * Create JWT token |
66 | * |
67 | * @param string $secret Secret (at least 256 bit) |
68 | * @param array{alg:string, typ:string} $header Header |
69 | * @param array{sub:string, uid?:string, name?:string, iat:string} $payload Payload |
70 | * |
71 | * @return string Header64 . Payload64 . hmac(Header64 . Payload64, secret) |
72 | * |
73 | * @since 1.0.0 |
74 | */ |
75 | public static function createJWT(string $secret, array $header, array $payload) : string |
76 | { |
77 | $headerJson = \json_encode($header); |
78 | $payloadJson = \json_encode($payload); |
79 | |
80 | if (!\is_string($headerJson) || !\is_string($payloadJson)) { |
81 | return ''; |
82 | } |
83 | |
84 | $header64 = Base64Url::encode($headerJson); |
85 | $payload64 = Base64Url::encode($payloadJson); |
86 | |
87 | $signature = self::createSignature($secret, $header, $payload); |
88 | |
89 | return $header64 . $payload64 . Base64Url::encode($signature); |
90 | } |
91 | |
92 | /** |
93 | * Get the header from the jwt string |
94 | * |
95 | * @param string $jwt JWT string |
96 | * |
97 | * @return array |
98 | * |
99 | * @since 1.0.0 |
100 | */ |
101 | public static function getHeader(string $jwt) : array |
102 | { |
103 | $explode = \explode('.', $jwt); |
104 | |
105 | if (\count($explode) !== 3) { |
106 | return []; |
107 | } |
108 | |
109 | $json = \json_decode(Base64Url::decode($explode[0]), true); |
110 | |
111 | return \is_array($json) ? $json : []; |
112 | } |
113 | |
114 | /** |
115 | * Get the payload from the jwt string |
116 | * |
117 | * @param string $jwt JWT string |
118 | * |
119 | * @return array |
120 | * |
121 | * @since 1.0.0 |
122 | */ |
123 | public static function getPayload(string $jwt) : array |
124 | { |
125 | $explode = \explode('.', $jwt); |
126 | |
127 | if (\count($explode) !== 3) { |
128 | return []; |
129 | } |
130 | |
131 | $json = \json_decode(Base64Url::decode($explode[1]), true); |
132 | |
133 | return \is_array($json) ? $json : []; |
134 | } |
135 | |
136 | /** |
137 | * Validate JWT token integrity |
138 | * |
139 | * @param string $secret Secret (at least 256 bit) |
140 | * @param string $jwt JWT token [Header64 . Payload64 . hmac(Header64 . Payload64, secret)] |
141 | * |
142 | * @return bool |
143 | * |
144 | * @since 1.0.0 |
145 | */ |
146 | public static function validateJWT(string $secret, string $jwt) : bool |
147 | { |
148 | $explode = \explode('.', $jwt); |
149 | |
150 | if (\count($explode) !== 3) { |
151 | return false; |
152 | } |
153 | |
154 | try { |
155 | $header = \json_decode(Base64Url::decode($explode[0]), true); |
156 | $payload = \json_decode(Base64Url::decode($explode[1]), true); |
157 | |
158 | if (!\is_array($header) || !\is_array($payload)) { |
159 | return false; |
160 | } |
161 | |
162 | /** @var array{alg:string, typ:string} $header */ |
163 | /** @var array{sub:string, uid?:string, name?:string, iat:string} $payload */ |
164 | $signature = self::createSignature($secret, $header, $payload); |
165 | |
166 | return \hash_equals($signature, $explode[2]); |
167 | } catch (\Throwable $_) { |
168 | return false; |
169 | } |
170 | } |
171 | } |