ILIAS  release_8 Revision v8.24
FirebaseClient.php
Go to the documentation of this file.
1<?php
2
20
26
35{
39 public const SUPPORTED_ALGORITHMS = array('RS256', 'RS384', 'RS512');
40
41 private ?string $jwtString = null;
42 private ?array $jwtHeaders = null;
43 private ?array $jwtPayload = null;
44 private static ?array $lastHeaders = null;
45 private static ?array $lastPayload = null;
46
52 public static function getSupportedAlgorithms(): array
53 {
55 }
56
62 public function hasJwt(): bool
63 {
64 return !empty($this->jwtString);
65 }
66
72 public function isEncrypted(): bool
73 {
74 return false; // Not supported by this client
75 }
76
83 public function load(string $jwtString, string $privateKey = null): bool
84 {
85 $sections = explode('.', $jwtString);
86 $ok = count($sections) === 3;
87 if ($ok) {
88 $headers = json_decode(JWT::urlsafeB64Decode($sections[0]), true); //changed
89 $payload = json_decode(JWT::urlsafeB64Decode($sections[1]), true); //changed
90 $ok = !is_null($headers) && !is_null($payload);
91 }
92 if ($ok) {
93 $this->jwtString = $jwtString;
94 $this->jwtHeaders = $headers;
95 $this->jwtPayload = $payload;
96 } else {
97 $this->jwtString = null;
98 $this->jwtHeaders = null;
99 $this->jwtPayload = null;
100 }
101
102 return $ok;
103 }
104
110 public function getJweHeaders(): array
111 {
112 return array(); // Encryption not supported by this client
113 }
114
120 public function hasHeader(string $name): bool
121 {
122 return !empty($this->jwtHeaders) && isset($this->jwtHeaders[$name]); //changed
123 }
124
131 public function getHeader(string $name, string $defaultValue = null): string
132 {
133 if ($this->hasHeader($name)) {
134 $value = $this->jwtHeaders[$name]; //changed
135 } else {
136 $value = $defaultValue;
137 }
138
139 return $value;
140 }
141
147 public function getHeaders(): array
148 {
149 return $this->jwtHeaders;
150 }
151
157 public static function getLastHeaders(): array
158 {
159 return self::$lastHeaders;
160 }
161
167 public function hasClaim(string $name): bool
168 {
169 return !empty($this->jwtPayload) && isset($this->jwtPayload[$name]); //changed
170 }
171
178 public function getClaim(string $name, string $defaultValue = null)
179 {
180 if ($this->hasClaim($name)) {
181 $value = $this->jwtPayload[$name]; //changed
182 } else {
183 $value = $defaultValue;
184 }
185
186 return $value;
187 }
188
194 public function getPayload(): array
195 {
196 return $this->jwtPayload;
197 }
198
204 public static function getLastPayload(): array
205 {
206 return self::$lastPayload;
207 }
208
215 public function verify(string $publicKey, string $jku = null): bool
216 {
217 $ok = false;
218 $hasPublicKey = !empty($publicKey);
219 if ($hasPublicKey) {
220 if (is_string($publicKey)) {
221 $json = json_decode($publicKey, true);
222 if (!is_null($json)) {
223 try {
224 $jwks = array('keys' => array($json));
225 $publicKey = static::parseKeySet($jwks);
226 } catch (\Exception $e) {
227 }
228 } else {
229 $publicKey = new Key($publicKey, $this->getHeader('alg'));
230 }
231 }
232 } elseif (!empty($jku)) {
233 $publicKey = $this->fetchPublicKey($jku);
234 }
236 $retry = false;
237
238 do {
239 try {
240 JWT::decode($this->jwtString, $publicKey);
241 $ok = true;
242 } catch (\Exception $e) {
243 Util::logError($e->getMessage());
244 if ($retry) {
245 $retry = false;
246 } elseif ($hasPublicKey && !empty($jku)) {
247 try {
248 $publicKey = $this->fetchPublicKey($jku);
249 $retry = true;
250 } catch (\Exception $e) {
251 }
252 }
253 }
254 } while (!$ok && $retry);
255
256 return $ok;
257 }
258
270 public static function sign(
271 array $payload,
272 string $signatureMethod,
273 string $privateKey,
274 string $kid = null,
275 string $jku = null,
276 string $encryptionMethod = null,
277 string $publicKey = null
278 ): string {
279 if (!empty($encryptionMethod)) {
280 $errorMessage = 'Encrypted tokens not supported by the Firebase JWT client';
281 Util::logError($errorMessage);
282 throw new \Exception($errorMessage);
283 }
285 $sections = explode('.', $jwtString);
286 self::$lastHeaders = json_decode(JWT::urlsafeB64Decode($sections[0]));
287 self::$lastPayload = json_decode(JWT::urlsafeB64Decode($sections[1]));
288
289 return $jwtString;
290 }
291
297 public static function generateKey(string $signatureMethod = 'RS256'): ?string
298 {
299 $privateKey = null;
300 switch ($signatureMethod) {
301 case 'RS512':
302 $size = 4096;
303 break;
304 case 'RS384':
305 $size = 3072;
306 break;
307 default:
308 $size = 2048;
309 break;
310 }
311 $config = array(
312 "private_key_bits" => $size,
313 "private_key_type" => OPENSSL_KEYTYPE_RSA
314 );
315 $res = openssl_pkey_new($config);
316 if (openssl_pkey_export($res, $privateKey)) {
317 $privateKey = str_replace('-----BEGIN PRIVATE KEY-----', '-----BEGIN RSA PRIVATE KEY-----', $privateKey);
318 $privateKey = str_replace('-----END PRIVATE KEY-----', '-----END RSA PRIVATE KEY-----', $privateKey);
319 }
320
321 return $privateKey;
322 }
323
329 public static function getPublicKey(string $privateKey): string
330 {
331 $publicKey = null;
332 $res = openssl_pkey_get_private($privateKey);
333 if ($res !== false) {
334 $details = openssl_pkey_get_details($res);
335 $publicKey = $details['key'];
336 }
337
338 return $publicKey;
339 }
340
348 public static function getJWKS(string $pemKey, string $signatureMethod, string $kid = null): array
349 {
350 $keys['keys'] = array();
351 $res = openssl_pkey_get_private($pemKey);
352 if ($res === false) {
353 $res = openssl_pkey_get_public($pemKey);
354 }
355 if ($res !== false) {
356 $details = openssl_pkey_get_details($res);
357 $key = [
358 'kty' => 'RSA',
359 'n' => JWT::urlsafeB64Encode($details['rsa']['n']),
360 'e' => JWT::urlsafeB64Encode($details['rsa']['e']),
361 'alg' => $signatureMethod,
362 'use' => 'sig'
363 ];
364 if (!empty($kid)) {
365 $key['kid'] = $kid;
366 }
367 $keys['keys'][] = $key;
368 }
369
370 return $keys;
371 }
372
373 ###
374 ### PRIVATE METHODS
375 ###
376
382 private function fetchPublicKey(string $jku): array
383 {
384 $publicKey = array();
385 $http = new HttpMessage($jku);
386 if ($http->send()) {
387 $keys = json_decode($http->response, true);
388 $publicKey = static::parseKeySet($keys);
389 }
390 return $publicKey;
391 }
392
403 private static function parseKeySet(array $jwks): array
404 {
405 $keys = array();
406 if (!isset($jwks['keys'])) {
407 throw new \UnexpectedValueException('"keys" member must exist in the JWK Set');
408 }
409 if (empty($jwks['keys'])) {
410 throw new \InvalidArgumentException('JWK Set did not contain any keys');
411 }
412
413 foreach ($jwks['keys'] as $k => $v) {
414 if (!empty($v['alg'])) {
415 $kid = isset($v['kid']) ? $v['kid'] : $k;
416 if ($key = JWK::parseKey($v)) {
417 $keys[$kid] = $key; //changed from
418 //$keys[$kid] = new Key($key, $v['alg']);
419 }
420 }
421 }
422
423 if (empty($keys)) {
424 throw new \UnexpectedValueException('No supported algorithms found in JWK Set');
425 }
426
427 return $keys;
428 }
429}
static parseKey(array $jwk, string $defaultAlg=null)
Parse a JWK key.
Definition: JWK.php:96
static int $leeway
Definition: JWT.php:41
static encode(array $payload, $key, string $alg, string $keyId=null, array $head=null)
Converts and signs a PHP array into a JWT string.
Definition: JWT.php:199
static decode(string $jwt, $keyOrKeyArray, stdClass &$headers=null)
Decodes a JWT string into a PHP object.
Definition: JWT.php:96
static urlsafeB64Encode(string $input)
Encode a string with URL-safe Base64.
Definition: JWT.php:444
static urlsafeB64Decode(string $input)
Decode a string with URL-safe Base64.
Definition: JWT.php:412
Class to represent an HTTP message request.
Definition: HttpMessage.php:31
Class to implement the JWT interface using the Firebase JWT library from https://github....
getClaim(string $name, string $defaultValue=null)
Get the value of the claim with the specified name.
static sign(array $payload, string $signatureMethod, string $privateKey, string $kid=null, string $jku=null, string $encryptionMethod=null, string $publicKey=null)
Sign the JWT.
const SUPPORTED_ALGORITHMS
Supported signature algorithms.
getJweHeaders()
Get the value of the JWE headers.
static generateKey(string $signatureMethod='RS256')
Generate a new private key in PEM format.
fetchPublicKey(string $jku)
Fetch the public keys from a URL.
hasJwt()
Check if a JWT is defined.
static getJWKS(string $pemKey, string $signatureMethod, string $kid=null)
Get the public JWKS from a key in PEM format.
getPayload()
Get the value of the payload.
static parseKeySet(array $jwks)
Parse a set of JWK keys.
verify(string $publicKey, string $jku=null)
Verify the signature of the JWT.
hasClaim(string $name)
Check whether a JWT has a claim with the specified name.
hasHeader(string $name)
Check whether a JWT has a header with the specified name.
static getLastPayload()
Get the value of the payload for the last signed JWT (before any encryption).
getHeader(string $name, string $defaultValue=null)
Get the value of the header with the specified name.
static getPublicKey(string $privateKey)
Get the public key for a private key.
getHeaders()
Get the value of the headers.
static getLastHeaders()
Get the value of the headers for the last signed JWT (before any encryption).
isEncrypted()
Check if a JWT's content is encrypted.
load(string $jwtString, string $privateKey=null)
Load a JWT from a string.
static getSupportedAlgorithms()
Return an array of supported signature algorithms.
static int $leeway
Leeway (in seconds) to allow when checking timestamps (default is 3 minutes).
Definition: Jwt.php:44
static logError(string $message, bool $showSource=true)
Log an error message.
Definition: Util.php:337
if(!file_exists(getcwd() . '/ilias.ini.php'))
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
Definition: confirmReg.php:20
Interface to represent an HWT client.
$privateKey
Definition: ltiregstart.php:68
$res
Definition: ltiservices.php:69
if(count($parts) !=3) $payload
Definition: ltitoken.php:70
if($format !==null) $name
Definition: metadata.php:247
if(!array_key_exists('PATH_INFO', $_SERVER)) $config
Definition: metadata.php:85
$keys
Definition: metadata.php:204
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
string $encryptionMethod
Algorithm used for encrypting messages.
Definition: System.php:57
string $jku
Endpoint for public key.
Definition: System.php:95
string $kid
Key ID.
Definition: System.php:88
string $signatureMethod
Method used for signing messages.
Definition: System.php:50
string $key
Consumer key/client ID value.
Definition: System.php:193
array $details
Details for error message relating to last request processed.
Definition: System.php:109
$http
Definition: raiseError.php:7