├── Composer-Setup.exe ├── JWT ├── JWT.php └── Key.php ├── composer.json ├── composer.lock ├── cors.php ├── db.php ├── firebase-php-jwt-d1e91ec ├── CHANGELOG.md ├── LICENSE ├── README.md ├── composer.json └── src │ ├── BeforeValidException.php │ ├── CachedKeySet.php │ ├── ExpiredException.php │ ├── JWK.php │ ├── JWT.php │ ├── JWTExceptionWithPayloadInterface.php │ ├── Key.php │ └── SignatureInvalidException.php ├── services.php ├── users.php └── vendor ├── autoload.php └── composer ├── ClassLoader.php ├── InstalledVersions.php ├── LICENSE ├── autoload_classmap.php ├── autoload_namespaces.php ├── autoload_psr4.php ├── autoload_real.php ├── autoload_static.php ├── installed.json ├── installed.php └── platform_check.php /Composer-Setup.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodeMaster0099/auth_backend-php-/c455cdc23797a80a56abc66a2814ecb95f9c49ea/Composer-Setup.exe -------------------------------------------------------------------------------- /JWT/JWT.php: -------------------------------------------------------------------------------- 1 | 24 | * @author Anant Narayanan 25 | * @license http://opensource.org/licenses/BSD-3-Clause 3-clause BSD 26 | * @link https://github.com/firebase/php-jwt 27 | */ 28 | class JWT 29 | { 30 | private const ASN1_INTEGER = 0x02; 31 | private const ASN1_SEQUENCE = 0x10; 32 | private const ASN1_BIT_STRING = 0x03; 33 | 34 | /** 35 | * When checking nbf, iat or expiration times, 36 | * we want to provide some extra leeway time to 37 | * account for clock skew. 38 | * 39 | * @var int 40 | */ 41 | public static $leeway = 0; 42 | 43 | /** 44 | * Allow the current timestamp to be specified. 45 | * Useful for fixing a value within unit testing. 46 | * Will default to PHP time() value if null. 47 | * 48 | * @var ?int 49 | */ 50 | public static $timestamp = null; 51 | 52 | /** 53 | * @var array 54 | */ 55 | public static $supported_algs = [ 56 | 'ES384' => ['openssl', 'SHA384'], 57 | 'ES256' => ['openssl', 'SHA256'], 58 | 'ES256K' => ['openssl', 'SHA256'], 59 | 'HS256' => ['hash_hmac', 'SHA256'], 60 | 'HS384' => ['hash_hmac', 'SHA384'], 61 | 'HS512' => ['hash_hmac', 'SHA512'], 62 | 'RS256' => ['openssl', 'SHA256'], 63 | 'RS384' => ['openssl', 'SHA384'], 64 | 'RS512' => ['openssl', 'SHA512'], 65 | 'EdDSA' => ['sodium_crypto', 'EdDSA'], 66 | ]; 67 | 68 | /** 69 | * Decodes a JWT string into a PHP object. 70 | * 71 | * @param string $jwt The JWT 72 | * @param Key|ArrayAccess|array $keyOrKeyArray The Key or associative array of key IDs 73 | * (kid) to Key objects. 74 | * If the algorithm used is asymmetric, this is 75 | * the public key. 76 | * Each Key object contains an algorithm and 77 | * matching key. 78 | * Supported algorithms are 'ES384','ES256', 79 | * 'HS256', 'HS384', 'HS512', 'RS256', 'RS384' 80 | * and 'RS512'. 81 | * @param stdClass $headers Optional. Populates stdClass with headers. 82 | * 83 | * @return stdClass The JWT's payload as a PHP object 84 | * 85 | * @throws InvalidArgumentException Provided key/key-array was empty or malformed 86 | * @throws DomainException Provided JWT is malformed 87 | * @throws UnexpectedValueException Provided JWT was invalid 88 | * @throws SignatureInvalidException Provided JWT was invalid because the signature verification failed 89 | * @throws BeforeValidException Provided JWT is trying to be used before it's eligible as defined by 'nbf' 90 | * @throws BeforeValidException Provided JWT is trying to be used before it's been created as defined by 'iat' 91 | * @throws ExpiredException Provided JWT has since expired, as defined by the 'exp' claim 92 | * 93 | * @uses jsonDecode 94 | * @uses urlsafeB64Decode 95 | */ 96 | public static function decode( 97 | string $jwt, 98 | $keyOrKeyArray, 99 | ?stdClass &$headers = null 100 | ): stdClass { 101 | // Validate JWT 102 | $timestamp = \is_null(static::$timestamp) ? \time() : static::$timestamp; 103 | 104 | if (empty($keyOrKeyArray)) { 105 | throw new InvalidArgumentException('Key may not be empty'); 106 | } 107 | $tks = \explode('.', $jwt); 108 | if (\count($tks) !== 3) { 109 | throw new UnexpectedValueException('Wrong number of segments'); 110 | } 111 | list($headb64, $bodyb64, $cryptob64) = $tks; 112 | $headerRaw = static::urlsafeB64Decode($headb64); 113 | if (null === ($header = static::jsonDecode($headerRaw))) { 114 | throw new UnexpectedValueException('Invalid header encoding'); 115 | } 116 | if ($headers !== null) { 117 | $headers = $header; 118 | } 119 | $payloadRaw = static::urlsafeB64Decode($bodyb64); 120 | if (null === ($payload = static::jsonDecode($payloadRaw))) { 121 | throw new UnexpectedValueException('Invalid claims encoding'); 122 | } 123 | if (\is_array($payload)) { 124 | // prevent PHP Fatal Error in edge-cases when payload is empty array 125 | $payload = (object) $payload; 126 | } 127 | if (!$payload instanceof stdClass) { 128 | throw new UnexpectedValueException('Payload must be a JSON object'); 129 | } 130 | $sig = static::urlsafeB64Decode($cryptob64); 131 | if (empty($header->alg)) { 132 | throw new UnexpectedValueException('Empty algorithm'); 133 | } 134 | if (empty(static::$supported_algs[$header->alg])) { 135 | throw new UnexpectedValueException('Algorithm not supported'); 136 | } 137 | 138 | $key = self::getKey($keyOrKeyArray, property_exists($header, 'kid') ? $header->kid : null); 139 | 140 | // Check the algorithm 141 | if (!self::constantTimeEquals($key->getAlgorithm(), $header->alg)) { 142 | // See issue #351 143 | throw new UnexpectedValueException('Incorrect key for this algorithm'); 144 | } 145 | if (\in_array($header->alg, ['ES256', 'ES256K', 'ES384'], true)) { 146 | // OpenSSL expects an ASN.1 DER sequence for ES256/ES256K/ES384 signatures 147 | $sig = self::signatureToDER($sig); 148 | } 149 | if (!self::verify("{$headb64}.{$bodyb64}", $sig, $key->getKeyMaterial(), $header->alg)) { 150 | throw new SignatureInvalidException('Signature verification failed'); 151 | } 152 | 153 | // Check the nbf if it is defined. This is the time that the 154 | // token can actually be used. If it's not yet that time, abort. 155 | if (isset($payload->nbf) && floor($payload->nbf) > ($timestamp + static::$leeway)) { 156 | $ex = new BeforeValidException( 157 | 'Cannot handle token with nbf prior to ' . \date(DateTime::ISO8601, (int) floor($payload->nbf)) 158 | ); 159 | $ex->setPayload($payload); 160 | throw $ex; 161 | } 162 | 163 | // Check that this token has been created before 'now'. This prevents 164 | // using tokens that have been created for later use (and haven't 165 | // correctly used the nbf claim). 166 | if (!isset($payload->nbf) && isset($payload->iat) && floor($payload->iat) > ($timestamp + static::$leeway)) { 167 | $ex = new BeforeValidException( 168 | 'Cannot handle token with iat prior to ' . \date(DateTime::ISO8601, (int) floor($payload->iat)) 169 | ); 170 | $ex->setPayload($payload); 171 | throw $ex; 172 | } 173 | 174 | // Check if this token has expired. 175 | if (isset($payload->exp) && ($timestamp - static::$leeway) >= $payload->exp) { 176 | $ex = new ExpiredException('Expired token'); 177 | $ex->setPayload($payload); 178 | throw $ex; 179 | } 180 | 181 | return $payload; 182 | } 183 | 184 | /** 185 | * Converts and signs a PHP array into a JWT string. 186 | * 187 | * @param array $payload PHP array 188 | * @param string|resource|OpenSSLAsymmetricKey|OpenSSLCertificate $key The secret key. 189 | * @param string $alg Supported algorithms are 'ES384','ES256', 'ES256K', 'HS256', 190 | * 'HS384', 'HS512', 'RS256', 'RS384', and 'RS512' 191 | * @param string $keyId 192 | * @param array $head An array with header elements to attach 193 | * 194 | * @return string A signed JWT 195 | * 196 | * @uses jsonEncode 197 | * @uses urlsafeB64Encode 198 | */ 199 | public static function encode( 200 | array $payload, 201 | $key, 202 | string $alg, 203 | ?string $keyId = null, 204 | ?array $head = null 205 | ): string { 206 | $header = ['typ' => 'JWT']; 207 | if (isset($head)) { 208 | $header = \array_merge($header, $head); 209 | } 210 | $header['alg'] = $alg; 211 | if ($keyId !== null) { 212 | $header['kid'] = $keyId; 213 | } 214 | $segments = []; 215 | $segments[] = static::urlsafeB64Encode((string) static::jsonEncode($header)); 216 | $segments[] = static::urlsafeB64Encode((string) static::jsonEncode($payload)); 217 | $signing_input = \implode('.', $segments); 218 | 219 | $signature = static::sign($signing_input, $key, $alg); 220 | $segments[] = static::urlsafeB64Encode($signature); 221 | 222 | return \implode('.', $segments); 223 | } 224 | 225 | /** 226 | * Sign a string with a given key and algorithm. 227 | * 228 | * @param string $msg The message to sign 229 | * @param string|resource|OpenSSLAsymmetricKey|OpenSSLCertificate $key The secret key. 230 | * @param string $alg Supported algorithms are 'EdDSA', 'ES384', 'ES256', 'ES256K', 'HS256', 231 | * 'HS384', 'HS512', 'RS256', 'RS384', and 'RS512' 232 | * 233 | * @return string An encrypted message 234 | * 235 | * @throws DomainException Unsupported algorithm or bad key was specified 236 | */ 237 | public static function sign( 238 | string $msg, 239 | $key, 240 | string $alg 241 | ): string { 242 | if (empty(static::$supported_algs[$alg])) { 243 | throw new DomainException('Algorithm not supported'); 244 | } 245 | list($function, $algorithm) = static::$supported_algs[$alg]; 246 | switch ($function) { 247 | case 'hash_hmac': 248 | if (!\is_string($key)) { 249 | throw new InvalidArgumentException('key must be a string when using hmac'); 250 | } 251 | return \hash_hmac($algorithm, $msg, $key, true); 252 | case 'openssl': 253 | $signature = ''; 254 | if (!\is_resource($key) && !openssl_pkey_get_private($key)) { 255 | throw new DomainException('OpenSSL unable to validate key'); 256 | } 257 | $success = \openssl_sign($msg, $signature, $key, $algorithm); // @phpstan-ignore-line 258 | if (!$success) { 259 | throw new DomainException('OpenSSL unable to sign data'); 260 | } 261 | if ($alg === 'ES256' || $alg === 'ES256K') { 262 | $signature = self::signatureFromDER($signature, 256); 263 | } elseif ($alg === 'ES384') { 264 | $signature = self::signatureFromDER($signature, 384); 265 | } 266 | return $signature; 267 | case 'sodium_crypto': 268 | if (!\function_exists('sodium_crypto_sign_detached')) { 269 | throw new DomainException('libsodium is not available'); 270 | } 271 | if (!\is_string($key)) { 272 | throw new InvalidArgumentException('key must be a string when using EdDSA'); 273 | } 274 | try { 275 | // The last non-empty line is used as the key. 276 | $lines = array_filter(explode("\n", $key)); 277 | $key = base64_decode((string) end($lines)); 278 | if (\strlen($key) === 0) { 279 | throw new DomainException('Key cannot be empty string'); 280 | } 281 | return sodium_crypto_sign_detached($msg, $key); 282 | } catch (Exception $e) { 283 | throw new DomainException($e->getMessage(), 0, $e); 284 | } 285 | } 286 | 287 | throw new DomainException('Algorithm not supported'); 288 | } 289 | 290 | /** 291 | * Verify a signature with the message, key and method. Not all methods 292 | * are symmetric, so we must have a separate verify and sign method. 293 | * 294 | * @param string $msg The original message (header and body) 295 | * @param string $signature The original signature 296 | * @param string|resource|OpenSSLAsymmetricKey|OpenSSLCertificate $keyMaterial For Ed*, ES*, HS*, a string key works. for RS*, must be an instance of OpenSSLAsymmetricKey 297 | * @param string $alg The algorithm 298 | * 299 | * @return bool 300 | * 301 | * @throws DomainException Invalid Algorithm, bad key, or OpenSSL failure 302 | */ 303 | private static function verify( 304 | string $msg, 305 | string $signature, 306 | $keyMaterial, 307 | string $alg 308 | ): bool { 309 | if (empty(static::$supported_algs[$alg])) { 310 | throw new DomainException('Algorithm not supported'); 311 | } 312 | 313 | list($function, $algorithm) = static::$supported_algs[$alg]; 314 | switch ($function) { 315 | case 'openssl': 316 | $success = \openssl_verify($msg, $signature, $keyMaterial, $algorithm); // @phpstan-ignore-line 317 | if ($success === 1) { 318 | return true; 319 | } 320 | if ($success === 0) { 321 | return false; 322 | } 323 | // returns 1 on success, 0 on failure, -1 on error. 324 | throw new DomainException( 325 | 'OpenSSL error: ' . \openssl_error_string() 326 | ); 327 | case 'sodium_crypto': 328 | if (!\function_exists('sodium_crypto_sign_verify_detached')) { 329 | throw new DomainException('libsodium is not available'); 330 | } 331 | if (!\is_string($keyMaterial)) { 332 | throw new InvalidArgumentException('key must be a string when using EdDSA'); 333 | } 334 | try { 335 | // The last non-empty line is used as the key. 336 | $lines = array_filter(explode("\n", $keyMaterial)); 337 | $key = base64_decode((string) end($lines)); 338 | if (\strlen($key) === 0) { 339 | throw new DomainException('Key cannot be empty string'); 340 | } 341 | if (\strlen($signature) === 0) { 342 | throw new DomainException('Signature cannot be empty string'); 343 | } 344 | return sodium_crypto_sign_verify_detached($signature, $msg, $key); 345 | } catch (Exception $e) { 346 | throw new DomainException($e->getMessage(), 0, $e); 347 | } 348 | case 'hash_hmac': 349 | default: 350 | if (!\is_string($keyMaterial)) { 351 | throw new InvalidArgumentException('key must be a string when using hmac'); 352 | } 353 | $hash = \hash_hmac($algorithm, $msg, $keyMaterial, true); 354 | return self::constantTimeEquals($hash, $signature); 355 | } 356 | } 357 | 358 | /** 359 | * Decode a JSON string into a PHP object. 360 | * 361 | * @param string $input JSON string 362 | * 363 | * @return mixed The decoded JSON string 364 | * 365 | * @throws DomainException Provided string was invalid JSON 366 | */ 367 | public static function jsonDecode(string $input) 368 | { 369 | $obj = \json_decode($input, false, 512, JSON_BIGINT_AS_STRING); 370 | 371 | if ($errno = \json_last_error()) { 372 | self::handleJsonError($errno); 373 | } elseif ($obj === null && $input !== 'null') { 374 | throw new DomainException('Null result with non-null input'); 375 | } 376 | return $obj; 377 | } 378 | 379 | /** 380 | * Encode a PHP array into a JSON string. 381 | * 382 | * @param array $input A PHP array 383 | * 384 | * @return string JSON representation of the PHP array 385 | * 386 | * @throws DomainException Provided object could not be encoded to valid JSON 387 | */ 388 | public static function jsonEncode(array $input): string 389 | { 390 | $json = \json_encode($input, \JSON_UNESCAPED_SLASHES); 391 | if ($errno = \json_last_error()) { 392 | self::handleJsonError($errno); 393 | } elseif ($json === 'null') { 394 | throw new DomainException('Null result with non-null input'); 395 | } 396 | if ($json === false) { 397 | throw new DomainException('Provided object could not be encoded to valid JSON'); 398 | } 399 | return $json; 400 | } 401 | 402 | /** 403 | * Decode a string with URL-safe Base64. 404 | * 405 | * @param string $input A Base64 encoded string 406 | * 407 | * @return string A decoded string 408 | * 409 | * @throws InvalidArgumentException invalid base64 characters 410 | */ 411 | public static function urlsafeB64Decode(string $input): string 412 | { 413 | return \base64_decode(self::convertBase64UrlToBase64($input)); 414 | } 415 | 416 | /** 417 | * Convert a string in the base64url (URL-safe Base64) encoding to standard base64. 418 | * 419 | * @param string $input A Base64 encoded string with URL-safe characters (-_ and no padding) 420 | * 421 | * @return string A Base64 encoded string with standard characters (+/) and padding (=), when 422 | * needed. 423 | * 424 | * @see https://www.rfc-editor.org/rfc/rfc4648 425 | */ 426 | public static function convertBase64UrlToBase64(string $input): string 427 | { 428 | $remainder = \strlen($input) % 4; 429 | if ($remainder) { 430 | $padlen = 4 - $remainder; 431 | $input .= \str_repeat('=', $padlen); 432 | } 433 | return \strtr($input, '-_', '+/'); 434 | } 435 | 436 | /** 437 | * Encode a string with URL-safe Base64. 438 | * 439 | * @param string $input The string you want encoded 440 | * 441 | * @return string The base64 encode of what you passed in 442 | */ 443 | public static function urlsafeB64Encode(string $input): string 444 | { 445 | return \str_replace('=', '', \strtr(\base64_encode($input), '+/', '-_')); 446 | } 447 | 448 | 449 | /** 450 | * Determine if an algorithm has been provided for each Key 451 | * 452 | * @param Key|ArrayAccess|array $keyOrKeyArray 453 | * @param string|null $kid 454 | * 455 | * @throws UnexpectedValueException 456 | * 457 | * @return Key 458 | */ 459 | private static function getKey( 460 | $keyOrKeyArray, 461 | ?string $kid 462 | ): Key { 463 | if ($keyOrKeyArray instanceof Key) { 464 | return $keyOrKeyArray; 465 | } 466 | 467 | if (empty($kid) && $kid !== '0') { 468 | throw new UnexpectedValueException('"kid" empty, unable to lookup correct key'); 469 | } 470 | 471 | if ($keyOrKeyArray instanceof CachedKeySet) { 472 | // Skip "isset" check, as this will automatically refresh if not set 473 | return $keyOrKeyArray[$kid]; 474 | } 475 | 476 | if (!isset($keyOrKeyArray[$kid])) { 477 | throw new UnexpectedValueException('"kid" invalid, unable to lookup correct key'); 478 | } 479 | 480 | return $keyOrKeyArray[$kid]; 481 | } 482 | 483 | /** 484 | * @param string $left The string of known length to compare against 485 | * @param string $right The user-supplied string 486 | * @return bool 487 | */ 488 | public static function constantTimeEquals(string $left, string $right): bool 489 | { 490 | if (\function_exists('hash_equals')) { 491 | return \hash_equals($left, $right); 492 | } 493 | $len = \min(self::safeStrlen($left), self::safeStrlen($right)); 494 | 495 | $status = 0; 496 | for ($i = 0; $i < $len; $i++) { 497 | $status |= (\ord($left[$i]) ^ \ord($right[$i])); 498 | } 499 | $status |= (self::safeStrlen($left) ^ self::safeStrlen($right)); 500 | 501 | return ($status === 0); 502 | } 503 | 504 | /** 505 | * Helper method to create a JSON error. 506 | * 507 | * @param int $errno An error number from json_last_error() 508 | * 509 | * @throws DomainException 510 | * 511 | * @return void 512 | */ 513 | private static function handleJsonError(int $errno): void 514 | { 515 | $messages = [ 516 | JSON_ERROR_DEPTH => 'Maximum stack depth exceeded', 517 | JSON_ERROR_STATE_MISMATCH => 'Invalid or malformed JSON', 518 | JSON_ERROR_CTRL_CHAR => 'Unexpected control character found', 519 | JSON_ERROR_SYNTAX => 'Syntax error, malformed JSON', 520 | JSON_ERROR_UTF8 => 'Malformed UTF-8 characters' //PHP >= 5.3.3 521 | ]; 522 | throw new DomainException( 523 | isset($messages[$errno]) 524 | ? $messages[$errno] 525 | : 'Unknown JSON error: ' . $errno 526 | ); 527 | } 528 | 529 | /** 530 | * Get the number of bytes in cryptographic strings. 531 | * 532 | * @param string $str 533 | * 534 | * @return int 535 | */ 536 | private static function safeStrlen(string $str): int 537 | { 538 | if (\function_exists('mb_strlen')) { 539 | return \mb_strlen($str, '8bit'); 540 | } 541 | return \strlen($str); 542 | } 543 | 544 | /** 545 | * Convert an ECDSA signature to an ASN.1 DER sequence 546 | * 547 | * @param string $sig The ECDSA signature to convert 548 | * @return string The encoded DER object 549 | */ 550 | private static function signatureToDER(string $sig): string 551 | { 552 | // Separate the signature into r-value and s-value 553 | $length = max(1, (int) (\strlen($sig) / 2)); 554 | list($r, $s) = \str_split($sig, $length); 555 | 556 | // Trim leading zeros 557 | $r = \ltrim($r, "\x00"); 558 | $s = \ltrim($s, "\x00"); 559 | 560 | // Convert r-value and s-value from unsigned big-endian integers to 561 | // signed two's complement 562 | if (\ord($r[0]) > 0x7f) { 563 | $r = "\x00" . $r; 564 | } 565 | if (\ord($s[0]) > 0x7f) { 566 | $s = "\x00" . $s; 567 | } 568 | 569 | return self::encodeDER( 570 | self::ASN1_SEQUENCE, 571 | self::encodeDER(self::ASN1_INTEGER, $r) . 572 | self::encodeDER(self::ASN1_INTEGER, $s) 573 | ); 574 | } 575 | 576 | /** 577 | * Encodes a value into a DER object. 578 | * 579 | * @param int $type DER tag 580 | * @param string $value the value to encode 581 | * 582 | * @return string the encoded object 583 | */ 584 | private static function encodeDER(int $type, string $value): string 585 | { 586 | $tag_header = 0; 587 | if ($type === self::ASN1_SEQUENCE) { 588 | $tag_header |= 0x20; 589 | } 590 | 591 | // Type 592 | $der = \chr($tag_header | $type); 593 | 594 | // Length 595 | $der .= \chr(\strlen($value)); 596 | 597 | return $der . $value; 598 | } 599 | 600 | /** 601 | * Encodes signature from a DER object. 602 | * 603 | * @param string $der binary signature in DER format 604 | * @param int $keySize the number of bits in the key 605 | * 606 | * @return string the signature 607 | */ 608 | private static function signatureFromDER(string $der, int $keySize): string 609 | { 610 | // OpenSSL returns the ECDSA signatures as a binary ASN.1 DER SEQUENCE 611 | list($offset, $_) = self::readDER($der); 612 | list($offset, $r) = self::readDER($der, $offset); 613 | list($offset, $s) = self::readDER($der, $offset); 614 | 615 | // Convert r-value and s-value from signed two's compliment to unsigned 616 | // big-endian integers 617 | $r = \ltrim($r, "\x00"); 618 | $s = \ltrim($s, "\x00"); 619 | 620 | // Pad out r and s so that they are $keySize bits long 621 | $r = \str_pad($r, $keySize / 8, "\x00", STR_PAD_LEFT); 622 | $s = \str_pad($s, $keySize / 8, "\x00", STR_PAD_LEFT); 623 | 624 | return $r . $s; 625 | } 626 | 627 | /** 628 | * Reads binary DER-encoded data and decodes into a single object 629 | * 630 | * @param string $der the binary data in DER format 631 | * @param int $offset the offset of the data stream containing the object 632 | * to decode 633 | * 634 | * @return array{int, string|null} the new offset and the decoded object 635 | */ 636 | private static function readDER(string $der, int $offset = 0): array 637 | { 638 | $pos = $offset; 639 | $size = \strlen($der); 640 | $constructed = (\ord($der[$pos]) >> 5) & 0x01; 641 | $type = \ord($der[$pos++]) & 0x1f; 642 | 643 | // Length 644 | $len = \ord($der[$pos++]); 645 | if ($len & 0x80) { 646 | $n = $len & 0x1f; 647 | $len = 0; 648 | while ($n-- && $pos < $size) { 649 | $len = ($len << 8) | \ord($der[$pos++]); 650 | } 651 | } 652 | 653 | // Value 654 | if ($type === self::ASN1_BIT_STRING) { 655 | $pos++; // Skip the first contents octet (padding indicator) 656 | $data = \substr($der, $pos, $len - 1); 657 | $pos += $len - 1; 658 | } elseif (!$constructed) { 659 | $data = \substr($der, $pos, $len); 660 | $pos += $len; 661 | } else { 662 | $data = null; 663 | } 664 | 665 | return [$pos, $data]; 666 | } 667 | } 668 | -------------------------------------------------------------------------------- /JWT/Key.php: -------------------------------------------------------------------------------- 1 | algorithm; 46 | } 47 | 48 | /** 49 | * @return string|resource|OpenSSLAsymmetricKey|OpenSSLCertificate 50 | */ 51 | public function getKeyMaterial() 52 | { 53 | return $this->keyMaterial; 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "require": { 3 | "firebase/php-jwt": "^6.11" 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /composer.lock: -------------------------------------------------------------------------------- 1 | { 2 | "_readme": [ 3 | "This file locks the dependencies of your project to a known state", 4 | "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", 5 | "This file is @generated automatically" 6 | ], 7 | "content-hash": "726b0a650f3a35c706f5a399d9e99662", 8 | "packages": [ 9 | { 10 | "name": "firebase/php-jwt", 11 | "version": "v6.11.1", 12 | "source": { 13 | "type": "git", 14 | "url": "https://github.com/firebase/php-jwt.git", 15 | "reference": "d1e91ecf8c598d073d0995afa8cd5c75c6e19e66" 16 | }, 17 | "dist": { 18 | "type": "zip", 19 | "url": "https://api.github.com/repos/firebase/php-jwt/zipball/d1e91ecf8c598d073d0995afa8cd5c75c6e19e66", 20 | "reference": "d1e91ecf8c598d073d0995afa8cd5c75c6e19e66", 21 | "shasum": "" 22 | }, 23 | "require": { 24 | "php": "^8.0" 25 | }, 26 | "require-dev": { 27 | "guzzlehttp/guzzle": "^7.4", 28 | "phpspec/prophecy-phpunit": "^2.0", 29 | "phpunit/phpunit": "^9.5", 30 | "psr/cache": "^2.0||^3.0", 31 | "psr/http-client": "^1.0", 32 | "psr/http-factory": "^1.0" 33 | }, 34 | "suggest": { 35 | "ext-sodium": "Support EdDSA (Ed25519) signatures", 36 | "paragonie/sodium_compat": "Support EdDSA (Ed25519) signatures when libsodium is not present" 37 | }, 38 | "type": "library", 39 | "autoload": { 40 | "psr-4": { 41 | "Firebase\\JWT\\": "src" 42 | } 43 | }, 44 | "notification-url": "https://packagist.org/downloads/", 45 | "license": [ 46 | "BSD-3-Clause" 47 | ], 48 | "authors": [ 49 | { 50 | "name": "Neuman Vong", 51 | "email": "neuman+pear@twilio.com", 52 | "role": "Developer" 53 | }, 54 | { 55 | "name": "Anant Narayanan", 56 | "email": "anant@php.net", 57 | "role": "Developer" 58 | } 59 | ], 60 | "description": "A simple library to encode and decode JSON Web Tokens (JWT) in PHP. Should conform to the current spec.", 61 | "homepage": "https://github.com/firebase/php-jwt", 62 | "keywords": [ 63 | "jwt", 64 | "php" 65 | ], 66 | "support": { 67 | "issues": "https://github.com/firebase/php-jwt/issues", 68 | "source": "https://github.com/firebase/php-jwt/tree/v6.11.1" 69 | }, 70 | "time": "2025-04-09T20:32:01+00:00" 71 | } 72 | ], 73 | "packages-dev": [], 74 | "aliases": [], 75 | "minimum-stability": "stable", 76 | "stability-flags": {}, 77 | "prefer-stable": false, 78 | "prefer-lowest": false, 79 | "platform": {}, 80 | "platform-dev": {}, 81 | "plugin-api-version": "2.6.0" 82 | } 83 | -------------------------------------------------------------------------------- /cors.php: -------------------------------------------------------------------------------- 1 | 14 | -------------------------------------------------------------------------------- /db.php: -------------------------------------------------------------------------------- 1 | connect_error) { 9 | die("Connection failed: " . $conn->connect_error); 10 | } 11 | ?> 12 | -------------------------------------------------------------------------------- /firebase-php-jwt-d1e91ec/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## [6.11.1](https://github.com/firebase/php-jwt/compare/v6.11.0...v6.11.1) (2025-04-09) 4 | 5 | 6 | ### Bug Fixes 7 | 8 | * update error text for consistency ([#528](https://github.com/firebase/php-jwt/issues/528)) ([c11113a](https://github.com/firebase/php-jwt/commit/c11113afa13265e016a669e75494b9203b8a7775)) 9 | 10 | ## [6.11.0](https://github.com/firebase/php-jwt/compare/v6.10.2...v6.11.0) (2025-01-23) 11 | 12 | 13 | ### Features 14 | 15 | * support octet typed JWK ([#587](https://github.com/firebase/php-jwt/issues/587)) ([7cb8a26](https://github.com/firebase/php-jwt/commit/7cb8a265fa81edf2fa6ef8098f5bc5ae573c33ad)) 16 | 17 | 18 | ### Bug Fixes 19 | 20 | * refactor constructor Key to use PHP 8.0 syntax ([#577](https://github.com/firebase/php-jwt/issues/577)) ([29fa2ce](https://github.com/firebase/php-jwt/commit/29fa2ce9e0582cd397711eec1e80c05ce20fabca)) 21 | 22 | ## [6.10.2](https://github.com/firebase/php-jwt/compare/v6.10.1...v6.10.2) (2024-11-24) 23 | 24 | 25 | ### Bug Fixes 26 | 27 | * Mitigate PHP8.4 deprecation warnings ([#570](https://github.com/firebase/php-jwt/issues/570)) ([76808fa](https://github.com/firebase/php-jwt/commit/76808fa227f3811aa5cdb3bf81233714b799a5b5)) 28 | * support php 8.4 ([#583](https://github.com/firebase/php-jwt/issues/583)) ([e3d68b0](https://github.com/firebase/php-jwt/commit/e3d68b044421339443c74199edd020e03fb1887e)) 29 | 30 | ## [6.10.1](https://github.com/firebase/php-jwt/compare/v6.10.0...v6.10.1) (2024-05-18) 31 | 32 | 33 | ### Bug Fixes 34 | 35 | * ensure ratelimit expiry is set every time ([#556](https://github.com/firebase/php-jwt/issues/556)) ([09cb208](https://github.com/firebase/php-jwt/commit/09cb2081c2c3bc0f61e2f2a5fbea5741f7498648)) 36 | * ratelimit cache expiration ([#550](https://github.com/firebase/php-jwt/issues/550)) ([dda7250](https://github.com/firebase/php-jwt/commit/dda725033585ece30ff8cae8937320d7e9f18bae)) 37 | 38 | ## [6.10.0](https://github.com/firebase/php-jwt/compare/v6.9.0...v6.10.0) (2023-11-28) 39 | 40 | 41 | ### Features 42 | 43 | * allow typ header override ([#546](https://github.com/firebase/php-jwt/issues/546)) ([79cb30b](https://github.com/firebase/php-jwt/commit/79cb30b729a22931b2fbd6b53f20629a83031ba9)) 44 | 45 | ## [6.9.0](https://github.com/firebase/php-jwt/compare/v6.8.1...v6.9.0) (2023-10-04) 46 | 47 | 48 | ### Features 49 | 50 | * add payload to jwt exception ([#521](https://github.com/firebase/php-jwt/issues/521)) ([175edf9](https://github.com/firebase/php-jwt/commit/175edf958bb61922ec135b2333acf5622f2238a2)) 51 | 52 | ## [6.8.1](https://github.com/firebase/php-jwt/compare/v6.8.0...v6.8.1) (2023-07-14) 53 | 54 | 55 | ### Bug Fixes 56 | 57 | * accept float claims but round down to ignore them ([#492](https://github.com/firebase/php-jwt/issues/492)) ([3936842](https://github.com/firebase/php-jwt/commit/39368423beeaacb3002afa7dcb75baebf204fe7e)) 58 | * different BeforeValidException messages for nbf and iat ([#526](https://github.com/firebase/php-jwt/issues/526)) ([0a53cf2](https://github.com/firebase/php-jwt/commit/0a53cf2986e45c2bcbf1a269f313ebf56a154ee4)) 59 | 60 | ## [6.8.0](https://github.com/firebase/php-jwt/compare/v6.7.0...v6.8.0) (2023-06-14) 61 | 62 | 63 | ### Features 64 | 65 | * add support for P-384 curve ([#515](https://github.com/firebase/php-jwt/issues/515)) ([5de4323](https://github.com/firebase/php-jwt/commit/5de4323f4baf4d70bca8663bd87682a69c656c3d)) 66 | 67 | 68 | ### Bug Fixes 69 | 70 | * handle invalid http responses ([#508](https://github.com/firebase/php-jwt/issues/508)) ([91c39c7](https://github.com/firebase/php-jwt/commit/91c39c72b22fc3e1191e574089552c1f2041c718)) 71 | 72 | ## [6.7.0](https://github.com/firebase/php-jwt/compare/v6.6.0...v6.7.0) (2023-06-14) 73 | 74 | 75 | ### Features 76 | 77 | * add ed25519 support to JWK (public keys) ([#452](https://github.com/firebase/php-jwt/issues/452)) ([e53979a](https://github.com/firebase/php-jwt/commit/e53979abae927de916a75b9d239cfda8ce32be2a)) 78 | 79 | ## [6.6.0](https://github.com/firebase/php-jwt/compare/v6.5.0...v6.6.0) (2023-06-13) 80 | 81 | 82 | ### Features 83 | 84 | * allow get headers when decoding token ([#442](https://github.com/firebase/php-jwt/issues/442)) ([fb85f47](https://github.com/firebase/php-jwt/commit/fb85f47cfaeffdd94faf8defdf07164abcdad6c3)) 85 | 86 | 87 | ### Bug Fixes 88 | 89 | * only check iat if nbf is not used ([#493](https://github.com/firebase/php-jwt/issues/493)) ([398ccd2](https://github.com/firebase/php-jwt/commit/398ccd25ea12fa84b9e4f1085d5ff448c21ec797)) 90 | 91 | ## [6.5.0](https://github.com/firebase/php-jwt/compare/v6.4.0...v6.5.0) (2023-05-12) 92 | 93 | 94 | ### Bug Fixes 95 | 96 | * allow KID of '0' ([#505](https://github.com/firebase/php-jwt/issues/505)) ([9dc46a9](https://github.com/firebase/php-jwt/commit/9dc46a9c3e5801294249cfd2554c5363c9f9326a)) 97 | 98 | 99 | ### Miscellaneous Chores 100 | 101 | * drop support for PHP 7.3 ([#495](https://github.com/firebase/php-jwt/issues/495)) 102 | 103 | ## [6.4.0](https://github.com/firebase/php-jwt/compare/v6.3.2...v6.4.0) (2023-02-08) 104 | 105 | 106 | ### Features 107 | 108 | * add support for W3C ES256K ([#462](https://github.com/firebase/php-jwt/issues/462)) ([213924f](https://github.com/firebase/php-jwt/commit/213924f51936291fbbca99158b11bd4ae56c2c95)) 109 | * improve caching by only decoding jwks when necessary ([#486](https://github.com/firebase/php-jwt/issues/486)) ([78d3ed1](https://github.com/firebase/php-jwt/commit/78d3ed1073553f7d0bbffa6c2010009a0d483d5c)) 110 | 111 | ## [6.3.2](https://github.com/firebase/php-jwt/compare/v6.3.1...v6.3.2) (2022-11-01) 112 | 113 | 114 | ### Bug Fixes 115 | 116 | * check kid before using as array index ([bad1b04](https://github.com/firebase/php-jwt/commit/bad1b040d0c736bbf86814c6b5ae614f517cf7bd)) 117 | 118 | ## [6.3.1](https://github.com/firebase/php-jwt/compare/v6.3.0...v6.3.1) (2022-11-01) 119 | 120 | 121 | ### Bug Fixes 122 | 123 | * casing of GET for PSR compat ([#451](https://github.com/firebase/php-jwt/issues/451)) ([60b52b7](https://github.com/firebase/php-jwt/commit/60b52b71978790eafcf3b95cfbd83db0439e8d22)) 124 | * string interpolation format for php 8.2 ([#446](https://github.com/firebase/php-jwt/issues/446)) ([2e07d8a](https://github.com/firebase/php-jwt/commit/2e07d8a1524d12b69b110ad649f17461d068b8f2)) 125 | 126 | ## 6.3.0 / 2022-07-15 127 | 128 | - Added ES256 support to JWK parsing ([#399](https://github.com/firebase/php-jwt/pull/399)) 129 | - Fixed potential caching error in `CachedKeySet` by caching jwks as strings ([#435](https://github.com/firebase/php-jwt/pull/435)) 130 | 131 | ## 6.2.0 / 2022-05-14 132 | 133 | - Added `CachedKeySet` ([#397](https://github.com/firebase/php-jwt/pull/397)) 134 | - Added `$defaultAlg` parameter to `JWT::parseKey` and `JWT::parseKeySet` ([#426](https://github.com/firebase/php-jwt/pull/426)). 135 | 136 | ## 6.1.0 / 2022-03-23 137 | 138 | - Drop support for PHP 5.3, 5.4, 5.5, 5.6, and 7.0 139 | - Add parameter typing and return types where possible 140 | 141 | ## 6.0.0 / 2022-01-24 142 | 143 | - **Backwards-Compatibility Breaking Changes**: See the [Release Notes](https://github.com/firebase/php-jwt/releases/tag/v6.0.0) for more information. 144 | - New Key object to prevent key/algorithm type confusion (#365) 145 | - Add JWK support (#273) 146 | - Add ES256 support (#256) 147 | - Add ES384 support (#324) 148 | - Add Ed25519 support (#343) 149 | 150 | ## 5.0.0 / 2017-06-26 151 | - Support RS384 and RS512. 152 | See [#117](https://github.com/firebase/php-jwt/pull/117). Thanks [@joostfaassen](https://github.com/joostfaassen)! 153 | - Add an example for RS256 openssl. 154 | See [#125](https://github.com/firebase/php-jwt/pull/125). Thanks [@akeeman](https://github.com/akeeman)! 155 | - Detect invalid Base64 encoding in signature. 156 | See [#162](https://github.com/firebase/php-jwt/pull/162). Thanks [@psignoret](https://github.com/psignoret)! 157 | - Update `JWT::verify` to handle OpenSSL errors. 158 | See [#159](https://github.com/firebase/php-jwt/pull/159). Thanks [@bshaffer](https://github.com/bshaffer)! 159 | - Add `array` type hinting to `decode` method 160 | See [#101](https://github.com/firebase/php-jwt/pull/101). Thanks [@hywak](https://github.com/hywak)! 161 | - Add all JSON error types. 162 | See [#110](https://github.com/firebase/php-jwt/pull/110). Thanks [@gbalduzzi](https://github.com/gbalduzzi)! 163 | - Bugfix 'kid' not in given key list. 164 | See [#129](https://github.com/firebase/php-jwt/pull/129). Thanks [@stampycode](https://github.com/stampycode)! 165 | - Miscellaneous cleanup, documentation and test fixes. 166 | See [#107](https://github.com/firebase/php-jwt/pull/107), [#115](https://github.com/firebase/php-jwt/pull/115), 167 | [#160](https://github.com/firebase/php-jwt/pull/160), [#161](https://github.com/firebase/php-jwt/pull/161), and 168 | [#165](https://github.com/firebase/php-jwt/pull/165). Thanks [@akeeman](https://github.com/akeeman), 169 | [@chinedufn](https://github.com/chinedufn), and [@bshaffer](https://github.com/bshaffer)! 170 | 171 | ## 4.0.0 / 2016-07-17 172 | - Add support for late static binding. See [#88](https://github.com/firebase/php-jwt/pull/88) for details. Thanks to [@chappy84](https://github.com/chappy84)! 173 | - Use static `$timestamp` instead of `time()` to improve unit testing. See [#93](https://github.com/firebase/php-jwt/pull/93) for details. Thanks to [@josephmcdermott](https://github.com/josephmcdermott)! 174 | - Fixes to exceptions classes. See [#81](https://github.com/firebase/php-jwt/pull/81) for details. Thanks to [@Maks3w](https://github.com/Maks3w)! 175 | - Fixes to PHPDoc. See [#76](https://github.com/firebase/php-jwt/pull/76) for details. Thanks to [@akeeman](https://github.com/akeeman)! 176 | 177 | ## 3.0.0 / 2015-07-22 178 | - Minimum PHP version updated from `5.2.0` to `5.3.0`. 179 | - Add `\Firebase\JWT` namespace. See 180 | [#59](https://github.com/firebase/php-jwt/pull/59) for details. Thanks to 181 | [@Dashron](https://github.com/Dashron)! 182 | - Require a non-empty key to decode and verify a JWT. See 183 | [#60](https://github.com/firebase/php-jwt/pull/60) for details. Thanks to 184 | [@sjones608](https://github.com/sjones608)! 185 | - Cleaner documentation blocks in the code. See 186 | [#62](https://github.com/firebase/php-jwt/pull/62) for details. Thanks to 187 | [@johanderuijter](https://github.com/johanderuijter)! 188 | 189 | ## 2.2.0 / 2015-06-22 190 | - Add support for adding custom, optional JWT headers to `JWT::encode()`. See 191 | [#53](https://github.com/firebase/php-jwt/pull/53/files) for details. Thanks to 192 | [@mcocaro](https://github.com/mcocaro)! 193 | 194 | ## 2.1.0 / 2015-05-20 195 | - Add support for adding a leeway to `JWT:decode()` that accounts for clock skew 196 | between signing and verifying entities. Thanks to [@lcabral](https://github.com/lcabral)! 197 | - Add support for passing an object implementing the `ArrayAccess` interface for 198 | `$keys` argument in `JWT::decode()`. Thanks to [@aztech-dev](https://github.com/aztech-dev)! 199 | 200 | ## 2.0.0 / 2015-04-01 201 | - **Note**: It is strongly recommended that you update to > v2.0.0 to address 202 | known security vulnerabilities in prior versions when both symmetric and 203 | asymmetric keys are used together. 204 | - Update signature for `JWT::decode(...)` to require an array of supported 205 | algorithms to use when verifying token signatures. 206 | -------------------------------------------------------------------------------- /firebase-php-jwt-d1e91ec/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2011, Neuman Vong 2 | 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | 8 | * Redistributions of source code must retain the above copyright 9 | notice, this list of conditions and the following disclaimer. 10 | 11 | * Redistributions in binary form must reproduce the above 12 | copyright notice, this list of conditions and the following 13 | disclaimer in the documentation and/or other materials provided 14 | with the distribution. 15 | 16 | * Neither the name of the copyright holder nor the names of other 17 | contributors may be used to endorse or promote products derived 18 | from this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 23 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 24 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 25 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 26 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 27 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 28 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 30 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 | -------------------------------------------------------------------------------- /firebase-php-jwt-d1e91ec/README.md: -------------------------------------------------------------------------------- 1 | ![Build Status](https://github.com/firebase/php-jwt/actions/workflows/tests.yml/badge.svg) 2 | [![Latest Stable Version](https://poser.pugx.org/firebase/php-jwt/v/stable)](https://packagist.org/packages/firebase/php-jwt) 3 | [![Total Downloads](https://poser.pugx.org/firebase/php-jwt/downloads)](https://packagist.org/packages/firebase/php-jwt) 4 | [![License](https://poser.pugx.org/firebase/php-jwt/license)](https://packagist.org/packages/firebase/php-jwt) 5 | 6 | PHP-JWT 7 | ======= 8 | A simple library to encode and decode JSON Web Tokens (JWT) in PHP, conforming to [RFC 7519](https://tools.ietf.org/html/rfc7519). 9 | 10 | Installation 11 | ------------ 12 | 13 | Use composer to manage your dependencies and download PHP-JWT: 14 | 15 | ```bash 16 | composer require firebase/php-jwt 17 | ``` 18 | 19 | Optionally, install the `paragonie/sodium_compat` package from composer if your 20 | php env does not have libsodium installed: 21 | 22 | ```bash 23 | composer require paragonie/sodium_compat 24 | ``` 25 | 26 | Example 27 | ------- 28 | ```php 29 | use Firebase\JWT\JWT; 30 | use Firebase\JWT\Key; 31 | 32 | $key = 'example_key'; 33 | $payload = [ 34 | 'iss' => 'http://example.org', 35 | 'aud' => 'http://example.com', 36 | 'iat' => 1356999524, 37 | 'nbf' => 1357000000 38 | ]; 39 | 40 | /** 41 | * IMPORTANT: 42 | * You must specify supported algorithms for your application. See 43 | * https://tools.ietf.org/html/draft-ietf-jose-json-web-algorithms-40 44 | * for a list of spec-compliant algorithms. 45 | */ 46 | $jwt = JWT::encode($payload, $key, 'HS256'); 47 | $decoded = JWT::decode($jwt, new Key($key, 'HS256')); 48 | print_r($decoded); 49 | 50 | // Pass a stdClass in as the third parameter to get the decoded header values 51 | $headers = new stdClass(); 52 | $decoded = JWT::decode($jwt, new Key($key, 'HS256'), $headers); 53 | print_r($headers); 54 | 55 | /* 56 | NOTE: This will now be an object instead of an associative array. To get 57 | an associative array, you will need to cast it as such: 58 | */ 59 | 60 | $decoded_array = (array) $decoded; 61 | 62 | /** 63 | * You can add a leeway to account for when there is a clock skew times between 64 | * the signing and verifying servers. It is recommended that this leeway should 65 | * not be bigger than a few minutes. 66 | * 67 | * Source: http://self-issued.info/docs/draft-ietf-oauth-json-web-token.html#nbfDef 68 | */ 69 | JWT::$leeway = 60; // $leeway in seconds 70 | $decoded = JWT::decode($jwt, new Key($key, 'HS256')); 71 | ``` 72 | Example encode/decode headers 73 | ------- 74 | Decoding the JWT headers without verifying the JWT first is NOT recommended, and is not supported by 75 | this library. This is because without verifying the JWT, the header values could have been tampered with. 76 | Any value pulled from an unverified header should be treated as if it could be any string sent in from an 77 | attacker. If this is something you still want to do in your application for whatever reason, it's possible to 78 | decode the header values manually simply by calling `json_decode` and `base64_decode` on the JWT 79 | header part: 80 | ```php 81 | use Firebase\JWT\JWT; 82 | 83 | $key = 'example_key'; 84 | $payload = [ 85 | 'iss' => 'http://example.org', 86 | 'aud' => 'http://example.com', 87 | 'iat' => 1356999524, 88 | 'nbf' => 1357000000 89 | ]; 90 | 91 | $headers = [ 92 | 'x-forwarded-for' => 'www.google.com' 93 | ]; 94 | 95 | // Encode headers in the JWT string 96 | $jwt = JWT::encode($payload, $key, 'HS256', null, $headers); 97 | 98 | // Decode headers from the JWT string WITHOUT validation 99 | // **IMPORTANT**: This operation is vulnerable to attacks, as the JWT has not yet been verified. 100 | // These headers could be any value sent by an attacker. 101 | list($headersB64, $payloadB64, $sig) = explode('.', $jwt); 102 | $decoded = json_decode(base64_decode($headersB64), true); 103 | 104 | print_r($decoded); 105 | ``` 106 | Example with RS256 (openssl) 107 | ---------------------------- 108 | ```php 109 | use Firebase\JWT\JWT; 110 | use Firebase\JWT\Key; 111 | 112 | $privateKey = << 'example.org', 156 | 'aud' => 'example.com', 157 | 'iat' => 1356999524, 158 | 'nbf' => 1357000000 159 | ]; 160 | 161 | $jwt = JWT::encode($payload, $privateKey, 'RS256'); 162 | echo "Encode:\n" . print_r($jwt, true) . "\n"; 163 | 164 | $decoded = JWT::decode($jwt, new Key($publicKey, 'RS256')); 165 | 166 | /* 167 | NOTE: This will now be an object instead of an associative array. To get 168 | an associative array, you will need to cast it as such: 169 | */ 170 | 171 | $decoded_array = (array) $decoded; 172 | echo "Decode:\n" . print_r($decoded_array, true) . "\n"; 173 | ``` 174 | 175 | Example with a passphrase 176 | ------------------------- 177 | 178 | ```php 179 | use Firebase\JWT\JWT; 180 | use Firebase\JWT\Key; 181 | 182 | // Your passphrase 183 | $passphrase = '[YOUR_PASSPHRASE]'; 184 | 185 | // Your private key file with passphrase 186 | // Can be generated with "ssh-keygen -t rsa -m pem" 187 | $privateKeyFile = '/path/to/key-with-passphrase.pem'; 188 | 189 | // Create a private key of type "resource" 190 | $privateKey = openssl_pkey_get_private( 191 | file_get_contents($privateKeyFile), 192 | $passphrase 193 | ); 194 | 195 | $payload = [ 196 | 'iss' => 'example.org', 197 | 'aud' => 'example.com', 198 | 'iat' => 1356999524, 199 | 'nbf' => 1357000000 200 | ]; 201 | 202 | $jwt = JWT::encode($payload, $privateKey, 'RS256'); 203 | echo "Encode:\n" . print_r($jwt, true) . "\n"; 204 | 205 | // Get public key from the private key, or pull from from a file. 206 | $publicKey = openssl_pkey_get_details($privateKey)['key']; 207 | 208 | $decoded = JWT::decode($jwt, new Key($publicKey, 'RS256')); 209 | echo "Decode:\n" . print_r((array) $decoded, true) . "\n"; 210 | ``` 211 | 212 | Example with EdDSA (libsodium and Ed25519 signature) 213 | ---------------------------- 214 | ```php 215 | use Firebase\JWT\JWT; 216 | use Firebase\JWT\Key; 217 | 218 | // Public and private keys are expected to be Base64 encoded. The last 219 | // non-empty line is used so that keys can be generated with 220 | // sodium_crypto_sign_keypair(). The secret keys generated by other tools may 221 | // need to be adjusted to match the input expected by libsodium. 222 | 223 | $keyPair = sodium_crypto_sign_keypair(); 224 | 225 | $privateKey = base64_encode(sodium_crypto_sign_secretkey($keyPair)); 226 | 227 | $publicKey = base64_encode(sodium_crypto_sign_publickey($keyPair)); 228 | 229 | $payload = [ 230 | 'iss' => 'example.org', 231 | 'aud' => 'example.com', 232 | 'iat' => 1356999524, 233 | 'nbf' => 1357000000 234 | ]; 235 | 236 | $jwt = JWT::encode($payload, $privateKey, 'EdDSA'); 237 | echo "Encode:\n" . print_r($jwt, true) . "\n"; 238 | 239 | $decoded = JWT::decode($jwt, new Key($publicKey, 'EdDSA')); 240 | echo "Decode:\n" . print_r((array) $decoded, true) . "\n"; 241 | ```` 242 | 243 | Example with multiple keys 244 | -------------------------- 245 | ```php 246 | use Firebase\JWT\JWT; 247 | use Firebase\JWT\Key; 248 | 249 | // Example RSA keys from previous example 250 | // $privateKey1 = '...'; 251 | // $publicKey1 = '...'; 252 | 253 | // Example EdDSA keys from previous example 254 | // $privateKey2 = '...'; 255 | // $publicKey2 = '...'; 256 | 257 | $payload = [ 258 | 'iss' => 'example.org', 259 | 'aud' => 'example.com', 260 | 'iat' => 1356999524, 261 | 'nbf' => 1357000000 262 | ]; 263 | 264 | $jwt1 = JWT::encode($payload, $privateKey1, 'RS256', 'kid1'); 265 | $jwt2 = JWT::encode($payload, $privateKey2, 'EdDSA', 'kid2'); 266 | echo "Encode 1:\n" . print_r($jwt1, true) . "\n"; 267 | echo "Encode 2:\n" . print_r($jwt2, true) . "\n"; 268 | 269 | $keys = [ 270 | 'kid1' => new Key($publicKey1, 'RS256'), 271 | 'kid2' => new Key($publicKey2, 'EdDSA'), 272 | ]; 273 | 274 | $decoded1 = JWT::decode($jwt1, $keys); 275 | $decoded2 = JWT::decode($jwt2, $keys); 276 | 277 | echo "Decode 1:\n" . print_r((array) $decoded1, true) . "\n"; 278 | echo "Decode 2:\n" . print_r((array) $decoded2, true) . "\n"; 279 | ``` 280 | 281 | Using JWKs 282 | ---------- 283 | 284 | ```php 285 | use Firebase\JWT\JWK; 286 | use Firebase\JWT\JWT; 287 | 288 | // Set of keys. The "keys" key is required. For example, the JSON response to 289 | // this endpoint: https://www.gstatic.com/iap/verify/public_key-jwk 290 | $jwks = ['keys' => []]; 291 | 292 | // JWK::parseKeySet($jwks) returns an associative array of **kid** to Firebase\JWT\Key 293 | // objects. Pass this as the second parameter to JWT::decode. 294 | JWT::decode($jwt, JWK::parseKeySet($jwks)); 295 | ``` 296 | 297 | Using Cached Key Sets 298 | --------------------- 299 | 300 | The `CachedKeySet` class can be used to fetch and cache JWKS (JSON Web Key Sets) from a public URI. 301 | This has the following advantages: 302 | 303 | 1. The results are cached for performance. 304 | 2. If an unrecognized key is requested, the cache is refreshed, to accomodate for key rotation. 305 | 3. If rate limiting is enabled, the JWKS URI will not make more than 10 requests a second. 306 | 307 | ```php 308 | use Firebase\JWT\CachedKeySet; 309 | use Firebase\JWT\JWT; 310 | 311 | // The URI for the JWKS you wish to cache the results from 312 | $jwksUri = 'https://www.gstatic.com/iap/verify/public_key-jwk'; 313 | 314 | // Create an HTTP client (can be any PSR-7 compatible HTTP client) 315 | $httpClient = new GuzzleHttp\Client(); 316 | 317 | // Create an HTTP request factory (can be any PSR-17 compatible HTTP request factory) 318 | $httpFactory = new GuzzleHttp\Psr\HttpFactory(); 319 | 320 | // Create a cache item pool (can be any PSR-6 compatible cache item pool) 321 | $cacheItemPool = Phpfastcache\CacheManager::getInstance('files'); 322 | 323 | $keySet = new CachedKeySet( 324 | $jwksUri, 325 | $httpClient, 326 | $httpFactory, 327 | $cacheItemPool, 328 | null, // $expiresAfter int seconds to set the JWKS to expire 329 | true // $rateLimit true to enable rate limit of 10 RPS on lookup of invalid keys 330 | ); 331 | 332 | $jwt = 'eyJhbGci...'; // Some JWT signed by a key from the $jwkUri above 333 | $decoded = JWT::decode($jwt, $keySet); 334 | ``` 335 | 336 | Miscellaneous 337 | ------------- 338 | 339 | #### Exception Handling 340 | 341 | When a call to `JWT::decode` is invalid, it will throw one of the following exceptions: 342 | 343 | ```php 344 | use Firebase\JWT\JWT; 345 | use Firebase\JWT\SignatureInvalidException; 346 | use Firebase\JWT\BeforeValidException; 347 | use Firebase\JWT\ExpiredException; 348 | use DomainException; 349 | use InvalidArgumentException; 350 | use UnexpectedValueException; 351 | 352 | try { 353 | $decoded = JWT::decode($jwt, $keys); 354 | } catch (InvalidArgumentException $e) { 355 | // provided key/key-array is empty or malformed. 356 | } catch (DomainException $e) { 357 | // provided algorithm is unsupported OR 358 | // provided key is invalid OR 359 | // unknown error thrown in openSSL or libsodium OR 360 | // libsodium is required but not available. 361 | } catch (SignatureInvalidException $e) { 362 | // provided JWT signature verification failed. 363 | } catch (BeforeValidException $e) { 364 | // provided JWT is trying to be used before "nbf" claim OR 365 | // provided JWT is trying to be used before "iat" claim. 366 | } catch (ExpiredException $e) { 367 | // provided JWT is trying to be used after "exp" claim. 368 | } catch (UnexpectedValueException $e) { 369 | // provided JWT is malformed OR 370 | // provided JWT is missing an algorithm / using an unsupported algorithm OR 371 | // provided JWT algorithm does not match provided key OR 372 | // provided key ID in key/key-array is empty or invalid. 373 | } 374 | ``` 375 | 376 | All exceptions in the `Firebase\JWT` namespace extend `UnexpectedValueException`, and can be simplified 377 | like this: 378 | 379 | ```php 380 | use Firebase\JWT\JWT; 381 | use UnexpectedValueException; 382 | try { 383 | $decoded = JWT::decode($jwt, $keys); 384 | } catch (LogicException $e) { 385 | // errors having to do with environmental setup or malformed JWT Keys 386 | } catch (UnexpectedValueException $e) { 387 | // errors having to do with JWT signature and claims 388 | } 389 | ``` 390 | 391 | #### Casting to array 392 | 393 | The return value of `JWT::decode` is the generic PHP object `stdClass`. If you'd like to handle with arrays 394 | instead, you can do the following: 395 | 396 | ```php 397 | // return type is stdClass 398 | $decoded = JWT::decode($jwt, $keys); 399 | 400 | // cast to array 401 | $decoded = json_decode(json_encode($decoded), true); 402 | ``` 403 | 404 | Tests 405 | ----- 406 | Run the tests using phpunit: 407 | 408 | ```bash 409 | $ pear install PHPUnit 410 | $ phpunit --configuration phpunit.xml.dist 411 | PHPUnit 3.7.10 by Sebastian Bergmann. 412 | ..... 413 | Time: 0 seconds, Memory: 2.50Mb 414 | OK (5 tests, 5 assertions) 415 | ``` 416 | 417 | New Lines in private keys 418 | ----- 419 | 420 | If your private key contains `\n` characters, be sure to wrap it in double quotes `""` 421 | and not single quotes `''` in order to properly interpret the escaped characters. 422 | 423 | License 424 | ------- 425 | [3-Clause BSD](http://opensource.org/licenses/BSD-3-Clause). 426 | -------------------------------------------------------------------------------- /firebase-php-jwt-d1e91ec/composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "firebase/php-jwt", 3 | "description": "A simple library to encode and decode JSON Web Tokens (JWT) in PHP. Should conform to the current spec.", 4 | "homepage": "https://github.com/firebase/php-jwt", 5 | "keywords": [ 6 | "php", 7 | "jwt" 8 | ], 9 | "authors": [ 10 | { 11 | "name": "Neuman Vong", 12 | "email": "neuman+pear@twilio.com", 13 | "role": "Developer" 14 | }, 15 | { 16 | "name": "Anant Narayanan", 17 | "email": "anant@php.net", 18 | "role": "Developer" 19 | } 20 | ], 21 | "license": "BSD-3-Clause", 22 | "require": { 23 | "php": "^8.0" 24 | }, 25 | "suggest": { 26 | "paragonie/sodium_compat": "Support EdDSA (Ed25519) signatures when libsodium is not present", 27 | "ext-sodium": "Support EdDSA (Ed25519) signatures" 28 | }, 29 | "autoload": { 30 | "psr-4": { 31 | "Firebase\\JWT\\": "src" 32 | } 33 | }, 34 | "require-dev": { 35 | "guzzlehttp/guzzle": "^7.4", 36 | "phpspec/prophecy-phpunit": "^2.0", 37 | "phpunit/phpunit": "^9.5", 38 | "psr/cache": "^2.0||^3.0", 39 | "psr/http-client": "^1.0", 40 | "psr/http-factory": "^1.0" 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /firebase-php-jwt-d1e91ec/src/BeforeValidException.php: -------------------------------------------------------------------------------- 1 | payload = $payload; 12 | } 13 | 14 | public function getPayload(): object 15 | { 16 | return $this->payload; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /firebase-php-jwt-d1e91ec/src/CachedKeySet.php: -------------------------------------------------------------------------------- 1 | 18 | */ 19 | class CachedKeySet implements ArrayAccess 20 | { 21 | /** 22 | * @var string 23 | */ 24 | private $jwksUri; 25 | /** 26 | * @var ClientInterface 27 | */ 28 | private $httpClient; 29 | /** 30 | * @var RequestFactoryInterface 31 | */ 32 | private $httpFactory; 33 | /** 34 | * @var CacheItemPoolInterface 35 | */ 36 | private $cache; 37 | /** 38 | * @var ?int 39 | */ 40 | private $expiresAfter; 41 | /** 42 | * @var ?CacheItemInterface 43 | */ 44 | private $cacheItem; 45 | /** 46 | * @var array> 47 | */ 48 | private $keySet; 49 | /** 50 | * @var string 51 | */ 52 | private $cacheKey; 53 | /** 54 | * @var string 55 | */ 56 | private $cacheKeyPrefix = 'jwks'; 57 | /** 58 | * @var int 59 | */ 60 | private $maxKeyLength = 64; 61 | /** 62 | * @var bool 63 | */ 64 | private $rateLimit; 65 | /** 66 | * @var string 67 | */ 68 | private $rateLimitCacheKey; 69 | /** 70 | * @var int 71 | */ 72 | private $maxCallsPerMinute = 10; 73 | /** 74 | * @var string|null 75 | */ 76 | private $defaultAlg; 77 | 78 | public function __construct( 79 | string $jwksUri, 80 | ClientInterface $httpClient, 81 | RequestFactoryInterface $httpFactory, 82 | CacheItemPoolInterface $cache, 83 | ?int $expiresAfter = null, 84 | bool $rateLimit = false, 85 | ?string $defaultAlg = null 86 | ) { 87 | $this->jwksUri = $jwksUri; 88 | $this->httpClient = $httpClient; 89 | $this->httpFactory = $httpFactory; 90 | $this->cache = $cache; 91 | $this->expiresAfter = $expiresAfter; 92 | $this->rateLimit = $rateLimit; 93 | $this->defaultAlg = $defaultAlg; 94 | $this->setCacheKeys(); 95 | } 96 | 97 | /** 98 | * @param string $keyId 99 | * @return Key 100 | */ 101 | public function offsetGet($keyId): Key 102 | { 103 | if (!$this->keyIdExists($keyId)) { 104 | throw new OutOfBoundsException('Key ID not found'); 105 | } 106 | return JWK::parseKey($this->keySet[$keyId], $this->defaultAlg); 107 | } 108 | 109 | /** 110 | * @param string $keyId 111 | * @return bool 112 | */ 113 | public function offsetExists($keyId): bool 114 | { 115 | return $this->keyIdExists($keyId); 116 | } 117 | 118 | /** 119 | * @param string $offset 120 | * @param Key $value 121 | */ 122 | public function offsetSet($offset, $value): void 123 | { 124 | throw new LogicException('Method not implemented'); 125 | } 126 | 127 | /** 128 | * @param string $offset 129 | */ 130 | public function offsetUnset($offset): void 131 | { 132 | throw new LogicException('Method not implemented'); 133 | } 134 | 135 | /** 136 | * @return array 137 | */ 138 | private function formatJwksForCache(string $jwks): array 139 | { 140 | $jwks = json_decode($jwks, true); 141 | 142 | if (!isset($jwks['keys'])) { 143 | throw new UnexpectedValueException('"keys" member must exist in the JWK Set'); 144 | } 145 | 146 | if (empty($jwks['keys'])) { 147 | throw new InvalidArgumentException('JWK Set did not contain any keys'); 148 | } 149 | 150 | $keys = []; 151 | foreach ($jwks['keys'] as $k => $v) { 152 | $kid = isset($v['kid']) ? $v['kid'] : $k; 153 | $keys[(string) $kid] = $v; 154 | } 155 | 156 | return $keys; 157 | } 158 | 159 | private function keyIdExists(string $keyId): bool 160 | { 161 | if (null === $this->keySet) { 162 | $item = $this->getCacheItem(); 163 | // Try to load keys from cache 164 | if ($item->isHit()) { 165 | // item found! retrieve it 166 | $this->keySet = $item->get(); 167 | // If the cached item is a string, the JWKS response was cached (previous behavior). 168 | // Parse this into expected format array instead. 169 | if (\is_string($this->keySet)) { 170 | $this->keySet = $this->formatJwksForCache($this->keySet); 171 | } 172 | } 173 | } 174 | 175 | if (!isset($this->keySet[$keyId])) { 176 | if ($this->rateLimitExceeded()) { 177 | return false; 178 | } 179 | $request = $this->httpFactory->createRequest('GET', $this->jwksUri); 180 | $jwksResponse = $this->httpClient->sendRequest($request); 181 | if ($jwksResponse->getStatusCode() !== 200) { 182 | throw new UnexpectedValueException( 183 | \sprintf('HTTP Error: %d %s for URI "%s"', 184 | $jwksResponse->getStatusCode(), 185 | $jwksResponse->getReasonPhrase(), 186 | $this->jwksUri, 187 | ), 188 | $jwksResponse->getStatusCode() 189 | ); 190 | } 191 | $this->keySet = $this->formatJwksForCache((string) $jwksResponse->getBody()); 192 | 193 | if (!isset($this->keySet[$keyId])) { 194 | return false; 195 | } 196 | 197 | $item = $this->getCacheItem(); 198 | $item->set($this->keySet); 199 | if ($this->expiresAfter) { 200 | $item->expiresAfter($this->expiresAfter); 201 | } 202 | $this->cache->save($item); 203 | } 204 | 205 | return true; 206 | } 207 | 208 | private function rateLimitExceeded(): bool 209 | { 210 | if (!$this->rateLimit) { 211 | return false; 212 | } 213 | 214 | $cacheItem = $this->cache->getItem($this->rateLimitCacheKey); 215 | 216 | $cacheItemData = []; 217 | if ($cacheItem->isHit() && \is_array($data = $cacheItem->get())) { 218 | $cacheItemData = $data; 219 | } 220 | 221 | $callsPerMinute = $cacheItemData['callsPerMinute'] ?? 0; 222 | $expiry = $cacheItemData['expiry'] ?? new \DateTime('+60 seconds', new \DateTimeZone('UTC')); 223 | 224 | if (++$callsPerMinute > $this->maxCallsPerMinute) { 225 | return true; 226 | } 227 | 228 | $cacheItem->set(['expiry' => $expiry, 'callsPerMinute' => $callsPerMinute]); 229 | $cacheItem->expiresAt($expiry); 230 | $this->cache->save($cacheItem); 231 | return false; 232 | } 233 | 234 | private function getCacheItem(): CacheItemInterface 235 | { 236 | if (\is_null($this->cacheItem)) { 237 | $this->cacheItem = $this->cache->getItem($this->cacheKey); 238 | } 239 | 240 | return $this->cacheItem; 241 | } 242 | 243 | private function setCacheKeys(): void 244 | { 245 | if (empty($this->jwksUri)) { 246 | throw new RuntimeException('JWKS URI is empty'); 247 | } 248 | 249 | // ensure we do not have illegal characters 250 | $key = preg_replace('|[^a-zA-Z0-9_\.!]|', '', $this->jwksUri); 251 | 252 | // add prefix 253 | $key = $this->cacheKeyPrefix . $key; 254 | 255 | // Hash keys if they exceed $maxKeyLength of 64 256 | if (\strlen($key) > $this->maxKeyLength) { 257 | $key = substr(hash('sha256', $key), 0, $this->maxKeyLength); 258 | } 259 | 260 | $this->cacheKey = $key; 261 | 262 | if ($this->rateLimit) { 263 | // add prefix 264 | $rateLimitKey = $this->cacheKeyPrefix . 'ratelimit' . $key; 265 | 266 | // Hash keys if they exceed $maxKeyLength of 64 267 | if (\strlen($rateLimitKey) > $this->maxKeyLength) { 268 | $rateLimitKey = substr(hash('sha256', $rateLimitKey), 0, $this->maxKeyLength); 269 | } 270 | 271 | $this->rateLimitCacheKey = $rateLimitKey; 272 | } 273 | } 274 | } 275 | -------------------------------------------------------------------------------- /firebase-php-jwt-d1e91ec/src/ExpiredException.php: -------------------------------------------------------------------------------- 1 | payload = $payload; 12 | } 13 | 14 | public function getPayload(): object 15 | { 16 | return $this->payload; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /firebase-php-jwt-d1e91ec/src/JWK.php: -------------------------------------------------------------------------------- 1 | 18 | * @license http://opensource.org/licenses/BSD-3-Clause 3-clause BSD 19 | * @link https://github.com/firebase/php-jwt 20 | */ 21 | class JWK 22 | { 23 | private const OID = '1.2.840.10045.2.1'; 24 | private const ASN1_OBJECT_IDENTIFIER = 0x06; 25 | private const ASN1_SEQUENCE = 0x10; // also defined in JWT 26 | private const ASN1_BIT_STRING = 0x03; 27 | private const EC_CURVES = [ 28 | 'P-256' => '1.2.840.10045.3.1.7', // Len: 64 29 | 'secp256k1' => '1.3.132.0.10', // Len: 64 30 | 'P-384' => '1.3.132.0.34', // Len: 96 31 | // 'P-521' => '1.3.132.0.35', // Len: 132 (not supported) 32 | ]; 33 | 34 | // For keys with "kty" equal to "OKP" (Octet Key Pair), the "crv" parameter must contain the key subtype. 35 | // This library supports the following subtypes: 36 | private const OKP_SUBTYPES = [ 37 | 'Ed25519' => true, // RFC 8037 38 | ]; 39 | 40 | /** 41 | * Parse a set of JWK keys 42 | * 43 | * @param array $jwks The JSON Web Key Set as an associative array 44 | * @param string $defaultAlg The algorithm for the Key object if "alg" is not set in the 45 | * JSON Web Key Set 46 | * 47 | * @return array An associative array of key IDs (kid) to Key objects 48 | * 49 | * @throws InvalidArgumentException Provided JWK Set is empty 50 | * @throws UnexpectedValueException Provided JWK Set was invalid 51 | * @throws DomainException OpenSSL failure 52 | * 53 | * @uses parseKey 54 | */ 55 | public static function parseKeySet(array $jwks, ?string $defaultAlg = null): array 56 | { 57 | $keys = []; 58 | 59 | if (!isset($jwks['keys'])) { 60 | throw new UnexpectedValueException('"keys" member must exist in the JWK Set'); 61 | } 62 | 63 | if (empty($jwks['keys'])) { 64 | throw new InvalidArgumentException('JWK Set did not contain any keys'); 65 | } 66 | 67 | foreach ($jwks['keys'] as $k => $v) { 68 | $kid = isset($v['kid']) ? $v['kid'] : $k; 69 | if ($key = self::parseKey($v, $defaultAlg)) { 70 | $keys[(string) $kid] = $key; 71 | } 72 | } 73 | 74 | if (0 === \count($keys)) { 75 | throw new UnexpectedValueException('No supported algorithms found in JWK Set'); 76 | } 77 | 78 | return $keys; 79 | } 80 | 81 | /** 82 | * Parse a JWK key 83 | * 84 | * @param array $jwk An individual JWK 85 | * @param string $defaultAlg The algorithm for the Key object if "alg" is not set in the 86 | * JSON Web Key Set 87 | * 88 | * @return Key The key object for the JWK 89 | * 90 | * @throws InvalidArgumentException Provided JWK is empty 91 | * @throws UnexpectedValueException Provided JWK was invalid 92 | * @throws DomainException OpenSSL failure 93 | * 94 | * @uses createPemFromModulusAndExponent 95 | */ 96 | public static function parseKey(array $jwk, ?string $defaultAlg = null): ?Key 97 | { 98 | if (empty($jwk)) { 99 | throw new InvalidArgumentException('JWK must not be empty'); 100 | } 101 | 102 | if (!isset($jwk['kty'])) { 103 | throw new UnexpectedValueException('JWK must contain a "kty" parameter'); 104 | } 105 | 106 | if (!isset($jwk['alg'])) { 107 | if (\is_null($defaultAlg)) { 108 | // The "alg" parameter is optional in a KTY, but an algorithm is required 109 | // for parsing in this library. Use the $defaultAlg parameter when parsing the 110 | // key set in order to prevent this error. 111 | // @see https://datatracker.ietf.org/doc/html/rfc7517#section-4.4 112 | throw new UnexpectedValueException('JWK must contain an "alg" parameter'); 113 | } 114 | $jwk['alg'] = $defaultAlg; 115 | } 116 | 117 | switch ($jwk['kty']) { 118 | case 'RSA': 119 | if (!empty($jwk['d'])) { 120 | throw new UnexpectedValueException('RSA private keys are not supported'); 121 | } 122 | if (!isset($jwk['n']) || !isset($jwk['e'])) { 123 | throw new UnexpectedValueException('RSA keys must contain values for both "n" and "e"'); 124 | } 125 | 126 | $pem = self::createPemFromModulusAndExponent($jwk['n'], $jwk['e']); 127 | $publicKey = \openssl_pkey_get_public($pem); 128 | if (false === $publicKey) { 129 | throw new DomainException( 130 | 'OpenSSL error: ' . \openssl_error_string() 131 | ); 132 | } 133 | return new Key($publicKey, $jwk['alg']); 134 | case 'EC': 135 | if (isset($jwk['d'])) { 136 | // The key is actually a private key 137 | throw new UnexpectedValueException('Key data must be for a public key'); 138 | } 139 | 140 | if (empty($jwk['crv'])) { 141 | throw new UnexpectedValueException('crv not set'); 142 | } 143 | 144 | if (!isset(self::EC_CURVES[$jwk['crv']])) { 145 | throw new DomainException('Unrecognised or unsupported EC curve'); 146 | } 147 | 148 | if (empty($jwk['x']) || empty($jwk['y'])) { 149 | throw new UnexpectedValueException('x and y not set'); 150 | } 151 | 152 | $publicKey = self::createPemFromCrvAndXYCoordinates($jwk['crv'], $jwk['x'], $jwk['y']); 153 | return new Key($publicKey, $jwk['alg']); 154 | case 'OKP': 155 | if (isset($jwk['d'])) { 156 | // The key is actually a private key 157 | throw new UnexpectedValueException('Key data must be for a public key'); 158 | } 159 | 160 | if (!isset($jwk['crv'])) { 161 | throw new UnexpectedValueException('crv not set'); 162 | } 163 | 164 | if (empty(self::OKP_SUBTYPES[$jwk['crv']])) { 165 | throw new DomainException('Unrecognised or unsupported OKP key subtype'); 166 | } 167 | 168 | if (empty($jwk['x'])) { 169 | throw new UnexpectedValueException('x not set'); 170 | } 171 | 172 | // This library works internally with EdDSA keys (Ed25519) encoded in standard base64. 173 | $publicKey = JWT::convertBase64urlToBase64($jwk['x']); 174 | return new Key($publicKey, $jwk['alg']); 175 | case 'oct': 176 | if (!isset($jwk['k'])) { 177 | throw new UnexpectedValueException('k not set'); 178 | } 179 | 180 | return new Key(JWT::urlsafeB64Decode($jwk['k']), $jwk['alg']); 181 | default: 182 | break; 183 | } 184 | 185 | return null; 186 | } 187 | 188 | /** 189 | * Converts the EC JWK values to pem format. 190 | * 191 | * @param string $crv The EC curve (only P-256 & P-384 is supported) 192 | * @param string $x The EC x-coordinate 193 | * @param string $y The EC y-coordinate 194 | * 195 | * @return string 196 | */ 197 | private static function createPemFromCrvAndXYCoordinates(string $crv, string $x, string $y): string 198 | { 199 | $pem = 200 | self::encodeDER( 201 | self::ASN1_SEQUENCE, 202 | self::encodeDER( 203 | self::ASN1_SEQUENCE, 204 | self::encodeDER( 205 | self::ASN1_OBJECT_IDENTIFIER, 206 | self::encodeOID(self::OID) 207 | ) 208 | . self::encodeDER( 209 | self::ASN1_OBJECT_IDENTIFIER, 210 | self::encodeOID(self::EC_CURVES[$crv]) 211 | ) 212 | ) . 213 | self::encodeDER( 214 | self::ASN1_BIT_STRING, 215 | \chr(0x00) . \chr(0x04) 216 | . JWT::urlsafeB64Decode($x) 217 | . JWT::urlsafeB64Decode($y) 218 | ) 219 | ); 220 | 221 | return \sprintf( 222 | "-----BEGIN PUBLIC KEY-----\n%s\n-----END PUBLIC KEY-----\n", 223 | wordwrap(base64_encode($pem), 64, "\n", true) 224 | ); 225 | } 226 | 227 | /** 228 | * Create a public key represented in PEM format from RSA modulus and exponent information 229 | * 230 | * @param string $n The RSA modulus encoded in Base64 231 | * @param string $e The RSA exponent encoded in Base64 232 | * 233 | * @return string The RSA public key represented in PEM format 234 | * 235 | * @uses encodeLength 236 | */ 237 | private static function createPemFromModulusAndExponent( 238 | string $n, 239 | string $e 240 | ): string { 241 | $mod = JWT::urlsafeB64Decode($n); 242 | $exp = JWT::urlsafeB64Decode($e); 243 | 244 | $modulus = \pack('Ca*a*', 2, self::encodeLength(\strlen($mod)), $mod); 245 | $publicExponent = \pack('Ca*a*', 2, self::encodeLength(\strlen($exp)), $exp); 246 | 247 | $rsaPublicKey = \pack( 248 | 'Ca*a*a*', 249 | 48, 250 | self::encodeLength(\strlen($modulus) + \strlen($publicExponent)), 251 | $modulus, 252 | $publicExponent 253 | ); 254 | 255 | // sequence(oid(1.2.840.113549.1.1.1), null)) = rsaEncryption. 256 | $rsaOID = \pack('H*', '300d06092a864886f70d0101010500'); // hex version of MA0GCSqGSIb3DQEBAQUA 257 | $rsaPublicKey = \chr(0) . $rsaPublicKey; 258 | $rsaPublicKey = \chr(3) . self::encodeLength(\strlen($rsaPublicKey)) . $rsaPublicKey; 259 | 260 | $rsaPublicKey = \pack( 261 | 'Ca*a*', 262 | 48, 263 | self::encodeLength(\strlen($rsaOID . $rsaPublicKey)), 264 | $rsaOID . $rsaPublicKey 265 | ); 266 | 267 | return "-----BEGIN PUBLIC KEY-----\r\n" . 268 | \chunk_split(\base64_encode($rsaPublicKey), 64) . 269 | '-----END PUBLIC KEY-----'; 270 | } 271 | 272 | /** 273 | * DER-encode the length 274 | * 275 | * DER supports lengths up to (2**8)**127, however, we'll only support lengths up to (2**8)**4. See 276 | * {@link http://itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf#p=13 X.690 paragraph 8.1.3} for more information. 277 | * 278 | * @param int $length 279 | * @return string 280 | */ 281 | private static function encodeLength(int $length): string 282 | { 283 | if ($length <= 0x7F) { 284 | return \chr($length); 285 | } 286 | 287 | $temp = \ltrim(\pack('N', $length), \chr(0)); 288 | 289 | return \pack('Ca*', 0x80 | \strlen($temp), $temp); 290 | } 291 | 292 | /** 293 | * Encodes a value into a DER object. 294 | * Also defined in Firebase\JWT\JWT 295 | * 296 | * @param int $type DER tag 297 | * @param string $value the value to encode 298 | * @return string the encoded object 299 | */ 300 | private static function encodeDER(int $type, string $value): string 301 | { 302 | $tag_header = 0; 303 | if ($type === self::ASN1_SEQUENCE) { 304 | $tag_header |= 0x20; 305 | } 306 | 307 | // Type 308 | $der = \chr($tag_header | $type); 309 | 310 | // Length 311 | $der .= \chr(\strlen($value)); 312 | 313 | return $der . $value; 314 | } 315 | 316 | /** 317 | * Encodes a string into a DER-encoded OID. 318 | * 319 | * @param string $oid the OID string 320 | * @return string the binary DER-encoded OID 321 | */ 322 | private static function encodeOID(string $oid): string 323 | { 324 | $octets = explode('.', $oid); 325 | 326 | // Get the first octet 327 | $first = (int) array_shift($octets); 328 | $second = (int) array_shift($octets); 329 | $oid = \chr($first * 40 + $second); 330 | 331 | // Iterate over subsequent octets 332 | foreach ($octets as $octet) { 333 | if ($octet == 0) { 334 | $oid .= \chr(0x00); 335 | continue; 336 | } 337 | $bin = ''; 338 | 339 | while ($octet) { 340 | $bin .= \chr(0x80 | ($octet & 0x7f)); 341 | $octet >>= 7; 342 | } 343 | $bin[0] = $bin[0] & \chr(0x7f); 344 | 345 | // Convert to big endian if necessary 346 | if (pack('V', 65534) == pack('L', 65534)) { 347 | $oid .= strrev($bin); 348 | } else { 349 | $oid .= $bin; 350 | } 351 | } 352 | 353 | return $oid; 354 | } 355 | } 356 | -------------------------------------------------------------------------------- /firebase-php-jwt-d1e91ec/src/JWT.php: -------------------------------------------------------------------------------- 1 | 24 | * @author Anant Narayanan 25 | * @license http://opensource.org/licenses/BSD-3-Clause 3-clause BSD 26 | * @link https://github.com/firebase/php-jwt 27 | */ 28 | class JWT 29 | { 30 | private const ASN1_INTEGER = 0x02; 31 | private const ASN1_SEQUENCE = 0x10; 32 | private const ASN1_BIT_STRING = 0x03; 33 | 34 | /** 35 | * When checking nbf, iat or expiration times, 36 | * we want to provide some extra leeway time to 37 | * account for clock skew. 38 | * 39 | * @var int 40 | */ 41 | public static $leeway = 0; 42 | 43 | /** 44 | * Allow the current timestamp to be specified. 45 | * Useful for fixing a value within unit testing. 46 | * Will default to PHP time() value if null. 47 | * 48 | * @var ?int 49 | */ 50 | public static $timestamp = null; 51 | 52 | /** 53 | * @var array 54 | */ 55 | public static $supported_algs = [ 56 | 'ES384' => ['openssl', 'SHA384'], 57 | 'ES256' => ['openssl', 'SHA256'], 58 | 'ES256K' => ['openssl', 'SHA256'], 59 | 'HS256' => ['hash_hmac', 'SHA256'], 60 | 'HS384' => ['hash_hmac', 'SHA384'], 61 | 'HS512' => ['hash_hmac', 'SHA512'], 62 | 'RS256' => ['openssl', 'SHA256'], 63 | 'RS384' => ['openssl', 'SHA384'], 64 | 'RS512' => ['openssl', 'SHA512'], 65 | 'EdDSA' => ['sodium_crypto', 'EdDSA'], 66 | ]; 67 | 68 | /** 69 | * Decodes a JWT string into a PHP object. 70 | * 71 | * @param string $jwt The JWT 72 | * @param Key|ArrayAccess|array $keyOrKeyArray The Key or associative array of key IDs 73 | * (kid) to Key objects. 74 | * If the algorithm used is asymmetric, this is 75 | * the public key. 76 | * Each Key object contains an algorithm and 77 | * matching key. 78 | * Supported algorithms are 'ES384','ES256', 79 | * 'HS256', 'HS384', 'HS512', 'RS256', 'RS384' 80 | * and 'RS512'. 81 | * @param stdClass $headers Optional. Populates stdClass with headers. 82 | * 83 | * @return stdClass The JWT's payload as a PHP object 84 | * 85 | * @throws InvalidArgumentException Provided key/key-array was empty or malformed 86 | * @throws DomainException Provided JWT is malformed 87 | * @throws UnexpectedValueException Provided JWT was invalid 88 | * @throws SignatureInvalidException Provided JWT was invalid because the signature verification failed 89 | * @throws BeforeValidException Provided JWT is trying to be used before it's eligible as defined by 'nbf' 90 | * @throws BeforeValidException Provided JWT is trying to be used before it's been created as defined by 'iat' 91 | * @throws ExpiredException Provided JWT has since expired, as defined by the 'exp' claim 92 | * 93 | * @uses jsonDecode 94 | * @uses urlsafeB64Decode 95 | */ 96 | public static function decode( 97 | string $jwt, 98 | $keyOrKeyArray, 99 | ?stdClass &$headers = null 100 | ): stdClass { 101 | // Validate JWT 102 | $timestamp = \is_null(static::$timestamp) ? \time() : static::$timestamp; 103 | 104 | if (empty($keyOrKeyArray)) { 105 | throw new InvalidArgumentException('Key may not be empty'); 106 | } 107 | $tks = \explode('.', $jwt); 108 | if (\count($tks) !== 3) { 109 | throw new UnexpectedValueException('Wrong number of segments'); 110 | } 111 | list($headb64, $bodyb64, $cryptob64) = $tks; 112 | $headerRaw = static::urlsafeB64Decode($headb64); 113 | if (null === ($header = static::jsonDecode($headerRaw))) { 114 | throw new UnexpectedValueException('Invalid header encoding'); 115 | } 116 | if ($headers !== null) { 117 | $headers = $header; 118 | } 119 | $payloadRaw = static::urlsafeB64Decode($bodyb64); 120 | if (null === ($payload = static::jsonDecode($payloadRaw))) { 121 | throw new UnexpectedValueException('Invalid claims encoding'); 122 | } 123 | if (\is_array($payload)) { 124 | // prevent PHP Fatal Error in edge-cases when payload is empty array 125 | $payload = (object) $payload; 126 | } 127 | if (!$payload instanceof stdClass) { 128 | throw new UnexpectedValueException('Payload must be a JSON object'); 129 | } 130 | $sig = static::urlsafeB64Decode($cryptob64); 131 | if (empty($header->alg)) { 132 | throw new UnexpectedValueException('Empty algorithm'); 133 | } 134 | if (empty(static::$supported_algs[$header->alg])) { 135 | throw new UnexpectedValueException('Algorithm not supported'); 136 | } 137 | 138 | $key = self::getKey($keyOrKeyArray, property_exists($header, 'kid') ? $header->kid : null); 139 | 140 | // Check the algorithm 141 | if (!self::constantTimeEquals($key->getAlgorithm(), $header->alg)) { 142 | // See issue #351 143 | throw new UnexpectedValueException('Incorrect key for this algorithm'); 144 | } 145 | if (\in_array($header->alg, ['ES256', 'ES256K', 'ES384'], true)) { 146 | // OpenSSL expects an ASN.1 DER sequence for ES256/ES256K/ES384 signatures 147 | $sig = self::signatureToDER($sig); 148 | } 149 | if (!self::verify("{$headb64}.{$bodyb64}", $sig, $key->getKeyMaterial(), $header->alg)) { 150 | throw new SignatureInvalidException('Signature verification failed'); 151 | } 152 | 153 | // Check the nbf if it is defined. This is the time that the 154 | // token can actually be used. If it's not yet that time, abort. 155 | if (isset($payload->nbf) && floor($payload->nbf) > ($timestamp + static::$leeway)) { 156 | $ex = new BeforeValidException( 157 | 'Cannot handle token with nbf prior to ' . \date(DateTime::ISO8601, (int) floor($payload->nbf)) 158 | ); 159 | $ex->setPayload($payload); 160 | throw $ex; 161 | } 162 | 163 | // Check that this token has been created before 'now'. This prevents 164 | // using tokens that have been created for later use (and haven't 165 | // correctly used the nbf claim). 166 | if (!isset($payload->nbf) && isset($payload->iat) && floor($payload->iat) > ($timestamp + static::$leeway)) { 167 | $ex = new BeforeValidException( 168 | 'Cannot handle token with iat prior to ' . \date(DateTime::ISO8601, (int) floor($payload->iat)) 169 | ); 170 | $ex->setPayload($payload); 171 | throw $ex; 172 | } 173 | 174 | // Check if this token has expired. 175 | if (isset($payload->exp) && ($timestamp - static::$leeway) >= $payload->exp) { 176 | $ex = new ExpiredException('Expired token'); 177 | $ex->setPayload($payload); 178 | throw $ex; 179 | } 180 | 181 | return $payload; 182 | } 183 | 184 | /** 185 | * Converts and signs a PHP array into a JWT string. 186 | * 187 | * @param array $payload PHP array 188 | * @param string|resource|OpenSSLAsymmetricKey|OpenSSLCertificate $key The secret key. 189 | * @param string $alg Supported algorithms are 'ES384','ES256', 'ES256K', 'HS256', 190 | * 'HS384', 'HS512', 'RS256', 'RS384', and 'RS512' 191 | * @param string $keyId 192 | * @param array $head An array with header elements to attach 193 | * 194 | * @return string A signed JWT 195 | * 196 | * @uses jsonEncode 197 | * @uses urlsafeB64Encode 198 | */ 199 | public static function encode( 200 | array $payload, 201 | $key, 202 | string $alg, 203 | ?string $keyId = null, 204 | ?array $head = null 205 | ): string { 206 | $header = ['typ' => 'JWT']; 207 | if (isset($head)) { 208 | $header = \array_merge($header, $head); 209 | } 210 | $header['alg'] = $alg; 211 | if ($keyId !== null) { 212 | $header['kid'] = $keyId; 213 | } 214 | $segments = []; 215 | $segments[] = static::urlsafeB64Encode((string) static::jsonEncode($header)); 216 | $segments[] = static::urlsafeB64Encode((string) static::jsonEncode($payload)); 217 | $signing_input = \implode('.', $segments); 218 | 219 | $signature = static::sign($signing_input, $key, $alg); 220 | $segments[] = static::urlsafeB64Encode($signature); 221 | 222 | return \implode('.', $segments); 223 | } 224 | 225 | /** 226 | * Sign a string with a given key and algorithm. 227 | * 228 | * @param string $msg The message to sign 229 | * @param string|resource|OpenSSLAsymmetricKey|OpenSSLCertificate $key The secret key. 230 | * @param string $alg Supported algorithms are 'EdDSA', 'ES384', 'ES256', 'ES256K', 'HS256', 231 | * 'HS384', 'HS512', 'RS256', 'RS384', and 'RS512' 232 | * 233 | * @return string An encrypted message 234 | * 235 | * @throws DomainException Unsupported algorithm or bad key was specified 236 | */ 237 | public static function sign( 238 | string $msg, 239 | $key, 240 | string $alg 241 | ): string { 242 | if (empty(static::$supported_algs[$alg])) { 243 | throw new DomainException('Algorithm not supported'); 244 | } 245 | list($function, $algorithm) = static::$supported_algs[$alg]; 246 | switch ($function) { 247 | case 'hash_hmac': 248 | if (!\is_string($key)) { 249 | throw new InvalidArgumentException('key must be a string when using hmac'); 250 | } 251 | return \hash_hmac($algorithm, $msg, $key, true); 252 | case 'openssl': 253 | $signature = ''; 254 | if (!\is_resource($key) && !openssl_pkey_get_private($key)) { 255 | throw new DomainException('OpenSSL unable to validate key'); 256 | } 257 | $success = \openssl_sign($msg, $signature, $key, $algorithm); // @phpstan-ignore-line 258 | if (!$success) { 259 | throw new DomainException('OpenSSL unable to sign data'); 260 | } 261 | if ($alg === 'ES256' || $alg === 'ES256K') { 262 | $signature = self::signatureFromDER($signature, 256); 263 | } elseif ($alg === 'ES384') { 264 | $signature = self::signatureFromDER($signature, 384); 265 | } 266 | return $signature; 267 | case 'sodium_crypto': 268 | if (!\function_exists('sodium_crypto_sign_detached')) { 269 | throw new DomainException('libsodium is not available'); 270 | } 271 | if (!\is_string($key)) { 272 | throw new InvalidArgumentException('key must be a string when using EdDSA'); 273 | } 274 | try { 275 | // The last non-empty line is used as the key. 276 | $lines = array_filter(explode("\n", $key)); 277 | $key = base64_decode((string) end($lines)); 278 | if (\strlen($key) === 0) { 279 | throw new DomainException('Key cannot be empty string'); 280 | } 281 | return sodium_crypto_sign_detached($msg, $key); 282 | } catch (Exception $e) { 283 | throw new DomainException($e->getMessage(), 0, $e); 284 | } 285 | } 286 | 287 | throw new DomainException('Algorithm not supported'); 288 | } 289 | 290 | /** 291 | * Verify a signature with the message, key and method. Not all methods 292 | * are symmetric, so we must have a separate verify and sign method. 293 | * 294 | * @param string $msg The original message (header and body) 295 | * @param string $signature The original signature 296 | * @param string|resource|OpenSSLAsymmetricKey|OpenSSLCertificate $keyMaterial For Ed*, ES*, HS*, a string key works. for RS*, must be an instance of OpenSSLAsymmetricKey 297 | * @param string $alg The algorithm 298 | * 299 | * @return bool 300 | * 301 | * @throws DomainException Invalid Algorithm, bad key, or OpenSSL failure 302 | */ 303 | private static function verify( 304 | string $msg, 305 | string $signature, 306 | $keyMaterial, 307 | string $alg 308 | ): bool { 309 | if (empty(static::$supported_algs[$alg])) { 310 | throw new DomainException('Algorithm not supported'); 311 | } 312 | 313 | list($function, $algorithm) = static::$supported_algs[$alg]; 314 | switch ($function) { 315 | case 'openssl': 316 | $success = \openssl_verify($msg, $signature, $keyMaterial, $algorithm); // @phpstan-ignore-line 317 | if ($success === 1) { 318 | return true; 319 | } 320 | if ($success === 0) { 321 | return false; 322 | } 323 | // returns 1 on success, 0 on failure, -1 on error. 324 | throw new DomainException( 325 | 'OpenSSL error: ' . \openssl_error_string() 326 | ); 327 | case 'sodium_crypto': 328 | if (!\function_exists('sodium_crypto_sign_verify_detached')) { 329 | throw new DomainException('libsodium is not available'); 330 | } 331 | if (!\is_string($keyMaterial)) { 332 | throw new InvalidArgumentException('key must be a string when using EdDSA'); 333 | } 334 | try { 335 | // The last non-empty line is used as the key. 336 | $lines = array_filter(explode("\n", $keyMaterial)); 337 | $key = base64_decode((string) end($lines)); 338 | if (\strlen($key) === 0) { 339 | throw new DomainException('Key cannot be empty string'); 340 | } 341 | if (\strlen($signature) === 0) { 342 | throw new DomainException('Signature cannot be empty string'); 343 | } 344 | return sodium_crypto_sign_verify_detached($signature, $msg, $key); 345 | } catch (Exception $e) { 346 | throw new DomainException($e->getMessage(), 0, $e); 347 | } 348 | case 'hash_hmac': 349 | default: 350 | if (!\is_string($keyMaterial)) { 351 | throw new InvalidArgumentException('key must be a string when using hmac'); 352 | } 353 | $hash = \hash_hmac($algorithm, $msg, $keyMaterial, true); 354 | return self::constantTimeEquals($hash, $signature); 355 | } 356 | } 357 | 358 | /** 359 | * Decode a JSON string into a PHP object. 360 | * 361 | * @param string $input JSON string 362 | * 363 | * @return mixed The decoded JSON string 364 | * 365 | * @throws DomainException Provided string was invalid JSON 366 | */ 367 | public static function jsonDecode(string $input) 368 | { 369 | $obj = \json_decode($input, false, 512, JSON_BIGINT_AS_STRING); 370 | 371 | if ($errno = \json_last_error()) { 372 | self::handleJsonError($errno); 373 | } elseif ($obj === null && $input !== 'null') { 374 | throw new DomainException('Null result with non-null input'); 375 | } 376 | return $obj; 377 | } 378 | 379 | /** 380 | * Encode a PHP array into a JSON string. 381 | * 382 | * @param array $input A PHP array 383 | * 384 | * @return string JSON representation of the PHP array 385 | * 386 | * @throws DomainException Provided object could not be encoded to valid JSON 387 | */ 388 | public static function jsonEncode(array $input): string 389 | { 390 | $json = \json_encode($input, \JSON_UNESCAPED_SLASHES); 391 | if ($errno = \json_last_error()) { 392 | self::handleJsonError($errno); 393 | } elseif ($json === 'null') { 394 | throw new DomainException('Null result with non-null input'); 395 | } 396 | if ($json === false) { 397 | throw new DomainException('Provided object could not be encoded to valid JSON'); 398 | } 399 | return $json; 400 | } 401 | 402 | /** 403 | * Decode a string with URL-safe Base64. 404 | * 405 | * @param string $input A Base64 encoded string 406 | * 407 | * @return string A decoded string 408 | * 409 | * @throws InvalidArgumentException invalid base64 characters 410 | */ 411 | public static function urlsafeB64Decode(string $input): string 412 | { 413 | return \base64_decode(self::convertBase64UrlToBase64($input)); 414 | } 415 | 416 | /** 417 | * Convert a string in the base64url (URL-safe Base64) encoding to standard base64. 418 | * 419 | * @param string $input A Base64 encoded string with URL-safe characters (-_ and no padding) 420 | * 421 | * @return string A Base64 encoded string with standard characters (+/) and padding (=), when 422 | * needed. 423 | * 424 | * @see https://www.rfc-editor.org/rfc/rfc4648 425 | */ 426 | public static function convertBase64UrlToBase64(string $input): string 427 | { 428 | $remainder = \strlen($input) % 4; 429 | if ($remainder) { 430 | $padlen = 4 - $remainder; 431 | $input .= \str_repeat('=', $padlen); 432 | } 433 | return \strtr($input, '-_', '+/'); 434 | } 435 | 436 | /** 437 | * Encode a string with URL-safe Base64. 438 | * 439 | * @param string $input The string you want encoded 440 | * 441 | * @return string The base64 encode of what you passed in 442 | */ 443 | public static function urlsafeB64Encode(string $input): string 444 | { 445 | return \str_replace('=', '', \strtr(\base64_encode($input), '+/', '-_')); 446 | } 447 | 448 | 449 | /** 450 | * Determine if an algorithm has been provided for each Key 451 | * 452 | * @param Key|ArrayAccess|array $keyOrKeyArray 453 | * @param string|null $kid 454 | * 455 | * @throws UnexpectedValueException 456 | * 457 | * @return Key 458 | */ 459 | private static function getKey( 460 | $keyOrKeyArray, 461 | ?string $kid 462 | ): Key { 463 | if ($keyOrKeyArray instanceof Key) { 464 | return $keyOrKeyArray; 465 | } 466 | 467 | if (empty($kid) && $kid !== '0') { 468 | throw new UnexpectedValueException('"kid" empty, unable to lookup correct key'); 469 | } 470 | 471 | if ($keyOrKeyArray instanceof CachedKeySet) { 472 | // Skip "isset" check, as this will automatically refresh if not set 473 | return $keyOrKeyArray[$kid]; 474 | } 475 | 476 | if (!isset($keyOrKeyArray[$kid])) { 477 | throw new UnexpectedValueException('"kid" invalid, unable to lookup correct key'); 478 | } 479 | 480 | return $keyOrKeyArray[$kid]; 481 | } 482 | 483 | /** 484 | * @param string $left The string of known length to compare against 485 | * @param string $right The user-supplied string 486 | * @return bool 487 | */ 488 | public static function constantTimeEquals(string $left, string $right): bool 489 | { 490 | if (\function_exists('hash_equals')) { 491 | return \hash_equals($left, $right); 492 | } 493 | $len = \min(self::safeStrlen($left), self::safeStrlen($right)); 494 | 495 | $status = 0; 496 | for ($i = 0; $i < $len; $i++) { 497 | $status |= (\ord($left[$i]) ^ \ord($right[$i])); 498 | } 499 | $status |= (self::safeStrlen($left) ^ self::safeStrlen($right)); 500 | 501 | return ($status === 0); 502 | } 503 | 504 | /** 505 | * Helper method to create a JSON error. 506 | * 507 | * @param int $errno An error number from json_last_error() 508 | * 509 | * @throws DomainException 510 | * 511 | * @return void 512 | */ 513 | private static function handleJsonError(int $errno): void 514 | { 515 | $messages = [ 516 | JSON_ERROR_DEPTH => 'Maximum stack depth exceeded', 517 | JSON_ERROR_STATE_MISMATCH => 'Invalid or malformed JSON', 518 | JSON_ERROR_CTRL_CHAR => 'Unexpected control character found', 519 | JSON_ERROR_SYNTAX => 'Syntax error, malformed JSON', 520 | JSON_ERROR_UTF8 => 'Malformed UTF-8 characters' //PHP >= 5.3.3 521 | ]; 522 | throw new DomainException( 523 | isset($messages[$errno]) 524 | ? $messages[$errno] 525 | : 'Unknown JSON error: ' . $errno 526 | ); 527 | } 528 | 529 | /** 530 | * Get the number of bytes in cryptographic strings. 531 | * 532 | * @param string $str 533 | * 534 | * @return int 535 | */ 536 | private static function safeStrlen(string $str): int 537 | { 538 | if (\function_exists('mb_strlen')) { 539 | return \mb_strlen($str, '8bit'); 540 | } 541 | return \strlen($str); 542 | } 543 | 544 | /** 545 | * Convert an ECDSA signature to an ASN.1 DER sequence 546 | * 547 | * @param string $sig The ECDSA signature to convert 548 | * @return string The encoded DER object 549 | */ 550 | private static function signatureToDER(string $sig): string 551 | { 552 | // Separate the signature into r-value and s-value 553 | $length = max(1, (int) (\strlen($sig) / 2)); 554 | list($r, $s) = \str_split($sig, $length); 555 | 556 | // Trim leading zeros 557 | $r = \ltrim($r, "\x00"); 558 | $s = \ltrim($s, "\x00"); 559 | 560 | // Convert r-value and s-value from unsigned big-endian integers to 561 | // signed two's complement 562 | if (\ord($r[0]) > 0x7f) { 563 | $r = "\x00" . $r; 564 | } 565 | if (\ord($s[0]) > 0x7f) { 566 | $s = "\x00" . $s; 567 | } 568 | 569 | return self::encodeDER( 570 | self::ASN1_SEQUENCE, 571 | self::encodeDER(self::ASN1_INTEGER, $r) . 572 | self::encodeDER(self::ASN1_INTEGER, $s) 573 | ); 574 | } 575 | 576 | /** 577 | * Encodes a value into a DER object. 578 | * 579 | * @param int $type DER tag 580 | * @param string $value the value to encode 581 | * 582 | * @return string the encoded object 583 | */ 584 | private static function encodeDER(int $type, string $value): string 585 | { 586 | $tag_header = 0; 587 | if ($type === self::ASN1_SEQUENCE) { 588 | $tag_header |= 0x20; 589 | } 590 | 591 | // Type 592 | $der = \chr($tag_header | $type); 593 | 594 | // Length 595 | $der .= \chr(\strlen($value)); 596 | 597 | return $der . $value; 598 | } 599 | 600 | /** 601 | * Encodes signature from a DER object. 602 | * 603 | * @param string $der binary signature in DER format 604 | * @param int $keySize the number of bits in the key 605 | * 606 | * @return string the signature 607 | */ 608 | private static function signatureFromDER(string $der, int $keySize): string 609 | { 610 | // OpenSSL returns the ECDSA signatures as a binary ASN.1 DER SEQUENCE 611 | list($offset, $_) = self::readDER($der); 612 | list($offset, $r) = self::readDER($der, $offset); 613 | list($offset, $s) = self::readDER($der, $offset); 614 | 615 | // Convert r-value and s-value from signed two's compliment to unsigned 616 | // big-endian integers 617 | $r = \ltrim($r, "\x00"); 618 | $s = \ltrim($s, "\x00"); 619 | 620 | // Pad out r and s so that they are $keySize bits long 621 | $r = \str_pad($r, $keySize / 8, "\x00", STR_PAD_LEFT); 622 | $s = \str_pad($s, $keySize / 8, "\x00", STR_PAD_LEFT); 623 | 624 | return $r . $s; 625 | } 626 | 627 | /** 628 | * Reads binary DER-encoded data and decodes into a single object 629 | * 630 | * @param string $der the binary data in DER format 631 | * @param int $offset the offset of the data stream containing the object 632 | * to decode 633 | * 634 | * @return array{int, string|null} the new offset and the decoded object 635 | */ 636 | private static function readDER(string $der, int $offset = 0): array 637 | { 638 | $pos = $offset; 639 | $size = \strlen($der); 640 | $constructed = (\ord($der[$pos]) >> 5) & 0x01; 641 | $type = \ord($der[$pos++]) & 0x1f; 642 | 643 | // Length 644 | $len = \ord($der[$pos++]); 645 | if ($len & 0x80) { 646 | $n = $len & 0x1f; 647 | $len = 0; 648 | while ($n-- && $pos < $size) { 649 | $len = ($len << 8) | \ord($der[$pos++]); 650 | } 651 | } 652 | 653 | // Value 654 | if ($type === self::ASN1_BIT_STRING) { 655 | $pos++; // Skip the first contents octet (padding indicator) 656 | $data = \substr($der, $pos, $len - 1); 657 | $pos += $len - 1; 658 | } elseif (!$constructed) { 659 | $data = \substr($der, $pos, $len); 660 | $pos += $len; 661 | } else { 662 | $data = null; 663 | } 664 | 665 | return [$pos, $data]; 666 | } 667 | } 668 | -------------------------------------------------------------------------------- /firebase-php-jwt-d1e91ec/src/JWTExceptionWithPayloadInterface.php: -------------------------------------------------------------------------------- 1 | algorithm; 46 | } 47 | 48 | /** 49 | * @return string|resource|OpenSSLAsymmetricKey|OpenSSLCertificate 50 | */ 51 | public function getKeyMaterial() 52 | { 53 | return $this->keyMaterial; 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /firebase-php-jwt-d1e91ec/src/SignatureInvalidException.php: -------------------------------------------------------------------------------- 1 | prepare("SELECT id, title, content FROM services"); 10 | $stmt->execute(); 11 | $result = $stmt->get_result(); 12 | 13 | $all_service = []; 14 | while ($row = $result->fetch_assoc()) { 15 | $all_service[] = $row; 16 | } 17 | echo json_encode($all_service); 18 | 19 | $stmt->close(); 20 | $conn->close(); 21 | exit(); 22 | } 23 | 24 | if ($method === 'POST') { 25 | $rawData = file_get_contents('php://input'); 26 | $data = json_decode($rawData, true); 27 | 28 | if (!isset($data['title']) || !isset($data['content'])) { 29 | echo json_encode(["status" => "error", "message" => "Title and Content are required"]); 30 | exit(); 31 | } 32 | 33 | $title = $data['title']; 34 | $content = $data['content']; 35 | 36 | $stmt = $conn->prepare("SELECT id FROM services WHERE title = ? AND content = ?"); 37 | $stmt->bind_param("ss", $title, $content); 38 | $stmt->execute(); 39 | $stmt->store_result(); 40 | 41 | if ($stmt->num_rows > 0) { 42 | echo json_encode(["status" => "error", "message" => "This service already exists."]); 43 | $stmt->close(); 44 | $conn->close(); 45 | exit(); 46 | } 47 | $stmt->close(); 48 | 49 | $stmt = $conn->prepare("INSERT INTO services (title, content) VALUES (?, ?)"); 50 | $stmt->bind_param("ss", $title, $content); 51 | 52 | if ($stmt->execute()) { 53 | echo json_encode([ 54 | "status" => "success", 55 | "message" => "Service added successfully", 56 | "data" => [ 57 | "id" => $stmt->insert_id, 58 | "title" => $title, 59 | "content" => $content, 60 | ] 61 | ]); 62 | } else { 63 | echo json_encode(["status" => "error", "message" => "Failed to add service"]); 64 | } 65 | $stmt->close(); 66 | $conn->close(); 67 | exit(); 68 | } 69 | 70 | if ($method === 'PUT') { 71 | // Update or Delete service 72 | $rawData = file_get_contents("php://input"); 73 | $data = json_decode($rawData, true); 74 | 75 | $id = $_PUT['id'] ?? null; 76 | $title = $_PUT['title'] ?? null; 77 | $content = $_PUT['content'] ?? null; 78 | 79 | if (!$id) { 80 | echo json_encode(["status" => "error", "message" => "ID is required"]); 81 | exit(); 82 | } 83 | 84 | if ($title === null && $content === null) { 85 | // DELETE 86 | $stmt = $conn->prepare("DELETE FROM services WHERE id = ?"); 87 | $stmt->bind_param('i', $id); 88 | $stmt->execute(); 89 | 90 | if ($stmt->affected_rows > 0) { 91 | echo json_encode(["status" => "success", "message" => "Service deleted successfully"]); 92 | } else { 93 | echo json_encode(["status" => "error", "message" => "No service found with this ID"]); 94 | } 95 | 96 | $stmt->close(); 97 | $conn->close(); 98 | exit(); 99 | } else{ 100 | // UPDATE 101 | $stmt = $conn->prepare("UPDATE services SET title = ?, content = ? WHERE id = ?"); 102 | $stmt->bind_param("ssi", $title, $content, $id); 103 | $stmt->execute(); 104 | 105 | if ($stmt->affected_rows > 0) { 106 | echo json_encode(["status" => "success", "message" => "Service updated successfully"]); 107 | } else { 108 | echo json_encode(["status" => "error", "message" => "Service not found or no changes made"]); 109 | } 110 | } 111 | 112 | $stmt->close(); 113 | $conn->close(); 114 | exit(); 115 | } 116 | 117 | echo json_encode(["status" => "error", "message" => "Unsupported request method"]); 118 | -------------------------------------------------------------------------------- /users.php: -------------------------------------------------------------------------------- 1 | prepare("SELECT id FROM users WHERE email = ?"); 17 | $stmt->bind_param("s", $email); 18 | $stmt->execute(); 19 | $stmt->store_result(); 20 | 21 | if ($stmt->num_rows > 0) { 22 | // Email is already registered, don't allow signup 23 | echo "Email already registered! Please use a different email."; 24 | } else { 25 | $stmt->close(); 26 | 27 | //Insert user 28 | $stmt = $conn->prepare("INSERT INTO users (email, username, password) VALUES (?, ?, ?)"); 29 | $stmt->bind_param("sss", $email, $username, $password); 30 | 31 | if ($stmt->execute()) { 32 | echo json_encode(["status" => "success", "message" => "Signup successful!"]); 33 | } else { 34 | echo "Signup error: " . $stmt->error; 35 | } 36 | $stmt->close(); 37 | } 38 | } 39 | 40 | $rawData = file_get_contents('php://input'); 41 | $data = json_decode($rawData, true); 42 | 43 | if (isset($data['action']) && $data['action'] === 'login') { 44 | $email = $data['email'] ?? ''; 45 | $password = $data['password'] ?? ''; 46 | 47 | if (empty($email) || empty($password)) { 48 | http_response_code(400); 49 | echo json_encode([ 50 | "status" => "error", 51 | "message" => "Email and password are required." 52 | ]); 53 | exit(); 54 | } 55 | 56 | // Fetch user 57 | $stmt = $conn->prepare("SELECT id, password FROM users WHERE email = ?"); 58 | $stmt->bind_param("s", $email); 59 | $stmt->execute(); 60 | $stmt->store_result(); 61 | 62 | if ($stmt->num_rows > 0) { 63 | $stmt->bind_result($userId, $hashedPassword); 64 | $stmt->fetch(); 65 | 66 | if (password_verify($password, $hashedPassword)) { 67 | // $_SESSION['user_id'] = $userId; 68 | $key = "secret_key"; 69 | $payload = [ 70 | "iss" => "yourname", 71 | "aud" => "", 72 | "iat" => time(), 73 | "exp" => time() + (60 * 60), 74 | "data" => [ 75 | "password" => $password, 76 | "email" => $email 77 | ], 78 | ]; 79 | $jwt = JWT::encode($payload, $key, 'HS256'); 80 | 81 | echo json_encode([ 82 | "status" => "success", 83 | "message" => "Login successful.", 84 | "data" => [ 85 | "userID" => $userId, 86 | "email" => $email, 87 | "token" => $jwt 88 | ] 89 | ]); 90 | exit(); 91 | } else { 92 | http_response_code(404); 93 | echo json_encode([ 94 | "status" => "error", 95 | "message" => "User not found." 96 | ]); 97 | exit(); 98 | } 99 | } 100 | $stmt->close(); 101 | } 102 | 103 | $conn->close(); 104 | 105 | ?> -------------------------------------------------------------------------------- /vendor/autoload.php: -------------------------------------------------------------------------------- 1 | 7 | * Jordi Boggiano 8 | * 9 | * For the full copyright and license information, please view the LICENSE 10 | * file that was distributed with this source code. 11 | */ 12 | 13 | namespace Composer\Autoload; 14 | 15 | /** 16 | * ClassLoader implements a PSR-0, PSR-4 and classmap class loader. 17 | * 18 | * $loader = new \Composer\Autoload\ClassLoader(); 19 | * 20 | * // register classes with namespaces 21 | * $loader->add('Symfony\Component', __DIR__.'/component'); 22 | * $loader->add('Symfony', __DIR__.'/framework'); 23 | * 24 | * // activate the autoloader 25 | * $loader->register(); 26 | * 27 | * // to enable searching the include path (eg. for PEAR packages) 28 | * $loader->setUseIncludePath(true); 29 | * 30 | * In this example, if you try to use a class in the Symfony\Component 31 | * namespace or one of its children (Symfony\Component\Console for instance), 32 | * the autoloader will first look for the class under the component/ 33 | * directory, and it will then fallback to the framework/ directory if not 34 | * found before giving up. 35 | * 36 | * This class is loosely based on the Symfony UniversalClassLoader. 37 | * 38 | * @author Fabien Potencier 39 | * @author Jordi Boggiano 40 | * @see https://www.php-fig.org/psr/psr-0/ 41 | * @see https://www.php-fig.org/psr/psr-4/ 42 | */ 43 | class ClassLoader 44 | { 45 | /** @var \Closure(string):void */ 46 | private static $includeFile; 47 | 48 | /** @var string|null */ 49 | private $vendorDir; 50 | 51 | // PSR-4 52 | /** 53 | * @var array> 54 | */ 55 | private $prefixLengthsPsr4 = array(); 56 | /** 57 | * @var array> 58 | */ 59 | private $prefixDirsPsr4 = array(); 60 | /** 61 | * @var list 62 | */ 63 | private $fallbackDirsPsr4 = array(); 64 | 65 | // PSR-0 66 | /** 67 | * List of PSR-0 prefixes 68 | * 69 | * Structured as array('F (first letter)' => array('Foo\Bar (full prefix)' => array('path', 'path2'))) 70 | * 71 | * @var array>> 72 | */ 73 | private $prefixesPsr0 = array(); 74 | /** 75 | * @var list 76 | */ 77 | private $fallbackDirsPsr0 = array(); 78 | 79 | /** @var bool */ 80 | private $useIncludePath = false; 81 | 82 | /** 83 | * @var array 84 | */ 85 | private $classMap = array(); 86 | 87 | /** @var bool */ 88 | private $classMapAuthoritative = false; 89 | 90 | /** 91 | * @var array 92 | */ 93 | private $missingClasses = array(); 94 | 95 | /** @var string|null */ 96 | private $apcuPrefix; 97 | 98 | /** 99 | * @var array 100 | */ 101 | private static $registeredLoaders = array(); 102 | 103 | /** 104 | * @param string|null $vendorDir 105 | */ 106 | public function __construct($vendorDir = null) 107 | { 108 | $this->vendorDir = $vendorDir; 109 | self::initializeIncludeClosure(); 110 | } 111 | 112 | /** 113 | * @return array> 114 | */ 115 | public function getPrefixes() 116 | { 117 | if (!empty($this->prefixesPsr0)) { 118 | return call_user_func_array('array_merge', array_values($this->prefixesPsr0)); 119 | } 120 | 121 | return array(); 122 | } 123 | 124 | /** 125 | * @return array> 126 | */ 127 | public function getPrefixesPsr4() 128 | { 129 | return $this->prefixDirsPsr4; 130 | } 131 | 132 | /** 133 | * @return list 134 | */ 135 | public function getFallbackDirs() 136 | { 137 | return $this->fallbackDirsPsr0; 138 | } 139 | 140 | /** 141 | * @return list 142 | */ 143 | public function getFallbackDirsPsr4() 144 | { 145 | return $this->fallbackDirsPsr4; 146 | } 147 | 148 | /** 149 | * @return array Array of classname => path 150 | */ 151 | public function getClassMap() 152 | { 153 | return $this->classMap; 154 | } 155 | 156 | /** 157 | * @param array $classMap Class to filename map 158 | * 159 | * @return void 160 | */ 161 | public function addClassMap(array $classMap) 162 | { 163 | if ($this->classMap) { 164 | $this->classMap = array_merge($this->classMap, $classMap); 165 | } else { 166 | $this->classMap = $classMap; 167 | } 168 | } 169 | 170 | /** 171 | * Registers a set of PSR-0 directories for a given prefix, either 172 | * appending or prepending to the ones previously set for this prefix. 173 | * 174 | * @param string $prefix The prefix 175 | * @param list|string $paths The PSR-0 root directories 176 | * @param bool $prepend Whether to prepend the directories 177 | * 178 | * @return void 179 | */ 180 | public function add($prefix, $paths, $prepend = false) 181 | { 182 | $paths = (array) $paths; 183 | if (!$prefix) { 184 | if ($prepend) { 185 | $this->fallbackDirsPsr0 = array_merge( 186 | $paths, 187 | $this->fallbackDirsPsr0 188 | ); 189 | } else { 190 | $this->fallbackDirsPsr0 = array_merge( 191 | $this->fallbackDirsPsr0, 192 | $paths 193 | ); 194 | } 195 | 196 | return; 197 | } 198 | 199 | $first = $prefix[0]; 200 | if (!isset($this->prefixesPsr0[$first][$prefix])) { 201 | $this->prefixesPsr0[$first][$prefix] = $paths; 202 | 203 | return; 204 | } 205 | if ($prepend) { 206 | $this->prefixesPsr0[$first][$prefix] = array_merge( 207 | $paths, 208 | $this->prefixesPsr0[$first][$prefix] 209 | ); 210 | } else { 211 | $this->prefixesPsr0[$first][$prefix] = array_merge( 212 | $this->prefixesPsr0[$first][$prefix], 213 | $paths 214 | ); 215 | } 216 | } 217 | 218 | /** 219 | * Registers a set of PSR-4 directories for a given namespace, either 220 | * appending or prepending to the ones previously set for this namespace. 221 | * 222 | * @param string $prefix The prefix/namespace, with trailing '\\' 223 | * @param list|string $paths The PSR-4 base directories 224 | * @param bool $prepend Whether to prepend the directories 225 | * 226 | * @throws \InvalidArgumentException 227 | * 228 | * @return void 229 | */ 230 | public function addPsr4($prefix, $paths, $prepend = false) 231 | { 232 | $paths = (array) $paths; 233 | if (!$prefix) { 234 | // Register directories for the root namespace. 235 | if ($prepend) { 236 | $this->fallbackDirsPsr4 = array_merge( 237 | $paths, 238 | $this->fallbackDirsPsr4 239 | ); 240 | } else { 241 | $this->fallbackDirsPsr4 = array_merge( 242 | $this->fallbackDirsPsr4, 243 | $paths 244 | ); 245 | } 246 | } elseif (!isset($this->prefixDirsPsr4[$prefix])) { 247 | // Register directories for a new namespace. 248 | $length = strlen($prefix); 249 | if ('\\' !== $prefix[$length - 1]) { 250 | throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator."); 251 | } 252 | $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length; 253 | $this->prefixDirsPsr4[$prefix] = $paths; 254 | } elseif ($prepend) { 255 | // Prepend directories for an already registered namespace. 256 | $this->prefixDirsPsr4[$prefix] = array_merge( 257 | $paths, 258 | $this->prefixDirsPsr4[$prefix] 259 | ); 260 | } else { 261 | // Append directories for an already registered namespace. 262 | $this->prefixDirsPsr4[$prefix] = array_merge( 263 | $this->prefixDirsPsr4[$prefix], 264 | $paths 265 | ); 266 | } 267 | } 268 | 269 | /** 270 | * Registers a set of PSR-0 directories for a given prefix, 271 | * replacing any others previously set for this prefix. 272 | * 273 | * @param string $prefix The prefix 274 | * @param list|string $paths The PSR-0 base directories 275 | * 276 | * @return void 277 | */ 278 | public function set($prefix, $paths) 279 | { 280 | if (!$prefix) { 281 | $this->fallbackDirsPsr0 = (array) $paths; 282 | } else { 283 | $this->prefixesPsr0[$prefix[0]][$prefix] = (array) $paths; 284 | } 285 | } 286 | 287 | /** 288 | * Registers a set of PSR-4 directories for a given namespace, 289 | * replacing any others previously set for this namespace. 290 | * 291 | * @param string $prefix The prefix/namespace, with trailing '\\' 292 | * @param list|string $paths The PSR-4 base directories 293 | * 294 | * @throws \InvalidArgumentException 295 | * 296 | * @return void 297 | */ 298 | public function setPsr4($prefix, $paths) 299 | { 300 | if (!$prefix) { 301 | $this->fallbackDirsPsr4 = (array) $paths; 302 | } else { 303 | $length = strlen($prefix); 304 | if ('\\' !== $prefix[$length - 1]) { 305 | throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator."); 306 | } 307 | $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length; 308 | $this->prefixDirsPsr4[$prefix] = (array) $paths; 309 | } 310 | } 311 | 312 | /** 313 | * Turns on searching the include path for class files. 314 | * 315 | * @param bool $useIncludePath 316 | * 317 | * @return void 318 | */ 319 | public function setUseIncludePath($useIncludePath) 320 | { 321 | $this->useIncludePath = $useIncludePath; 322 | } 323 | 324 | /** 325 | * Can be used to check if the autoloader uses the include path to check 326 | * for classes. 327 | * 328 | * @return bool 329 | */ 330 | public function getUseIncludePath() 331 | { 332 | return $this->useIncludePath; 333 | } 334 | 335 | /** 336 | * Turns off searching the prefix and fallback directories for classes 337 | * that have not been registered with the class map. 338 | * 339 | * @param bool $classMapAuthoritative 340 | * 341 | * @return void 342 | */ 343 | public function setClassMapAuthoritative($classMapAuthoritative) 344 | { 345 | $this->classMapAuthoritative = $classMapAuthoritative; 346 | } 347 | 348 | /** 349 | * Should class lookup fail if not found in the current class map? 350 | * 351 | * @return bool 352 | */ 353 | public function isClassMapAuthoritative() 354 | { 355 | return $this->classMapAuthoritative; 356 | } 357 | 358 | /** 359 | * APCu prefix to use to cache found/not-found classes, if the extension is enabled. 360 | * 361 | * @param string|null $apcuPrefix 362 | * 363 | * @return void 364 | */ 365 | public function setApcuPrefix($apcuPrefix) 366 | { 367 | $this->apcuPrefix = function_exists('apcu_fetch') && filter_var(ini_get('apc.enabled'), FILTER_VALIDATE_BOOLEAN) ? $apcuPrefix : null; 368 | } 369 | 370 | /** 371 | * The APCu prefix in use, or null if APCu caching is not enabled. 372 | * 373 | * @return string|null 374 | */ 375 | public function getApcuPrefix() 376 | { 377 | return $this->apcuPrefix; 378 | } 379 | 380 | /** 381 | * Registers this instance as an autoloader. 382 | * 383 | * @param bool $prepend Whether to prepend the autoloader or not 384 | * 385 | * @return void 386 | */ 387 | public function register($prepend = false) 388 | { 389 | spl_autoload_register(array($this, 'loadClass'), true, $prepend); 390 | 391 | if (null === $this->vendorDir) { 392 | return; 393 | } 394 | 395 | if ($prepend) { 396 | self::$registeredLoaders = array($this->vendorDir => $this) + self::$registeredLoaders; 397 | } else { 398 | unset(self::$registeredLoaders[$this->vendorDir]); 399 | self::$registeredLoaders[$this->vendorDir] = $this; 400 | } 401 | } 402 | 403 | /** 404 | * Unregisters this instance as an autoloader. 405 | * 406 | * @return void 407 | */ 408 | public function unregister() 409 | { 410 | spl_autoload_unregister(array($this, 'loadClass')); 411 | 412 | if (null !== $this->vendorDir) { 413 | unset(self::$registeredLoaders[$this->vendorDir]); 414 | } 415 | } 416 | 417 | /** 418 | * Loads the given class or interface. 419 | * 420 | * @param string $class The name of the class 421 | * @return true|null True if loaded, null otherwise 422 | */ 423 | public function loadClass($class) 424 | { 425 | if ($file = $this->findFile($class)) { 426 | $includeFile = self::$includeFile; 427 | $includeFile($file); 428 | 429 | return true; 430 | } 431 | 432 | return null; 433 | } 434 | 435 | /** 436 | * Finds the path to the file where the class is defined. 437 | * 438 | * @param string $class The name of the class 439 | * 440 | * @return string|false The path if found, false otherwise 441 | */ 442 | public function findFile($class) 443 | { 444 | // class map lookup 445 | if (isset($this->classMap[$class])) { 446 | return $this->classMap[$class]; 447 | } 448 | if ($this->classMapAuthoritative || isset($this->missingClasses[$class])) { 449 | return false; 450 | } 451 | if (null !== $this->apcuPrefix) { 452 | $file = apcu_fetch($this->apcuPrefix.$class, $hit); 453 | if ($hit) { 454 | return $file; 455 | } 456 | } 457 | 458 | $file = $this->findFileWithExtension($class, '.php'); 459 | 460 | // Search for Hack files if we are running on HHVM 461 | if (false === $file && defined('HHVM_VERSION')) { 462 | $file = $this->findFileWithExtension($class, '.hh'); 463 | } 464 | 465 | if (null !== $this->apcuPrefix) { 466 | apcu_add($this->apcuPrefix.$class, $file); 467 | } 468 | 469 | if (false === $file) { 470 | // Remember that this class does not exist. 471 | $this->missingClasses[$class] = true; 472 | } 473 | 474 | return $file; 475 | } 476 | 477 | /** 478 | * Returns the currently registered loaders keyed by their corresponding vendor directories. 479 | * 480 | * @return array 481 | */ 482 | public static function getRegisteredLoaders() 483 | { 484 | return self::$registeredLoaders; 485 | } 486 | 487 | /** 488 | * @param string $class 489 | * @param string $ext 490 | * @return string|false 491 | */ 492 | private function findFileWithExtension($class, $ext) 493 | { 494 | // PSR-4 lookup 495 | $logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext; 496 | 497 | $first = $class[0]; 498 | if (isset($this->prefixLengthsPsr4[$first])) { 499 | $subPath = $class; 500 | while (false !== $lastPos = strrpos($subPath, '\\')) { 501 | $subPath = substr($subPath, 0, $lastPos); 502 | $search = $subPath . '\\'; 503 | if (isset($this->prefixDirsPsr4[$search])) { 504 | $pathEnd = DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $lastPos + 1); 505 | foreach ($this->prefixDirsPsr4[$search] as $dir) { 506 | if (file_exists($file = $dir . $pathEnd)) { 507 | return $file; 508 | } 509 | } 510 | } 511 | } 512 | } 513 | 514 | // PSR-4 fallback dirs 515 | foreach ($this->fallbackDirsPsr4 as $dir) { 516 | if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) { 517 | return $file; 518 | } 519 | } 520 | 521 | // PSR-0 lookup 522 | if (false !== $pos = strrpos($class, '\\')) { 523 | // namespaced class name 524 | $logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1) 525 | . strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR); 526 | } else { 527 | // PEAR-like class name 528 | $logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . $ext; 529 | } 530 | 531 | if (isset($this->prefixesPsr0[$first])) { 532 | foreach ($this->prefixesPsr0[$first] as $prefix => $dirs) { 533 | if (0 === strpos($class, $prefix)) { 534 | foreach ($dirs as $dir) { 535 | if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) { 536 | return $file; 537 | } 538 | } 539 | } 540 | } 541 | } 542 | 543 | // PSR-0 fallback dirs 544 | foreach ($this->fallbackDirsPsr0 as $dir) { 545 | if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) { 546 | return $file; 547 | } 548 | } 549 | 550 | // PSR-0 include paths. 551 | if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) { 552 | return $file; 553 | } 554 | 555 | return false; 556 | } 557 | 558 | /** 559 | * @return void 560 | */ 561 | private static function initializeIncludeClosure() 562 | { 563 | if (self::$includeFile !== null) { 564 | return; 565 | } 566 | 567 | /** 568 | * Scope isolated include. 569 | * 570 | * Prevents access to $this/self from included files. 571 | * 572 | * @param string $file 573 | * @return void 574 | */ 575 | self::$includeFile = \Closure::bind(static function($file) { 576 | include $file; 577 | }, null, null); 578 | } 579 | } 580 | -------------------------------------------------------------------------------- /vendor/composer/InstalledVersions.php: -------------------------------------------------------------------------------- 1 | 7 | * Jordi Boggiano 8 | * 9 | * For the full copyright and license information, please view the LICENSE 10 | * file that was distributed with this source code. 11 | */ 12 | 13 | namespace Composer; 14 | 15 | use Composer\Autoload\ClassLoader; 16 | use Composer\Semver\VersionParser; 17 | 18 | /** 19 | * This class is copied in every Composer installed project and available to all 20 | * 21 | * See also https://getcomposer.org/doc/07-runtime.md#installed-versions 22 | * 23 | * To require its presence, you can require `composer-runtime-api ^2.0` 24 | * 25 | * @final 26 | */ 27 | class InstalledVersions 28 | { 29 | /** 30 | * @var string|null if set (by reflection by Composer), this should be set to the path where this class is being copied to 31 | * @internal 32 | */ 33 | private static $selfDir = null; 34 | 35 | /** 36 | * @var mixed[]|null 37 | * @psalm-var array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array}|array{}|null 38 | */ 39 | private static $installed; 40 | 41 | /** 42 | * @var bool 43 | */ 44 | private static $installedIsLocalDir; 45 | 46 | /** 47 | * @var bool|null 48 | */ 49 | private static $canGetVendors; 50 | 51 | /** 52 | * @var array[] 53 | * @psalm-var array}> 54 | */ 55 | private static $installedByVendor = array(); 56 | 57 | /** 58 | * Returns a list of all package names which are present, either by being installed, replaced or provided 59 | * 60 | * @return string[] 61 | * @psalm-return list 62 | */ 63 | public static function getInstalledPackages() 64 | { 65 | $packages = array(); 66 | foreach (self::getInstalled() as $installed) { 67 | $packages[] = array_keys($installed['versions']); 68 | } 69 | 70 | if (1 === \count($packages)) { 71 | return $packages[0]; 72 | } 73 | 74 | return array_keys(array_flip(\call_user_func_array('array_merge', $packages))); 75 | } 76 | 77 | /** 78 | * Returns a list of all package names with a specific type e.g. 'library' 79 | * 80 | * @param string $type 81 | * @return string[] 82 | * @psalm-return list 83 | */ 84 | public static function getInstalledPackagesByType($type) 85 | { 86 | $packagesByType = array(); 87 | 88 | foreach (self::getInstalled() as $installed) { 89 | foreach ($installed['versions'] as $name => $package) { 90 | if (isset($package['type']) && $package['type'] === $type) { 91 | $packagesByType[] = $name; 92 | } 93 | } 94 | } 95 | 96 | return $packagesByType; 97 | } 98 | 99 | /** 100 | * Checks whether the given package is installed 101 | * 102 | * This also returns true if the package name is provided or replaced by another package 103 | * 104 | * @param string $packageName 105 | * @param bool $includeDevRequirements 106 | * @return bool 107 | */ 108 | public static function isInstalled($packageName, $includeDevRequirements = true) 109 | { 110 | foreach (self::getInstalled() as $installed) { 111 | if (isset($installed['versions'][$packageName])) { 112 | return $includeDevRequirements || !isset($installed['versions'][$packageName]['dev_requirement']) || $installed['versions'][$packageName]['dev_requirement'] === false; 113 | } 114 | } 115 | 116 | return false; 117 | } 118 | 119 | /** 120 | * Checks whether the given package satisfies a version constraint 121 | * 122 | * e.g. If you want to know whether version 2.3+ of package foo/bar is installed, you would call: 123 | * 124 | * Composer\InstalledVersions::satisfies(new VersionParser, 'foo/bar', '^2.3') 125 | * 126 | * @param VersionParser $parser Install composer/semver to have access to this class and functionality 127 | * @param string $packageName 128 | * @param string|null $constraint A version constraint to check for, if you pass one you have to make sure composer/semver is required by your package 129 | * @return bool 130 | */ 131 | public static function satisfies(VersionParser $parser, $packageName, $constraint) 132 | { 133 | $constraint = $parser->parseConstraints((string) $constraint); 134 | $provided = $parser->parseConstraints(self::getVersionRanges($packageName)); 135 | 136 | return $provided->matches($constraint); 137 | } 138 | 139 | /** 140 | * Returns a version constraint representing all the range(s) which are installed for a given package 141 | * 142 | * It is easier to use this via isInstalled() with the $constraint argument if you need to check 143 | * whether a given version of a package is installed, and not just whether it exists 144 | * 145 | * @param string $packageName 146 | * @return string Version constraint usable with composer/semver 147 | */ 148 | public static function getVersionRanges($packageName) 149 | { 150 | foreach (self::getInstalled() as $installed) { 151 | if (!isset($installed['versions'][$packageName])) { 152 | continue; 153 | } 154 | 155 | $ranges = array(); 156 | if (isset($installed['versions'][$packageName]['pretty_version'])) { 157 | $ranges[] = $installed['versions'][$packageName]['pretty_version']; 158 | } 159 | if (array_key_exists('aliases', $installed['versions'][$packageName])) { 160 | $ranges = array_merge($ranges, $installed['versions'][$packageName]['aliases']); 161 | } 162 | if (array_key_exists('replaced', $installed['versions'][$packageName])) { 163 | $ranges = array_merge($ranges, $installed['versions'][$packageName]['replaced']); 164 | } 165 | if (array_key_exists('provided', $installed['versions'][$packageName])) { 166 | $ranges = array_merge($ranges, $installed['versions'][$packageName]['provided']); 167 | } 168 | 169 | return implode(' || ', $ranges); 170 | } 171 | 172 | throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed'); 173 | } 174 | 175 | /** 176 | * @param string $packageName 177 | * @return string|null If the package is being replaced or provided but is not really installed, null will be returned as version, use satisfies or getVersionRanges if you need to know if a given version is present 178 | */ 179 | public static function getVersion($packageName) 180 | { 181 | foreach (self::getInstalled() as $installed) { 182 | if (!isset($installed['versions'][$packageName])) { 183 | continue; 184 | } 185 | 186 | if (!isset($installed['versions'][$packageName]['version'])) { 187 | return null; 188 | } 189 | 190 | return $installed['versions'][$packageName]['version']; 191 | } 192 | 193 | throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed'); 194 | } 195 | 196 | /** 197 | * @param string $packageName 198 | * @return string|null If the package is being replaced or provided but is not really installed, null will be returned as version, use satisfies or getVersionRanges if you need to know if a given version is present 199 | */ 200 | public static function getPrettyVersion($packageName) 201 | { 202 | foreach (self::getInstalled() as $installed) { 203 | if (!isset($installed['versions'][$packageName])) { 204 | continue; 205 | } 206 | 207 | if (!isset($installed['versions'][$packageName]['pretty_version'])) { 208 | return null; 209 | } 210 | 211 | return $installed['versions'][$packageName]['pretty_version']; 212 | } 213 | 214 | throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed'); 215 | } 216 | 217 | /** 218 | * @param string $packageName 219 | * @return string|null If the package is being replaced or provided but is not really installed, null will be returned as reference 220 | */ 221 | public static function getReference($packageName) 222 | { 223 | foreach (self::getInstalled() as $installed) { 224 | if (!isset($installed['versions'][$packageName])) { 225 | continue; 226 | } 227 | 228 | if (!isset($installed['versions'][$packageName]['reference'])) { 229 | return null; 230 | } 231 | 232 | return $installed['versions'][$packageName]['reference']; 233 | } 234 | 235 | throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed'); 236 | } 237 | 238 | /** 239 | * @param string $packageName 240 | * @return string|null If the package is being replaced or provided but is not really installed, null will be returned as install path. Packages of type metapackages also have a null install path. 241 | */ 242 | public static function getInstallPath($packageName) 243 | { 244 | foreach (self::getInstalled() as $installed) { 245 | if (!isset($installed['versions'][$packageName])) { 246 | continue; 247 | } 248 | 249 | return isset($installed['versions'][$packageName]['install_path']) ? $installed['versions'][$packageName]['install_path'] : null; 250 | } 251 | 252 | throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed'); 253 | } 254 | 255 | /** 256 | * @return array 257 | * @psalm-return array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool} 258 | */ 259 | public static function getRootPackage() 260 | { 261 | $installed = self::getInstalled(); 262 | 263 | return $installed[0]['root']; 264 | } 265 | 266 | /** 267 | * Returns the raw installed.php data for custom implementations 268 | * 269 | * @deprecated Use getAllRawData() instead which returns all datasets for all autoloaders present in the process. getRawData only returns the first dataset loaded, which may not be what you expect. 270 | * @return array[] 271 | * @psalm-return array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array} 272 | */ 273 | public static function getRawData() 274 | { 275 | @trigger_error('getRawData only returns the first dataset loaded, which may not be what you expect. Use getAllRawData() instead which returns all datasets for all autoloaders present in the process.', E_USER_DEPRECATED); 276 | 277 | if (null === self::$installed) { 278 | // only require the installed.php file if this file is loaded from its dumped location, 279 | // and not from its source location in the composer/composer package, see https://github.com/composer/composer/issues/9937 280 | if (substr(__DIR__, -8, 1) !== 'C') { 281 | self::$installed = include __DIR__ . '/installed.php'; 282 | } else { 283 | self::$installed = array(); 284 | } 285 | } 286 | 287 | return self::$installed; 288 | } 289 | 290 | /** 291 | * Returns the raw data of all installed.php which are currently loaded for custom implementations 292 | * 293 | * @return array[] 294 | * @psalm-return list}> 295 | */ 296 | public static function getAllRawData() 297 | { 298 | return self::getInstalled(); 299 | } 300 | 301 | /** 302 | * Lets you reload the static array from another file 303 | * 304 | * This is only useful for complex integrations in which a project needs to use 305 | * this class but then also needs to execute another project's autoloader in process, 306 | * and wants to ensure both projects have access to their version of installed.php. 307 | * 308 | * A typical case would be PHPUnit, where it would need to make sure it reads all 309 | * the data it needs from this class, then call reload() with 310 | * `require $CWD/vendor/composer/installed.php` (or similar) as input to make sure 311 | * the project in which it runs can then also use this class safely, without 312 | * interference between PHPUnit's dependencies and the project's dependencies. 313 | * 314 | * @param array[] $data A vendor/composer/installed.php data set 315 | * @return void 316 | * 317 | * @psalm-param array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array} $data 318 | */ 319 | public static function reload($data) 320 | { 321 | self::$installed = $data; 322 | self::$installedByVendor = array(); 323 | 324 | // when using reload, we disable the duplicate protection to ensure that self::$installed data is 325 | // always returned, but we cannot know whether it comes from the installed.php in __DIR__ or not, 326 | // so we have to assume it does not, and that may result in duplicate data being returned when listing 327 | // all installed packages for example 328 | self::$installedIsLocalDir = false; 329 | } 330 | 331 | /** 332 | * @return string 333 | */ 334 | private static function getSelfDir() 335 | { 336 | if (self::$selfDir === null) { 337 | self::$selfDir = strtr(__DIR__, '\\', '/'); 338 | } 339 | 340 | return self::$selfDir; 341 | } 342 | 343 | /** 344 | * @return array[] 345 | * @psalm-return list}> 346 | */ 347 | private static function getInstalled() 348 | { 349 | if (null === self::$canGetVendors) { 350 | self::$canGetVendors = method_exists('Composer\Autoload\ClassLoader', 'getRegisteredLoaders'); 351 | } 352 | 353 | $installed = array(); 354 | $copiedLocalDir = false; 355 | 356 | if (self::$canGetVendors) { 357 | $selfDir = self::getSelfDir(); 358 | foreach (ClassLoader::getRegisteredLoaders() as $vendorDir => $loader) { 359 | $vendorDir = strtr($vendorDir, '\\', '/'); 360 | if (isset(self::$installedByVendor[$vendorDir])) { 361 | $installed[] = self::$installedByVendor[$vendorDir]; 362 | } elseif (is_file($vendorDir.'/composer/installed.php')) { 363 | /** @var array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array} $required */ 364 | $required = require $vendorDir.'/composer/installed.php'; 365 | self::$installedByVendor[$vendorDir] = $required; 366 | $installed[] = $required; 367 | if (self::$installed === null && $vendorDir.'/composer' === $selfDir) { 368 | self::$installed = $required; 369 | self::$installedIsLocalDir = true; 370 | } 371 | } 372 | if (self::$installedIsLocalDir && $vendorDir.'/composer' === $selfDir) { 373 | $copiedLocalDir = true; 374 | } 375 | } 376 | } 377 | 378 | if (null === self::$installed) { 379 | // only require the installed.php file if this file is loaded from its dumped location, 380 | // and not from its source location in the composer/composer package, see https://github.com/composer/composer/issues/9937 381 | if (substr(__DIR__, -8, 1) !== 'C') { 382 | /** @var array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array} $required */ 383 | $required = require __DIR__ . '/installed.php'; 384 | self::$installed = $required; 385 | } else { 386 | self::$installed = array(); 387 | } 388 | } 389 | 390 | if (self::$installed !== array() && !$copiedLocalDir) { 391 | $installed[] = self::$installed; 392 | } 393 | 394 | return $installed; 395 | } 396 | } 397 | -------------------------------------------------------------------------------- /vendor/composer/LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Copyright (c) Nils Adermann, Jordi Boggiano 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is furnished 9 | to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all 12 | copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | THE SOFTWARE. 21 | 22 | -------------------------------------------------------------------------------- /vendor/composer/autoload_classmap.php: -------------------------------------------------------------------------------- 1 | $vendorDir . '/composer/InstalledVersions.php', 10 | ); 11 | -------------------------------------------------------------------------------- /vendor/composer/autoload_namespaces.php: -------------------------------------------------------------------------------- 1 | array($vendorDir . '/firebase/php-jwt/src'), 10 | ); 11 | -------------------------------------------------------------------------------- /vendor/composer/autoload_real.php: -------------------------------------------------------------------------------- 1 | register(true); 35 | 36 | return $loader; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /vendor/composer/autoload_static.php: -------------------------------------------------------------------------------- 1 | 11 | array ( 12 | 'Firebase\\JWT\\' => 13, 13 | ), 14 | ); 15 | 16 | public static $prefixDirsPsr4 = array ( 17 | 'Firebase\\JWT\\' => 18 | array ( 19 | 0 => __DIR__ . '/..' . '/firebase/php-jwt/src', 20 | ), 21 | ); 22 | 23 | public static $classMap = array ( 24 | 'Composer\\InstalledVersions' => __DIR__ . '/..' . '/composer/InstalledVersions.php', 25 | ); 26 | 27 | public static function getInitializer(ClassLoader $loader) 28 | { 29 | return \Closure::bind(function () use ($loader) { 30 | $loader->prefixLengthsPsr4 = ComposerStaticInit20fad51902f91e7fd3039e016a6556b5::$prefixLengthsPsr4; 31 | $loader->prefixDirsPsr4 = ComposerStaticInit20fad51902f91e7fd3039e016a6556b5::$prefixDirsPsr4; 32 | $loader->classMap = ComposerStaticInit20fad51902f91e7fd3039e016a6556b5::$classMap; 33 | 34 | }, null, ClassLoader::class); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /vendor/composer/installed.json: -------------------------------------------------------------------------------- 1 | { 2 | "packages": [ 3 | { 4 | "name": "firebase/php-jwt", 5 | "version": "v6.11.1", 6 | "version_normalized": "6.11.1.0", 7 | "source": { 8 | "type": "git", 9 | "url": "https://github.com/firebase/php-jwt.git", 10 | "reference": "d1e91ecf8c598d073d0995afa8cd5c75c6e19e66" 11 | }, 12 | "dist": { 13 | "type": "zip", 14 | "url": "https://api.github.com/repos/firebase/php-jwt/zipball/d1e91ecf8c598d073d0995afa8cd5c75c6e19e66", 15 | "reference": "d1e91ecf8c598d073d0995afa8cd5c75c6e19e66", 16 | "shasum": "" 17 | }, 18 | "require": { 19 | "php": "^8.0" 20 | }, 21 | "require-dev": { 22 | "guzzlehttp/guzzle": "^7.4", 23 | "phpspec/prophecy-phpunit": "^2.0", 24 | "phpunit/phpunit": "^9.5", 25 | "psr/cache": "^2.0||^3.0", 26 | "psr/http-client": "^1.0", 27 | "psr/http-factory": "^1.0" 28 | }, 29 | "suggest": { 30 | "ext-sodium": "Support EdDSA (Ed25519) signatures", 31 | "paragonie/sodium_compat": "Support EdDSA (Ed25519) signatures when libsodium is not present" 32 | }, 33 | "time": "2025-04-09T20:32:01+00:00", 34 | "type": "library", 35 | "installation-source": "source", 36 | "autoload": { 37 | "psr-4": { 38 | "Firebase\\JWT\\": "src" 39 | } 40 | }, 41 | "notification-url": "https://packagist.org/downloads/", 42 | "license": [ 43 | "BSD-3-Clause" 44 | ], 45 | "authors": [ 46 | { 47 | "name": "Neuman Vong", 48 | "email": "neuman+pear@twilio.com", 49 | "role": "Developer" 50 | }, 51 | { 52 | "name": "Anant Narayanan", 53 | "email": "anant@php.net", 54 | "role": "Developer" 55 | } 56 | ], 57 | "description": "A simple library to encode and decode JSON Web Tokens (JWT) in PHP. Should conform to the current spec.", 58 | "homepage": "https://github.com/firebase/php-jwt", 59 | "keywords": [ 60 | "jwt", 61 | "php" 62 | ], 63 | "support": { 64 | "issues": "https://github.com/firebase/php-jwt/issues", 65 | "source": "https://github.com/firebase/php-jwt/tree/v6.11.1" 66 | }, 67 | "install-path": "../firebase/php-jwt" 68 | } 69 | ], 70 | "dev": true, 71 | "dev-package-names": [] 72 | } 73 | -------------------------------------------------------------------------------- /vendor/composer/installed.php: -------------------------------------------------------------------------------- 1 | array( 3 | 'name' => '__root__', 4 | 'pretty_version' => 'dev-main', 5 | 'version' => 'dev-main', 6 | 'reference' => '74dc922cd654cbcc95ebfcab4bbac34140466740', 7 | 'type' => 'library', 8 | 'install_path' => __DIR__ . '/../../', 9 | 'aliases' => array(), 10 | 'dev' => true, 11 | ), 12 | 'versions' => array( 13 | '__root__' => array( 14 | 'pretty_version' => 'dev-main', 15 | 'version' => 'dev-main', 16 | 'reference' => '74dc922cd654cbcc95ebfcab4bbac34140466740', 17 | 'type' => 'library', 18 | 'install_path' => __DIR__ . '/../../', 19 | 'aliases' => array(), 20 | 'dev_requirement' => false, 21 | ), 22 | 'firebase/php-jwt' => array( 23 | 'pretty_version' => 'v6.11.1', 24 | 'version' => '6.11.1.0', 25 | 'reference' => 'd1e91ecf8c598d073d0995afa8cd5c75c6e19e66', 26 | 'type' => 'library', 27 | 'install_path' => __DIR__ . '/../firebase/php-jwt', 28 | 'aliases' => array(), 29 | 'dev_requirement' => false, 30 | ), 31 | ), 32 | ); 33 | -------------------------------------------------------------------------------- /vendor/composer/platform_check.php: -------------------------------------------------------------------------------- 1 | = 80000)) { 8 | $issues[] = 'Your Composer dependencies require a PHP version ">= 8.0.0". You are running ' . PHP_VERSION . '.'; 9 | } 10 | 11 | if ($issues) { 12 | if (!headers_sent()) { 13 | header('HTTP/1.1 500 Internal Server Error'); 14 | } 15 | if (!ini_get('display_errors')) { 16 | if (PHP_SAPI === 'cli' || PHP_SAPI === 'phpdbg') { 17 | fwrite(STDERR, 'Composer detected issues in your platform:' . PHP_EOL.PHP_EOL . implode(PHP_EOL, $issues) . PHP_EOL.PHP_EOL); 18 | } elseif (!headers_sent()) { 19 | echo 'Composer detected issues in your platform:' . PHP_EOL.PHP_EOL . str_replace('You are running '.PHP_VERSION.'.', '', implode(PHP_EOL, $issues)) . PHP_EOL.PHP_EOL; 20 | } 21 | } 22 | trigger_error( 23 | 'Composer detected issues in your platform: ' . implode(' ', $issues), 24 | E_USER_ERROR 25 | ); 26 | } 27 | --------------------------------------------------------------------------------