LTI Integration Library 4.10.3
PHP class library for building LTI integrations
 
Loading...
Searching...
No Matches
FirebaseClient.php
1<?php
2
3namespace ceLTIc\LTI\Jwt;
4
5use Firebase\JWT\JWT;
6use Firebase\JWT\JWK;
7use Firebase\JWT\Key;
10
19{
20
24 const SUPPORTED_ALGORITHMS = array('RS256', 'RS384', 'RS512');
25
31 private $jwtString = null;
32
38 private $jwtHeaders = null;
39
45 private $jwtPayload = null;
46
52 private static $lastHeaders = null;
53
59 private static $lastPayload = null;
60
66 public static function getSupportedAlgorithms()
67 {
69 }
70
76 public function hasJwt()
77 {
78 return !empty($this->jwtString);
79 }
80
86 public function isEncrypted()
87 {
88 return false; // Not supported by this client
89 }
90
99 public function load($jwtString, $privateKey = null)
100 {
101 $sections = explode('.', $jwtString);
102 $ok = count($sections) === 3;
103 if ($ok) {
104 $headers = Util::jsonDecode(JWT::urlsafeB64Decode($sections[0]));
105 $payload = Util::jsonDecode(JWT::urlsafeB64Decode($sections[1]));
106 $ok = !is_null($headers) && !is_null($payload);
107 }
108 if ($ok) {
109 $this->jwtString = $jwtString;
110 $this->jwtHeaders = $headers;
111 $this->jwtPayload = $payload;
112 } else {
113 $this->jwtString = null;
114 $this->jwtHeaders = null;
115 $this->jwtPayload = null;
116 }
117
118 return $ok;
119 }
120
126 public function getJweHeaders()
127 {
128 return array(); // Encryption not supported by this client
129 }
130
138 public function hasHeader($name)
139 {
140 return !empty($this->jwtHeaders) && isset($this->jwtHeaders->{$name});
141 }
142
151 public function getHeader($name, $defaultValue = null)
152 {
153 if ($this->hasHeader($name)) {
154 $value = $this->jwtHeaders->{$name};
155 } else {
156 $value = $defaultValue;
157 }
158
159 return $value;
160 }
161
167 public function getHeaders()
168 {
169 return $this->jwtHeaders;
170 }
171
177 public static function getLastHeaders()
178 {
179 return self::$lastHeaders;
180 }
181
189 public function hasClaim($name)
190 {
191 return !empty($this->jwtPayload) && isset($this->jwtPayload->{$name});
192 }
193
202 public function getClaim($name, $defaultValue = null)
203 {
204 if ($this->hasClaim($name)) {
205 $value = $this->jwtPayload->{$name};
206 } else {
207 $value = $defaultValue;
208 }
209
210 return $value;
211 }
212
218 public function getPayload()
219 {
220 return $this->jwtPayload;
221 }
222
228 public static function getLastPayload()
229 {
230 return self::$lastPayload;
231 }
232
241 public function verify($publicKey, $jku = null)
242 {
243 $ok = false;
244 $hasPublicKey = !empty($publicKey);
245 if ($hasPublicKey) {
246 if (is_string($publicKey)) {
247 $json = Util::jsonDecode($publicKey, true);
248 if (!is_null($json)) {
249 try {
250 $jwks = array('keys' => array($json));
251 $publicKey = static::parseKeySet($jwks);
252 } catch (\Exception $e) {
253
254 }
255 } else {
256 $publicKey = new Key($publicKey, $this->getHeader('alg'));
257 }
258 }
259 } elseif (!empty($jku)) {
260 $publicKey = $this->fetchPublicKey($jku);
261 }
262 JWT::$leeway = Jwt::$leeway;
263 $retry = false;
264 do {
265 try {
266 JWT::decode($this->jwtString, $publicKey);
267 $ok = true;
268 } catch (\Exception $e) {
269 Util::logError($e->getMessage());
270 if ($retry) {
271 $retry = false;
272 } elseif ($hasPublicKey && !empty($jku)) {
273 try {
274 $publicKey = $this->fetchPublicKey($jku);
275 $retry = true;
276 } catch (\Exception $e) {
277
278 }
279 }
280 }
281 } while (!$ok && $retry);
282
283 return $ok;
284 }
285
300 public static function sign($payload, $signatureMethod, $privateKey, $kid = null, $jku = null, $encryptionMethod = null,
301 $publicKey = null)
302 {
303 if (!empty($encryptionMethod)) {
304 $errorMessage = 'Encrypted tokens not supported by the Firebase JWT client';
305 Util::logError($errorMessage);
306 throw new \Exception($errorMessage);
307 }
308 $jwtString = JWT::encode($payload, $privateKey, $signatureMethod, $kid);
309 $sections = explode('.', $jwtString);
310 self::$lastHeaders = Util::jsonDecode(JWT::urlsafeB64Decode($sections[0]));
311 self::$lastPayload = Util::jsonDecode(JWT::urlsafeB64Decode($sections[1]));
312
313 return $jwtString;
314 }
315
323 public static function generateKey($signatureMethod = 'RS256')
324 {
325 $privateKey = null;
326 switch ($signatureMethod) {
327 case 'RS512':
328 $size = 4096;
329 break;
330 case 'RS384':
331 $size = 3072;
332 break;
333 default:
334 $size = 2048;
335 break;
336 }
337 $config = array(
338 "private_key_bits" => $size,
339 "private_key_type" => OPENSSL_KEYTYPE_RSA
340 );
341 $res = openssl_pkey_new($config);
342 if ($res !== false) {
343 if (openssl_pkey_export($res, $privateKey)) {
344 $privateKey = str_replace('-----BEGIN PRIVATE KEY-----', '-----BEGIN RSA PRIVATE KEY-----', $privateKey);
345 $privateKey = str_replace('-----END PRIVATE KEY-----', '-----END RSA PRIVATE KEY-----', $privateKey);
346 }
347 }
348
349 return $privateKey;
350 }
351
359 public static function getPublicKey($privateKey)
360 {
361 $publicKey = null;
362 $res = openssl_pkey_get_private($privateKey);
363 if ($res !== false) {
364 $details = openssl_pkey_get_details($res);
365 $publicKey = $details['key'];
366 }
367
368 return $publicKey;
369 }
370
380 public static function getJWKS($pemKey, $signatureMethod, $kid = null)
381 {
382 $keys['keys'] = array();
383 $res = openssl_pkey_get_private($pemKey);
384 if ($res === false) {
385 $res = openssl_pkey_get_public($pemKey);
386 }
387 if ($res !== false) {
388 $details = openssl_pkey_get_details($res);
389 $key = [
390 'kty' => 'RSA',
391 'n' => JWT::urlsafeB64Encode($details['rsa']['n']),
392 'e' => JWT::urlsafeB64Encode($details['rsa']['e']),
393 'alg' => $signatureMethod,
394 'use' => 'sig'
395 ];
396 if (!empty($kid)) {
397 $key['kid'] = $kid;
398 }
399 $keys['keys'][] = $key;
400 }
401
402 return $keys;
403 }
404
405###
406### PRIVATE METHODS
407###
408
416 private function fetchPublicKey($jku)
417 {
418 $publicKey = array();
419 $http = new HttpMessage($jku);
420 if ($http->send()) {
421 $keys = Util::jsonDecode($http->response, true);
422 if (is_array($keys)) {
423 try {
424 $publicKey = static::parseKeySet($keys);
425 } catch (\Exception $e) {
426
427 }
428 }
429 }
430
431 return $publicKey;
432 }
433
448 private static function parseKeySet($jwks)
449 {
450 $keys = array();
451
452 if (!isset($jwks['keys'])) {
453 throw new \UnexpectedValueException('"keys" member must exist in the JWK Set');
454 }
455 if (empty($jwks['keys'])) {
456 throw new \InvalidArgumentException('JWK Set did not contain any keys');
457 }
458
459 foreach ($jwks['keys'] as $k => $v) {
460 if (!empty($v['alg'])) {
461 $kid = isset($v['kid']) ? $v['kid'] : $k;
462 if ($key = JWK::parseKey($v)) {
463 if (!$key instanceof Key) {
464 $key = new Key($key, $v['alg']);
465 }
466 $keys[$kid] = $key;
467 }
468 }
469 }
470
471 if (empty($keys)) {
472 throw new \UnexpectedValueException('No supported algorithms found in JWK Set');
473 }
474
475 return $keys;
476 }
477
478}
Class to represent an HTTP message request.
Class to implement the JWT interface using the Firebase JWT library from https://github....
static getLastHeaders()
Get the value of the headers for the last signed JWT (before any encryption).
static getLastPayload()
Get the value of the payload for the last signed JWT (before any encryption).
getHeaders()
Get the value of the headers.
hasJwt()
Check if a JWT is defined.
getJweHeaders()
Get the value of the JWE headers.
static getPublicKey($privateKey)
Get the public key for a private key.
load($jwtString, $privateKey=null)
Load a JWT from a string.
static getJWKS($pemKey, $signatureMethod, $kid=null)
Get the public JWKS from a key in PEM format.
static generateKey($signatureMethod='RS256')
Generate a new private key in PEM format.
const SUPPORTED_ALGORITHMS
Supported signature algorithms.
isEncrypted()
Check if a JWT's content is encrypted.
static getSupportedAlgorithms()
Return an array of supported signature algorithms.
getHeader($name, $defaultValue=null)
Get the value of the header with the specified name.
getClaim($name, $defaultValue=null)
Get the value of the claim with the specified name.
static sign($payload, $signatureMethod, $privateKey, $kid=null, $jku=null, $encryptionMethod=null, $publicKey=null)
Sign the JWT.
hasHeader($name)
Check whether a JWT has a header with the specified name.
getPayload()
Get the value of the payload.
hasClaim($name)
Check whether a JWT has a claim with the specified name.
verify($publicKey, $jku=null)
Verify the signature of the JWT.
Class to represent an HTTP message request.
Definition Jwt.php:15
Class to implement utility methods.
Definition Util.php:15
static jsonDecode($str, $associative=false)
Decode a JSON string.
Definition Util.php:560
Interface to represent an HWT client.