From f346d3d962dba12e2e6ce7cc7b086679695bce9c Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 9 Apr 2025 23:41:07 +0000 Subject: [PATCH] Update dependency firebase/php-jwt to v6.11.1 --- composer.lock | 24 +- vendor/composer/InstalledVersions.php | 72 ++++- vendor/composer/installed.json | 25 +- vendor/composer/installed.php | 12 +- vendor/firebase/php-jwt/CHANGELOG.md | 205 ++++++++++++++ vendor/firebase/php-jwt/README.md | 260 +++++++++++------- vendor/firebase/php-jwt/composer.json | 13 +- .../php-jwt/src/BeforeValidException.php | 13 +- vendor/firebase/php-jwt/src/CachedKeySet.php | 75 +++-- .../firebase/php-jwt/src/ExpiredException.php | 13 +- vendor/firebase/php-jwt/src/JWK.php | 176 +++++++++++- vendor/firebase/php-jwt/src/JWT.php | 159 +++++++---- .../src/JWTExceptionWithPayloadInterface.php | 20 ++ vendor/firebase/php-jwt/src/Key.php | 13 +- 14 files changed, 840 insertions(+), 240 deletions(-) create mode 100644 vendor/firebase/php-jwt/CHANGELOG.md create mode 100644 vendor/firebase/php-jwt/src/JWTExceptionWithPayloadInterface.php diff --git a/composer.lock b/composer.lock index a52592c..8371a65 100644 --- a/composer.lock +++ b/composer.lock @@ -8,26 +8,26 @@ "packages": [ { "name": "firebase/php-jwt", - "version": "v6.10.0", + "version": "v6.11.1", "source": { "type": "git", "url": "https://github.com/firebase/php-jwt.git", - "reference": "a49db6f0a5033aef5143295342f1c95521b075ff" + "reference": "d1e91ecf8c598d073d0995afa8cd5c75c6e19e66" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/firebase/php-jwt/zipball/a49db6f0a5033aef5143295342f1c95521b075ff", - "reference": "a49db6f0a5033aef5143295342f1c95521b075ff", + "url": "https://api.github.com/repos/firebase/php-jwt/zipball/d1e91ecf8c598d073d0995afa8cd5c75c6e19e66", + "reference": "d1e91ecf8c598d073d0995afa8cd5c75c6e19e66", "shasum": "" }, "require": { - "php": "^7.4||^8.0" + "php": "^8.0" }, "require-dev": { - "guzzlehttp/guzzle": "^6.5||^7.4", + "guzzlehttp/guzzle": "^7.4", "phpspec/prophecy-phpunit": "^2.0", "phpunit/phpunit": "^9.5", - "psr/cache": "^1.0||^2.0", + "psr/cache": "^2.0||^3.0", "psr/http-client": "^1.0", "psr/http-factory": "^1.0" }, @@ -65,18 +65,18 @@ ], "support": { "issues": "https://github.com/firebase/php-jwt/issues", - "source": "https://github.com/firebase/php-jwt/tree/v6.10.0" + "source": "https://github.com/firebase/php-jwt/tree/v6.11.1" }, - "time": "2023-12-01T16:26:39+00:00" + "time": "2025-04-09T20:32:01+00:00" } ], "packages-dev": [], "aliases": [], "minimum-stability": "stable", - "stability-flags": [], + "stability-flags": {}, "prefer-stable": false, "prefer-lowest": false, - "platform": [], - "platform-dev": [], + "platform": {}, + "platform-dev": {}, "plugin-api-version": "2.6.0" } diff --git a/vendor/composer/InstalledVersions.php b/vendor/composer/InstalledVersions.php index 41bc143..2052022 100644 --- a/vendor/composer/InstalledVersions.php +++ b/vendor/composer/InstalledVersions.php @@ -26,12 +26,23 @@ */ class InstalledVersions { + /** + * @var string|null if set (by reflection by Composer), this should be set to the path where this class is being copied to + * @internal + */ + private static $selfDir = null; + /** * @var mixed[]|null - * @psalm-var array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string, type: string}, versions: array}|array{}|null + * @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 */ private static $installed; + /** + * @var bool + */ + private static $installedIsLocalDir; + /** * @var bool|null */ @@ -39,7 +50,7 @@ class InstalledVersions /** * @var array[] - * @psalm-var array}> + * @psalm-var array}> */ private static $installedByVendor = array(); @@ -98,7 +109,7 @@ public static function isInstalled($packageName, $includeDevRequirements = true) { foreach (self::getInstalled() as $installed) { if (isset($installed['versions'][$packageName])) { - return $includeDevRequirements || empty($installed['versions'][$packageName]['dev_requirement']); + return $includeDevRequirements || !isset($installed['versions'][$packageName]['dev_requirement']) || $installed['versions'][$packageName]['dev_requirement'] === false; } } @@ -119,7 +130,7 @@ public static function isInstalled($packageName, $includeDevRequirements = true) */ public static function satisfies(VersionParser $parser, $packageName, $constraint) { - $constraint = $parser->parseConstraints($constraint); + $constraint = $parser->parseConstraints((string) $constraint); $provided = $parser->parseConstraints(self::getVersionRanges($packageName)); return $provided->matches($constraint); @@ -243,7 +254,7 @@ public static function getInstallPath($packageName) /** * @return array - * @psalm-return array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string, type: string} + * @psalm-return array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool} */ public static function getRootPackage() { @@ -257,7 +268,7 @@ public static function getRootPackage() * * @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. * @return array[] - * @psalm-return array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string, type: string}, versions: array} + * @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} */ public static function getRawData() { @@ -280,7 +291,7 @@ public static function getRawData() * Returns the raw data of all installed.php which are currently loaded for custom implementations * * @return array[] - * @psalm-return list}> + * @psalm-return list}> */ public static function getAllRawData() { @@ -303,17 +314,35 @@ public static function getAllRawData() * @param array[] $data A vendor/composer/installed.php data set * @return void * - * @psalm-param array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string, type: string}, versions: array} $data + * @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 */ public static function reload($data) { self::$installed = $data; self::$installedByVendor = array(); + + // when using reload, we disable the duplicate protection to ensure that self::$installed data is + // always returned, but we cannot know whether it comes from the installed.php in __DIR__ or not, + // so we have to assume it does not, and that may result in duplicate data being returned when listing + // all installed packages for example + self::$installedIsLocalDir = false; + } + + /** + * @return string + */ + private static function getSelfDir() + { + if (self::$selfDir === null) { + self::$selfDir = strtr(__DIR__, '\\', '/'); + } + + return self::$selfDir; } /** * @return array[] - * @psalm-return list}> + * @psalm-return list}> */ private static function getInstalled() { @@ -322,17 +351,27 @@ private static function getInstalled() } $installed = array(); + $copiedLocalDir = false; if (self::$canGetVendors) { + $selfDir = self::getSelfDir(); foreach (ClassLoader::getRegisteredLoaders() as $vendorDir => $loader) { + $vendorDir = strtr($vendorDir, '\\', '/'); if (isset(self::$installedByVendor[$vendorDir])) { $installed[] = self::$installedByVendor[$vendorDir]; } elseif (is_file($vendorDir.'/composer/installed.php')) { - $installed[] = self::$installedByVendor[$vendorDir] = require $vendorDir.'/composer/installed.php'; - if (null === self::$installed && strtr($vendorDir.'/composer', '\\', '/') === strtr(__DIR__, '\\', '/')) { - self::$installed = $installed[count($installed) - 1]; + /** @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 */ + $required = require $vendorDir.'/composer/installed.php'; + self::$installedByVendor[$vendorDir] = $required; + $installed[] = $required; + if (self::$installed === null && $vendorDir.'/composer' === $selfDir) { + self::$installed = $required; + self::$installedIsLocalDir = true; } } + if (self::$installedIsLocalDir && $vendorDir.'/composer' === $selfDir) { + $copiedLocalDir = true; + } } } @@ -340,12 +379,17 @@ private static function getInstalled() // only require the installed.php file if this file is loaded from its dumped location, // and not from its source location in the composer/composer package, see https://github.com/composer/composer/issues/9937 if (substr(__DIR__, -8, 1) !== 'C') { - self::$installed = require __DIR__ . '/installed.php'; + /** @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 */ + $required = require __DIR__ . '/installed.php'; + self::$installed = $required; } else { self::$installed = array(); } } - $installed[] = self::$installed; + + if (self::$installed !== array() && !$copiedLocalDir) { + $installed[] = self::$installed; + } return $installed; } diff --git a/vendor/composer/installed.json b/vendor/composer/installed.json index 5bfeb74..3adcf01 100644 --- a/vendor/composer/installed.json +++ b/vendor/composer/installed.json @@ -2,34 +2,35 @@ "packages": [ { "name": "firebase/php-jwt", - "version": "v6.2.0", - "version_normalized": "6.2.0.0", + "version": "v6.11.1", + "version_normalized": "6.11.1.0", "source": { "type": "git", "url": "https://github.com/firebase/php-jwt.git", - "reference": "d28e6df83830252650da4623c78aaaf98fb385f3" + "reference": "d1e91ecf8c598d073d0995afa8cd5c75c6e19e66" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/firebase/php-jwt/zipball/d28e6df83830252650da4623c78aaaf98fb385f3", - "reference": "d28e6df83830252650da4623c78aaaf98fb385f3", + "url": "https://api.github.com/repos/firebase/php-jwt/zipball/d1e91ecf8c598d073d0995afa8cd5c75c6e19e66", + "reference": "d1e91ecf8c598d073d0995afa8cd5c75c6e19e66", "shasum": "" }, "require": { - "php": "^7.1||^8.0" + "php": "^8.0" }, "require-dev": { - "guzzlehttp/guzzle": "^6.5||^7.4", - "phpspec/prophecy-phpunit": "^1.1", - "phpunit/phpunit": "^7.5||^9.5", - "psr/cache": "^1.0||^2.0", + "guzzlehttp/guzzle": "^7.4", + "phpspec/prophecy-phpunit": "^2.0", + "phpunit/phpunit": "^9.5", + "psr/cache": "^2.0||^3.0", "psr/http-client": "^1.0", "psr/http-factory": "^1.0" }, "suggest": { + "ext-sodium": "Support EdDSA (Ed25519) signatures", "paragonie/sodium_compat": "Support EdDSA (Ed25519) signatures when libsodium is not present" }, - "time": "2022-05-13T20:54:50+00:00", + "time": "2025-04-09T20:32:01+00:00", "type": "library", "installation-source": "dist", "autoload": { @@ -61,7 +62,7 @@ ], "support": { "issues": "https://github.com/firebase/php-jwt/issues", - "source": "https://github.com/firebase/php-jwt/tree/v6.2.0" + "source": "https://github.com/firebase/php-jwt/tree/v6.11.1" }, "install-path": "../firebase/php-jwt" } diff --git a/vendor/composer/installed.php b/vendor/composer/installed.php index 767e5b8..230f558 100644 --- a/vendor/composer/installed.php +++ b/vendor/composer/installed.php @@ -1,31 +1,31 @@ array( + 'name' => '__root__', 'pretty_version' => 'dev-main', 'version' => 'dev-main', + 'reference' => '88f8f7ee0f71aa443b077b7c6e102f1036094866', 'type' => 'library', 'install_path' => __DIR__ . '/../../', 'aliases' => array(), - 'reference' => '83968534e6a2301829fb6ef4a3a32ebc3ed839cb', - 'name' => '__root__', 'dev' => true, ), 'versions' => array( '__root__' => array( 'pretty_version' => 'dev-main', 'version' => 'dev-main', + 'reference' => '88f8f7ee0f71aa443b077b7c6e102f1036094866', 'type' => 'library', 'install_path' => __DIR__ . '/../../', 'aliases' => array(), - 'reference' => '83968534e6a2301829fb6ef4a3a32ebc3ed839cb', 'dev_requirement' => false, ), 'firebase/php-jwt' => array( - 'pretty_version' => 'v6.2.0', - 'version' => '6.2.0.0', + 'pretty_version' => 'v6.11.1', + 'version' => '6.11.1.0', + 'reference' => 'd1e91ecf8c598d073d0995afa8cd5c75c6e19e66', 'type' => 'library', 'install_path' => __DIR__ . '/../firebase/php-jwt', 'aliases' => array(), - 'reference' => 'd28e6df83830252650da4623c78aaaf98fb385f3', 'dev_requirement' => false, ), ), diff --git a/vendor/firebase/php-jwt/CHANGELOG.md b/vendor/firebase/php-jwt/CHANGELOG.md new file mode 100644 index 0000000..7b5f6ce --- /dev/null +++ b/vendor/firebase/php-jwt/CHANGELOG.md @@ -0,0 +1,205 @@ +# Changelog + +## [6.11.1](https://github.com/firebase/php-jwt/compare/v6.11.0...v6.11.1) (2025-04-09) + + +### Bug Fixes + +* update error text for consistency ([#528](https://github.com/firebase/php-jwt/issues/528)) ([c11113a](https://github.com/firebase/php-jwt/commit/c11113afa13265e016a669e75494b9203b8a7775)) + +## [6.11.0](https://github.com/firebase/php-jwt/compare/v6.10.2...v6.11.0) (2025-01-23) + + +### Features + +* support octet typed JWK ([#587](https://github.com/firebase/php-jwt/issues/587)) ([7cb8a26](https://github.com/firebase/php-jwt/commit/7cb8a265fa81edf2fa6ef8098f5bc5ae573c33ad)) + + +### Bug Fixes + +* 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)) + +## [6.10.2](https://github.com/firebase/php-jwt/compare/v6.10.1...v6.10.2) (2024-11-24) + + +### Bug Fixes + +* Mitigate PHP8.4 deprecation warnings ([#570](https://github.com/firebase/php-jwt/issues/570)) ([76808fa](https://github.com/firebase/php-jwt/commit/76808fa227f3811aa5cdb3bf81233714b799a5b5)) +* support php 8.4 ([#583](https://github.com/firebase/php-jwt/issues/583)) ([e3d68b0](https://github.com/firebase/php-jwt/commit/e3d68b044421339443c74199edd020e03fb1887e)) + +## [6.10.1](https://github.com/firebase/php-jwt/compare/v6.10.0...v6.10.1) (2024-05-18) + + +### Bug Fixes + +* 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)) +* ratelimit cache expiration ([#550](https://github.com/firebase/php-jwt/issues/550)) ([dda7250](https://github.com/firebase/php-jwt/commit/dda725033585ece30ff8cae8937320d7e9f18bae)) + +## [6.10.0](https://github.com/firebase/php-jwt/compare/v6.9.0...v6.10.0) (2023-11-28) + + +### Features + +* allow typ header override ([#546](https://github.com/firebase/php-jwt/issues/546)) ([79cb30b](https://github.com/firebase/php-jwt/commit/79cb30b729a22931b2fbd6b53f20629a83031ba9)) + +## [6.9.0](https://github.com/firebase/php-jwt/compare/v6.8.1...v6.9.0) (2023-10-04) + + +### Features + +* add payload to jwt exception ([#521](https://github.com/firebase/php-jwt/issues/521)) ([175edf9](https://github.com/firebase/php-jwt/commit/175edf958bb61922ec135b2333acf5622f2238a2)) + +## [6.8.1](https://github.com/firebase/php-jwt/compare/v6.8.0...v6.8.1) (2023-07-14) + + +### Bug Fixes + +* 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)) +* 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)) + +## [6.8.0](https://github.com/firebase/php-jwt/compare/v6.7.0...v6.8.0) (2023-06-14) + + +### Features + +* add support for P-384 curve ([#515](https://github.com/firebase/php-jwt/issues/515)) ([5de4323](https://github.com/firebase/php-jwt/commit/5de4323f4baf4d70bca8663bd87682a69c656c3d)) + + +### Bug Fixes + +* handle invalid http responses ([#508](https://github.com/firebase/php-jwt/issues/508)) ([91c39c7](https://github.com/firebase/php-jwt/commit/91c39c72b22fc3e1191e574089552c1f2041c718)) + +## [6.7.0](https://github.com/firebase/php-jwt/compare/v6.6.0...v6.7.0) (2023-06-14) + + +### Features + +* 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)) + +## [6.6.0](https://github.com/firebase/php-jwt/compare/v6.5.0...v6.6.0) (2023-06-13) + + +### Features + +* allow get headers when decoding token ([#442](https://github.com/firebase/php-jwt/issues/442)) ([fb85f47](https://github.com/firebase/php-jwt/commit/fb85f47cfaeffdd94faf8defdf07164abcdad6c3)) + + +### Bug Fixes + +* 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)) + +## [6.5.0](https://github.com/firebase/php-jwt/compare/v6.4.0...v6.5.0) (2023-05-12) + + +### Bug Fixes + +* allow KID of '0' ([#505](https://github.com/firebase/php-jwt/issues/505)) ([9dc46a9](https://github.com/firebase/php-jwt/commit/9dc46a9c3e5801294249cfd2554c5363c9f9326a)) + + +### Miscellaneous Chores + +* drop support for PHP 7.3 ([#495](https://github.com/firebase/php-jwt/issues/495)) + +## [6.4.0](https://github.com/firebase/php-jwt/compare/v6.3.2...v6.4.0) (2023-02-08) + + +### Features + +* add support for W3C ES256K ([#462](https://github.com/firebase/php-jwt/issues/462)) ([213924f](https://github.com/firebase/php-jwt/commit/213924f51936291fbbca99158b11bd4ae56c2c95)) +* 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)) + +## [6.3.2](https://github.com/firebase/php-jwt/compare/v6.3.1...v6.3.2) (2022-11-01) + + +### Bug Fixes + +* check kid before using as array index ([bad1b04](https://github.com/firebase/php-jwt/commit/bad1b040d0c736bbf86814c6b5ae614f517cf7bd)) + +## [6.3.1](https://github.com/firebase/php-jwt/compare/v6.3.0...v6.3.1) (2022-11-01) + + +### Bug Fixes + +* casing of GET for PSR compat ([#451](https://github.com/firebase/php-jwt/issues/451)) ([60b52b7](https://github.com/firebase/php-jwt/commit/60b52b71978790eafcf3b95cfbd83db0439e8d22)) +* 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)) + +## 6.3.0 / 2022-07-15 + + - Added ES256 support to JWK parsing ([#399](https://github.com/firebase/php-jwt/pull/399)) + - Fixed potential caching error in `CachedKeySet` by caching jwks as strings ([#435](https://github.com/firebase/php-jwt/pull/435)) + +## 6.2.0 / 2022-05-14 + + - Added `CachedKeySet` ([#397](https://github.com/firebase/php-jwt/pull/397)) + - Added `$defaultAlg` parameter to `JWT::parseKey` and `JWT::parseKeySet` ([#426](https://github.com/firebase/php-jwt/pull/426)). + +## 6.1.0 / 2022-03-23 + + - Drop support for PHP 5.3, 5.4, 5.5, 5.6, and 7.0 + - Add parameter typing and return types where possible + +## 6.0.0 / 2022-01-24 + + - **Backwards-Compatibility Breaking Changes**: See the [Release Notes](https://github.com/firebase/php-jwt/releases/tag/v6.0.0) for more information. + - New Key object to prevent key/algorithm type confusion (#365) + - Add JWK support (#273) + - Add ES256 support (#256) + - Add ES384 support (#324) + - Add Ed25519 support (#343) + +## 5.0.0 / 2017-06-26 +- Support RS384 and RS512. + See [#117](https://github.com/firebase/php-jwt/pull/117). Thanks [@joostfaassen](https://github.com/joostfaassen)! +- Add an example for RS256 openssl. + See [#125](https://github.com/firebase/php-jwt/pull/125). Thanks [@akeeman](https://github.com/akeeman)! +- Detect invalid Base64 encoding in signature. + See [#162](https://github.com/firebase/php-jwt/pull/162). Thanks [@psignoret](https://github.com/psignoret)! +- Update `JWT::verify` to handle OpenSSL errors. + See [#159](https://github.com/firebase/php-jwt/pull/159). Thanks [@bshaffer](https://github.com/bshaffer)! +- Add `array` type hinting to `decode` method + See [#101](https://github.com/firebase/php-jwt/pull/101). Thanks [@hywak](https://github.com/hywak)! +- Add all JSON error types. + See [#110](https://github.com/firebase/php-jwt/pull/110). Thanks [@gbalduzzi](https://github.com/gbalduzzi)! +- Bugfix 'kid' not in given key list. + See [#129](https://github.com/firebase/php-jwt/pull/129). Thanks [@stampycode](https://github.com/stampycode)! +- Miscellaneous cleanup, documentation and test fixes. + See [#107](https://github.com/firebase/php-jwt/pull/107), [#115](https://github.com/firebase/php-jwt/pull/115), + [#160](https://github.com/firebase/php-jwt/pull/160), [#161](https://github.com/firebase/php-jwt/pull/161), and + [#165](https://github.com/firebase/php-jwt/pull/165). Thanks [@akeeman](https://github.com/akeeman), + [@chinedufn](https://github.com/chinedufn), and [@bshaffer](https://github.com/bshaffer)! + +## 4.0.0 / 2016-07-17 +- 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)! +- 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)! +- Fixes to exceptions classes. See [#81](https://github.com/firebase/php-jwt/pull/81) for details. Thanks to [@Maks3w](https://github.com/Maks3w)! +- Fixes to PHPDoc. See [#76](https://github.com/firebase/php-jwt/pull/76) for details. Thanks to [@akeeman](https://github.com/akeeman)! + +## 3.0.0 / 2015-07-22 +- Minimum PHP version updated from `5.2.0` to `5.3.0`. +- Add `\Firebase\JWT` namespace. See +[#59](https://github.com/firebase/php-jwt/pull/59) for details. Thanks to +[@Dashron](https://github.com/Dashron)! +- Require a non-empty key to decode and verify a JWT. See +[#60](https://github.com/firebase/php-jwt/pull/60) for details. Thanks to +[@sjones608](https://github.com/sjones608)! +- Cleaner documentation blocks in the code. See +[#62](https://github.com/firebase/php-jwt/pull/62) for details. Thanks to +[@johanderuijter](https://github.com/johanderuijter)! + +## 2.2.0 / 2015-06-22 +- Add support for adding custom, optional JWT headers to `JWT::encode()`. See +[#53](https://github.com/firebase/php-jwt/pull/53/files) for details. Thanks to +[@mcocaro](https://github.com/mcocaro)! + +## 2.1.0 / 2015-05-20 +- Add support for adding a leeway to `JWT:decode()` that accounts for clock skew +between signing and verifying entities. Thanks to [@lcabral](https://github.com/lcabral)! +- Add support for passing an object implementing the `ArrayAccess` interface for +`$keys` argument in `JWT::decode()`. Thanks to [@aztech-dev](https://github.com/aztech-dev)! + +## 2.0.0 / 2015-04-01 +- **Note**: It is strongly recommended that you update to > v2.0.0 to address + known security vulnerabilities in prior versions when both symmetric and + asymmetric keys are used together. +- Update signature for `JWT::decode(...)` to require an array of supported + algorithms to use when verifying token signatures. diff --git a/vendor/firebase/php-jwt/README.md b/vendor/firebase/php-jwt/README.md index 42e8b6d..e45ccb8 100644 --- a/vendor/firebase/php-jwt/README.md +++ b/vendor/firebase/php-jwt/README.md @@ -17,7 +17,7 @@ composer require firebase/php-jwt ``` Optionally, install the `paragonie/sodium_compat` package from composer if your -php is < 7.2 or does not have libsodium installed: +php env does not have libsodium installed: ```bash composer require paragonie/sodium_compat @@ -45,9 +45,13 @@ $payload = [ */ $jwt = JWT::encode($payload, $key, 'HS256'); $decoded = JWT::decode($jwt, new Key($key, 'HS256')); - print_r($decoded); +// Pass a stdClass in as the third parameter to get the decoded header values +$headers = new stdClass(); +$decoded = JWT::decode($jwt, new Key($key, 'HS256'), $headers); +print_r($headers); + /* NOTE: This will now be an object instead of an associative array. To get an associative array, you will need to cast it as such: @@ -65,6 +69,40 @@ $decoded_array = (array) $decoded; JWT::$leeway = 60; // $leeway in seconds $decoded = JWT::decode($jwt, new Key($key, 'HS256')); ``` +Example encode/decode headers +------- +Decoding the JWT headers without verifying the JWT first is NOT recommended, and is not supported by +this library. This is because without verifying the JWT, the header values could have been tampered with. +Any value pulled from an unverified header should be treated as if it could be any string sent in from an +attacker. If this is something you still want to do in your application for whatever reason, it's possible to +decode the header values manually simply by calling `json_decode` and `base64_decode` on the JWT +header part: +```php +use Firebase\JWT\JWT; + +$key = 'example_key'; +$payload = [ + 'iss' => 'http://example.org', + 'aud' => 'http://example.com', + 'iat' => 1356999524, + 'nbf' => 1357000000 +]; + +$headers = [ + 'x-forwarded-for' => 'www.google.com' +]; + +// Encode headers in the JWT string +$jwt = JWT::encode($payload, $key, 'HS256', null, $headers); + +// Decode headers from the JWT string WITHOUT validation +// **IMPORTANT**: This operation is vulnerable to attacks, as the JWT has not yet been verified. +// These headers could be any value sent by an attacker. +list($headersB64, $payloadB64, $sig) = explode('.', $jwt); +$decoded = json_decode(base64_decode($headersB64), true); + +print_r($decoded); +``` Example with RS256 (openssl) ---------------------------- ```php @@ -73,28 +111,43 @@ use Firebase\JWT\Key; $privateKey = << 'example.org', + 'aud' => 'example.com', + 'iat' => 1356999524, + 'nbf' => 1357000000 +]; + +$jwt1 = JWT::encode($payload, $privateKey1, 'RS256', 'kid1'); +$jwt2 = JWT::encode($payload, $privateKey2, 'EdDSA', 'kid2'); +echo "Encode 1:\n" . print_r($jwt1, true) . "\n"; +echo "Encode 2:\n" . print_r($jwt2, true) . "\n"; + +$keys = [ + 'kid1' => new Key($publicKey1, 'RS256'), + 'kid2' => new Key($publicKey2, 'EdDSA'), +]; + +$decoded1 = JWT::decode($jwt1, $keys); +$decoded2 = JWT::decode($jwt2, $keys); + +echo "Decode 1:\n" . print_r((array) $decoded1, true) . "\n"; +echo "Decode 2:\n" . print_r((array) $decoded2, true) . "\n"; +``` + Using JWKs ---------- @@ -200,7 +291,7 @@ $jwks = ['keys' => []]; // JWK::parseKeySet($jwks) returns an associative array of **kid** to Firebase\JWT\Key // objects. Pass this as the second parameter to JWT::decode. -JWT::decode($payload, JWK::parseKeySet($jwks)); +JWT::decode($jwt, JWK::parseKeySet($jwks)); ``` Using Cached Key Sets @@ -245,6 +336,58 @@ $decoded = JWT::decode($jwt, $keySet); Miscellaneous ------------- +#### Exception Handling + +When a call to `JWT::decode` is invalid, it will throw one of the following exceptions: + +```php +use Firebase\JWT\JWT; +use Firebase\JWT\SignatureInvalidException; +use Firebase\JWT\BeforeValidException; +use Firebase\JWT\ExpiredException; +use DomainException; +use InvalidArgumentException; +use UnexpectedValueException; + +try { + $decoded = JWT::decode($jwt, $keys); +} catch (InvalidArgumentException $e) { + // provided key/key-array is empty or malformed. +} catch (DomainException $e) { + // provided algorithm is unsupported OR + // provided key is invalid OR + // unknown error thrown in openSSL or libsodium OR + // libsodium is required but not available. +} catch (SignatureInvalidException $e) { + // provided JWT signature verification failed. +} catch (BeforeValidException $e) { + // provided JWT is trying to be used before "nbf" claim OR + // provided JWT is trying to be used before "iat" claim. +} catch (ExpiredException $e) { + // provided JWT is trying to be used after "exp" claim. +} catch (UnexpectedValueException $e) { + // provided JWT is malformed OR + // provided JWT is missing an algorithm / using an unsupported algorithm OR + // provided JWT algorithm does not match provided key OR + // provided key ID in key/key-array is empty or invalid. +} +``` + +All exceptions in the `Firebase\JWT` namespace extend `UnexpectedValueException`, and can be simplified +like this: + +```php +use Firebase\JWT\JWT; +use UnexpectedValueException; +try { + $decoded = JWT::decode($jwt, $keys); +} catch (LogicException $e) { + // errors having to do with environmental setup or malformed JWT Keys +} catch (UnexpectedValueException $e) { + // errors having to do with JWT signature and claims +} +``` + #### Casting to array The return value of `JWT::decode` is the generic PHP object `stdClass`. If you'd like to handle with arrays @@ -252,87 +395,12 @@ instead, you can do the following: ```php // return type is stdClass -$decoded = JWT::decode($payload, $keys); +$decoded = JWT::decode($jwt, $keys); // cast to array $decoded = json_decode(json_encode($decoded), true); ``` -Changelog ---------- - -#### 6.1.0 / 2022-03-23 - - - Drop support for PHP 5.3, 5.4, 5.5, 5.6, and 7.0 - - Add parameter typing and return types where possible - -#### 6.0.0 / 2022-01-24 - - - **Backwards-Compatibility Breaking Changes**: See the [Release Notes](https://github.com/firebase/php-jwt/releases/tag/v6.0.0) for more information. - - New Key object to prevent key/algorithm type confusion (#365) - - Add JWK support (#273) - - Add ES256 support (#256) - - Add ES384 support (#324) - - Add Ed25519 support (#343) - -#### 5.0.0 / 2017-06-26 -- Support RS384 and RS512. - See [#117](https://github.com/firebase/php-jwt/pull/117). Thanks [@joostfaassen](https://github.com/joostfaassen)! -- Add an example for RS256 openssl. - See [#125](https://github.com/firebase/php-jwt/pull/125). Thanks [@akeeman](https://github.com/akeeman)! -- Detect invalid Base64 encoding in signature. - See [#162](https://github.com/firebase/php-jwt/pull/162). Thanks [@psignoret](https://github.com/psignoret)! -- Update `JWT::verify` to handle OpenSSL errors. - See [#159](https://github.com/firebase/php-jwt/pull/159). Thanks [@bshaffer](https://github.com/bshaffer)! -- Add `array` type hinting to `decode` method - See [#101](https://github.com/firebase/php-jwt/pull/101). Thanks [@hywak](https://github.com/hywak)! -- Add all JSON error types. - See [#110](https://github.com/firebase/php-jwt/pull/110). Thanks [@gbalduzzi](https://github.com/gbalduzzi)! -- Bugfix 'kid' not in given key list. - See [#129](https://github.com/firebase/php-jwt/pull/129). Thanks [@stampycode](https://github.com/stampycode)! -- Miscellaneous cleanup, documentation and test fixes. - See [#107](https://github.com/firebase/php-jwt/pull/107), [#115](https://github.com/firebase/php-jwt/pull/115), - [#160](https://github.com/firebase/php-jwt/pull/160), [#161](https://github.com/firebase/php-jwt/pull/161), and - [#165](https://github.com/firebase/php-jwt/pull/165). Thanks [@akeeman](https://github.com/akeeman), - [@chinedufn](https://github.com/chinedufn), and [@bshaffer](https://github.com/bshaffer)! - -#### 4.0.0 / 2016-07-17 -- 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)! -- 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)! -- Fixes to exceptions classes. See [#81](https://github.com/firebase/php-jwt/pull/81) for details. Thanks to [@Maks3w](https://github.com/Maks3w)! -- Fixes to PHPDoc. See [#76](https://github.com/firebase/php-jwt/pull/76) for details. Thanks to [@akeeman](https://github.com/akeeman)! - -#### 3.0.0 / 2015-07-22 -- Minimum PHP version updated from `5.2.0` to `5.3.0`. -- Add `\Firebase\JWT` namespace. See -[#59](https://github.com/firebase/php-jwt/pull/59) for details. Thanks to -[@Dashron](https://github.com/Dashron)! -- Require a non-empty key to decode and verify a JWT. See -[#60](https://github.com/firebase/php-jwt/pull/60) for details. Thanks to -[@sjones608](https://github.com/sjones608)! -- Cleaner documentation blocks in the code. See -[#62](https://github.com/firebase/php-jwt/pull/62) for details. Thanks to -[@johanderuijter](https://github.com/johanderuijter)! - -#### 2.2.0 / 2015-06-22 -- Add support for adding custom, optional JWT headers to `JWT::encode()`. See -[#53](https://github.com/firebase/php-jwt/pull/53/files) for details. Thanks to -[@mcocaro](https://github.com/mcocaro)! - -#### 2.1.0 / 2015-05-20 -- Add support for adding a leeway to `JWT:decode()` that accounts for clock skew -between signing and verifying entities. Thanks to [@lcabral](https://github.com/lcabral)! -- Add support for passing an object implementing the `ArrayAccess` interface for -`$keys` argument in `JWT::decode()`. Thanks to [@aztech-dev](https://github.com/aztech-dev)! - -#### 2.0.0 / 2015-04-01 -- **Note**: It is strongly recommended that you update to > v2.0.0 to address - known security vulnerabilities in prior versions when both symmetric and - asymmetric keys are used together. -- Update signature for `JWT::decode(...)` to require an array of supported - algorithms to use when verifying token signatures. - - Tests ----- Run the tests using phpunit: diff --git a/vendor/firebase/php-jwt/composer.json b/vendor/firebase/php-jwt/composer.json index 2a3cb2d..816cfd0 100644 --- a/vendor/firebase/php-jwt/composer.json +++ b/vendor/firebase/php-jwt/composer.json @@ -20,10 +20,11 @@ ], "license": "BSD-3-Clause", "require": { - "php": "^7.1||^8.0" + "php": "^8.0" }, "suggest": { - "paragonie/sodium_compat": "Support EdDSA (Ed25519) signatures when libsodium is not present" + "paragonie/sodium_compat": "Support EdDSA (Ed25519) signatures when libsodium is not present", + "ext-sodium": "Support EdDSA (Ed25519) signatures" }, "autoload": { "psr-4": { @@ -31,10 +32,10 @@ } }, "require-dev": { - "guzzlehttp/guzzle": "^6.5||^7.4", - "phpspec/prophecy-phpunit": "^1.1", - "phpunit/phpunit": "^7.5||^9.5", - "psr/cache": "^1.0||^2.0", + "guzzlehttp/guzzle": "^7.4", + "phpspec/prophecy-phpunit": "^2.0", + "phpunit/phpunit": "^9.5", + "psr/cache": "^2.0||^3.0", "psr/http-client": "^1.0", "psr/http-factory": "^1.0" } diff --git a/vendor/firebase/php-jwt/src/BeforeValidException.php b/vendor/firebase/php-jwt/src/BeforeValidException.php index c147852..595164b 100644 --- a/vendor/firebase/php-jwt/src/BeforeValidException.php +++ b/vendor/firebase/php-jwt/src/BeforeValidException.php @@ -2,6 +2,17 @@ namespace Firebase\JWT; -class BeforeValidException extends \UnexpectedValueException +class BeforeValidException extends \UnexpectedValueException implements JWTExceptionWithPayloadInterface { + private object $payload; + + public function setPayload(object $payload): void + { + $this->payload = $payload; + } + + public function getPayload(): object + { + return $this->payload; + } } diff --git a/vendor/firebase/php-jwt/src/CachedKeySet.php b/vendor/firebase/php-jwt/src/CachedKeySet.php index f1580c9..8e8e8d6 100644 --- a/vendor/firebase/php-jwt/src/CachedKeySet.php +++ b/vendor/firebase/php-jwt/src/CachedKeySet.php @@ -3,6 +3,7 @@ namespace Firebase\JWT; use ArrayAccess; +use InvalidArgumentException; use LogicException; use OutOfBoundsException; use Psr\Cache\CacheItemInterface; @@ -10,6 +11,7 @@ use Psr\Http\Client\ClientInterface; use Psr\Http\Message\RequestFactoryInterface; use RuntimeException; +use UnexpectedValueException; /** * @implements ArrayAccess @@ -41,7 +43,7 @@ class CachedKeySet implements ArrayAccess */ private $cacheItem; /** - * @var array + * @var array> */ private $keySet; /** @@ -78,9 +80,9 @@ public function __construct( ClientInterface $httpClient, RequestFactoryInterface $httpFactory, CacheItemPoolInterface $cache, - int $expiresAfter = null, + ?int $expiresAfter = null, bool $rateLimit = false, - string $defaultAlg = null + ?string $defaultAlg = null ) { $this->jwksUri = $jwksUri; $this->httpClient = $httpClient; @@ -101,7 +103,7 @@ public function offsetGet($keyId): Key if (!$this->keyIdExists($keyId)) { throw new OutOfBoundsException('Key ID not found'); } - return $this->keySet[$keyId]; + return JWK::parseKey($this->keySet[$keyId], $this->defaultAlg); } /** @@ -130,15 +132,43 @@ public function offsetUnset($offset): void throw new LogicException('Method not implemented'); } + /** + * @return array + */ + private function formatJwksForCache(string $jwks): array + { + $jwks = json_decode($jwks, true); + + if (!isset($jwks['keys'])) { + throw new UnexpectedValueException('"keys" member must exist in the JWK Set'); + } + + if (empty($jwks['keys'])) { + throw new InvalidArgumentException('JWK Set did not contain any keys'); + } + + $keys = []; + foreach ($jwks['keys'] as $k => $v) { + $kid = isset($v['kid']) ? $v['kid'] : $k; + $keys[(string) $kid] = $v; + } + + return $keys; + } + private function keyIdExists(string $keyId): bool { - $keySetToCache = null; if (null === $this->keySet) { $item = $this->getCacheItem(); // Try to load keys from cache if ($item->isHit()) { - // item found! Return it + // item found! retrieve it $this->keySet = $item->get(); + // If the cached item is a string, the JWKS response was cached (previous behavior). + // Parse this into expected format array instead. + if (\is_string($this->keySet)) { + $this->keySet = $this->formatJwksForCache($this->keySet); + } } } @@ -146,19 +176,26 @@ private function keyIdExists(string $keyId): bool if ($this->rateLimitExceeded()) { return false; } - $request = $this->httpFactory->createRequest('get', $this->jwksUri); + $request = $this->httpFactory->createRequest('GET', $this->jwksUri); $jwksResponse = $this->httpClient->sendRequest($request); - $jwks = json_decode((string) $jwksResponse->getBody(), true); - $this->keySet = $keySetToCache = JWK::parseKeySet($jwks, $this->defaultAlg); + if ($jwksResponse->getStatusCode() !== 200) { + throw new UnexpectedValueException( + \sprintf('HTTP Error: %d %s for URI "%s"', + $jwksResponse->getStatusCode(), + $jwksResponse->getReasonPhrase(), + $this->jwksUri, + ), + $jwksResponse->getStatusCode() + ); + } + $this->keySet = $this->formatJwksForCache((string) $jwksResponse->getBody()); if (!isset($this->keySet[$keyId])) { return false; } - } - if ($keySetToCache) { $item = $this->getCacheItem(); - $item->set($keySetToCache); + $item->set($this->keySet); if ($this->expiresAfter) { $item->expiresAfter($this->expiresAfter); } @@ -175,15 +212,21 @@ private function rateLimitExceeded(): bool } $cacheItem = $this->cache->getItem($this->rateLimitCacheKey); - if (!$cacheItem->isHit()) { - $cacheItem->expiresAfter(1); // # of calls are cached each minute + + $cacheItemData = []; + if ($cacheItem->isHit() && \is_array($data = $cacheItem->get())) { + $cacheItemData = $data; } - $callsPerMinute = (int) $cacheItem->get(); + $callsPerMinute = $cacheItemData['callsPerMinute'] ?? 0; + $expiry = $cacheItemData['expiry'] ?? new \DateTime('+60 seconds', new \DateTimeZone('UTC')); + if (++$callsPerMinute > $this->maxCallsPerMinute) { return true; } - $cacheItem->set($callsPerMinute); + + $cacheItem->set(['expiry' => $expiry, 'callsPerMinute' => $callsPerMinute]); + $cacheItem->expiresAt($expiry); $this->cache->save($cacheItem); return false; } diff --git a/vendor/firebase/php-jwt/src/ExpiredException.php b/vendor/firebase/php-jwt/src/ExpiredException.php index 81ba52d..12fef09 100644 --- a/vendor/firebase/php-jwt/src/ExpiredException.php +++ b/vendor/firebase/php-jwt/src/ExpiredException.php @@ -2,6 +2,17 @@ namespace Firebase\JWT; -class ExpiredException extends \UnexpectedValueException +class ExpiredException extends \UnexpectedValueException implements JWTExceptionWithPayloadInterface { + private object $payload; + + public function setPayload(object $payload): void + { + $this->payload = $payload; + } + + public function getPayload(): object + { + return $this->payload; + } } diff --git a/vendor/firebase/php-jwt/src/JWK.php b/vendor/firebase/php-jwt/src/JWK.php index 7f22570..405dcc4 100644 --- a/vendor/firebase/php-jwt/src/JWK.php +++ b/vendor/firebase/php-jwt/src/JWK.php @@ -20,6 +20,23 @@ */ class JWK { + private const OID = '1.2.840.10045.2.1'; + private const ASN1_OBJECT_IDENTIFIER = 0x06; + private const ASN1_SEQUENCE = 0x10; // also defined in JWT + private const ASN1_BIT_STRING = 0x03; + private const EC_CURVES = [ + 'P-256' => '1.2.840.10045.3.1.7', // Len: 64 + 'secp256k1' => '1.3.132.0.10', // Len: 64 + 'P-384' => '1.3.132.0.34', // Len: 96 + // 'P-521' => '1.3.132.0.35', // Len: 132 (not supported) + ]; + + // For keys with "kty" equal to "OKP" (Octet Key Pair), the "crv" parameter must contain the key subtype. + // This library supports the following subtypes: + private const OKP_SUBTYPES = [ + 'Ed25519' => true, // RFC 8037 + ]; + /** * Parse a set of JWK keys * @@ -35,7 +52,7 @@ class JWK * * @uses parseKey */ - public static function parseKeySet(array $jwks, string $defaultAlg = null): array + public static function parseKeySet(array $jwks, ?string $defaultAlg = null): array { $keys = []; @@ -76,7 +93,7 @@ public static function parseKeySet(array $jwks, string $defaultAlg = null): arra * * @uses createPemFromModulusAndExponent */ - public static function parseKey(array $jwk, string $defaultAlg = null): ?Key + public static function parseKey(array $jwk, ?string $defaultAlg = null): ?Key { if (empty($jwk)) { throw new InvalidArgumentException('JWK must not be empty'); @@ -114,14 +131,99 @@ public static function parseKey(array $jwk, string $defaultAlg = null): ?Key ); } return new Key($publicKey, $jwk['alg']); + case 'EC': + if (isset($jwk['d'])) { + // The key is actually a private key + throw new UnexpectedValueException('Key data must be for a public key'); + } + + if (empty($jwk['crv'])) { + throw new UnexpectedValueException('crv not set'); + } + + if (!isset(self::EC_CURVES[$jwk['crv']])) { + throw new DomainException('Unrecognised or unsupported EC curve'); + } + + if (empty($jwk['x']) || empty($jwk['y'])) { + throw new UnexpectedValueException('x and y not set'); + } + + $publicKey = self::createPemFromCrvAndXYCoordinates($jwk['crv'], $jwk['x'], $jwk['y']); + return new Key($publicKey, $jwk['alg']); + case 'OKP': + if (isset($jwk['d'])) { + // The key is actually a private key + throw new UnexpectedValueException('Key data must be for a public key'); + } + + if (!isset($jwk['crv'])) { + throw new UnexpectedValueException('crv not set'); + } + + if (empty(self::OKP_SUBTYPES[$jwk['crv']])) { + throw new DomainException('Unrecognised or unsupported OKP key subtype'); + } + + if (empty($jwk['x'])) { + throw new UnexpectedValueException('x not set'); + } + + // This library works internally with EdDSA keys (Ed25519) encoded in standard base64. + $publicKey = JWT::convertBase64urlToBase64($jwk['x']); + return new Key($publicKey, $jwk['alg']); + case 'oct': + if (!isset($jwk['k'])) { + throw new UnexpectedValueException('k not set'); + } + + return new Key(JWT::urlsafeB64Decode($jwk['k']), $jwk['alg']); default: - // Currently only RSA is supported break; } return null; } + /** + * Converts the EC JWK values to pem format. + * + * @param string $crv The EC curve (only P-256 & P-384 is supported) + * @param string $x The EC x-coordinate + * @param string $y The EC y-coordinate + * + * @return string + */ + private static function createPemFromCrvAndXYCoordinates(string $crv, string $x, string $y): string + { + $pem = + self::encodeDER( + self::ASN1_SEQUENCE, + self::encodeDER( + self::ASN1_SEQUENCE, + self::encodeDER( + self::ASN1_OBJECT_IDENTIFIER, + self::encodeOID(self::OID) + ) + . self::encodeDER( + self::ASN1_OBJECT_IDENTIFIER, + self::encodeOID(self::EC_CURVES[$crv]) + ) + ) . + self::encodeDER( + self::ASN1_BIT_STRING, + \chr(0x00) . \chr(0x04) + . JWT::urlsafeB64Decode($x) + . JWT::urlsafeB64Decode($y) + ) + ); + + return \sprintf( + "-----BEGIN PUBLIC KEY-----\n%s\n-----END PUBLIC KEY-----\n", + wordwrap(base64_encode($pem), 64, "\n", true) + ); + } + /** * Create a public key represented in PEM format from RSA modulus and exponent information * @@ -162,11 +264,9 @@ private static function createPemFromModulusAndExponent( $rsaOID . $rsaPublicKey ); - $rsaPublicKey = "-----BEGIN PUBLIC KEY-----\r\n" . + return "-----BEGIN PUBLIC KEY-----\r\n" . \chunk_split(\base64_encode($rsaPublicKey), 64) . '-----END PUBLIC KEY-----'; - - return $rsaPublicKey; } /** @@ -188,4 +288,68 @@ private static function encodeLength(int $length): string return \pack('Ca*', 0x80 | \strlen($temp), $temp); } + + /** + * Encodes a value into a DER object. + * Also defined in Firebase\JWT\JWT + * + * @param int $type DER tag + * @param string $value the value to encode + * @return string the encoded object + */ + private static function encodeDER(int $type, string $value): string + { + $tag_header = 0; + if ($type === self::ASN1_SEQUENCE) { + $tag_header |= 0x20; + } + + // Type + $der = \chr($tag_header | $type); + + // Length + $der .= \chr(\strlen($value)); + + return $der . $value; + } + + /** + * Encodes a string into a DER-encoded OID. + * + * @param string $oid the OID string + * @return string the binary DER-encoded OID + */ + private static function encodeOID(string $oid): string + { + $octets = explode('.', $oid); + + // Get the first octet + $first = (int) array_shift($octets); + $second = (int) array_shift($octets); + $oid = \chr($first * 40 + $second); + + // Iterate over subsequent octets + foreach ($octets as $octet) { + if ($octet == 0) { + $oid .= \chr(0x00); + continue; + } + $bin = ''; + + while ($octet) { + $bin .= \chr(0x80 | ($octet & 0x7f)); + $octet >>= 7; + } + $bin[0] = $bin[0] & \chr(0x7f); + + // Convert to big endian if necessary + if (pack('V', 65534) == pack('L', 65534)) { + $oid .= strrev($bin); + } else { + $oid .= $bin; + } + } + + return $oid; + } } diff --git a/vendor/firebase/php-jwt/src/JWT.php b/vendor/firebase/php-jwt/src/JWT.php index 9011292..833a415 100644 --- a/vendor/firebase/php-jwt/src/JWT.php +++ b/vendor/firebase/php-jwt/src/JWT.php @@ -55,6 +55,7 @@ class JWT public static $supported_algs = [ 'ES384' => ['openssl', 'SHA384'], 'ES256' => ['openssl', 'SHA256'], + 'ES256K' => ['openssl', 'SHA256'], 'HS256' => ['hash_hmac', 'SHA256'], 'HS384' => ['hash_hmac', 'SHA384'], 'HS512' => ['hash_hmac', 'SHA512'], @@ -68,15 +69,20 @@ class JWT * Decodes a JWT string into a PHP object. * * @param string $jwt The JWT - * @param Key|array $keyOrKeyArray The Key or associative array of key IDs (kid) to Key objects. - * If the algorithm used is asymmetric, this is the public key - * Each Key object contains an algorithm and matching key. - * Supported algorithms are 'ES384','ES256', 'HS256', 'HS384', - * 'HS512', 'RS256', 'RS384', and 'RS512' + * @param Key|ArrayAccess|array $keyOrKeyArray The Key or associative array of key IDs + * (kid) to Key objects. + * If the algorithm used is asymmetric, this is + * the public key. + * Each Key object contains an algorithm and + * matching key. + * Supported algorithms are 'ES384','ES256', + * 'HS256', 'HS384', 'HS512', 'RS256', 'RS384' + * and 'RS512'. + * @param stdClass $headers Optional. Populates stdClass with headers. * * @return stdClass The JWT's payload as a PHP object * - * @throws InvalidArgumentException Provided key/key-array was empty + * @throws InvalidArgumentException Provided key/key-array was empty or malformed * @throws DomainException Provided JWT is malformed * @throws UnexpectedValueException Provided JWT was invalid * @throws SignatureInvalidException Provided JWT was invalid because the signature verification failed @@ -89,7 +95,8 @@ class JWT */ public static function decode( string $jwt, - $keyOrKeyArray + $keyOrKeyArray, + ?stdClass &$headers = null ): stdClass { // Validate JWT $timestamp = \is_null(static::$timestamp) ? \time() : static::$timestamp; @@ -98,7 +105,7 @@ public static function decode( throw new InvalidArgumentException('Key may not be empty'); } $tks = \explode('.', $jwt); - if (\count($tks) != 3) { + if (\count($tks) !== 3) { throw new UnexpectedValueException('Wrong number of segments'); } list($headb64, $bodyb64, $cryptob64) = $tks; @@ -106,6 +113,9 @@ public static function decode( if (null === ($header = static::jsonDecode($headerRaw))) { throw new UnexpectedValueException('Invalid header encoding'); } + if ($headers !== null) { + $headers = $header; + } $payloadRaw = static::urlsafeB64Decode($bodyb64); if (null === ($payload = static::jsonDecode($payloadRaw))) { throw new UnexpectedValueException('Invalid claims encoding'); @@ -132,46 +142,52 @@ public static function decode( // See issue #351 throw new UnexpectedValueException('Incorrect key for this algorithm'); } - if ($header->alg === 'ES256' || $header->alg === 'ES384') { - // OpenSSL expects an ASN.1 DER sequence for ES256/ES384 signatures + if (\in_array($header->alg, ['ES256', 'ES256K', 'ES384'], true)) { + // OpenSSL expects an ASN.1 DER sequence for ES256/ES256K/ES384 signatures $sig = self::signatureToDER($sig); } - if (!self::verify("$headb64.$bodyb64", $sig, $key->getKeyMaterial(), $header->alg)) { + if (!self::verify("{$headb64}.{$bodyb64}", $sig, $key->getKeyMaterial(), $header->alg)) { throw new SignatureInvalidException('Signature verification failed'); } // Check the nbf if it is defined. This is the time that the // token can actually be used. If it's not yet that time, abort. - if (isset($payload->nbf) && $payload->nbf > ($timestamp + static::$leeway)) { - throw new BeforeValidException( - 'Cannot handle token prior to ' . \date(DateTime::ISO8601, $payload->nbf) + if (isset($payload->nbf) && floor($payload->nbf) > ($timestamp + static::$leeway)) { + $ex = new BeforeValidException( + 'Cannot handle token with nbf prior to ' . \date(DateTime::ISO8601, (int) floor($payload->nbf)) ); + $ex->setPayload($payload); + throw $ex; } // Check that this token has been created before 'now'. This prevents // using tokens that have been created for later use (and haven't // correctly used the nbf claim). - if (isset($payload->iat) && $payload->iat > ($timestamp + static::$leeway)) { - throw new BeforeValidException( - 'Cannot handle token prior to ' . \date(DateTime::ISO8601, $payload->iat) + if (!isset($payload->nbf) && isset($payload->iat) && floor($payload->iat) > ($timestamp + static::$leeway)) { + $ex = new BeforeValidException( + 'Cannot handle token with iat prior to ' . \date(DateTime::ISO8601, (int) floor($payload->iat)) ); + $ex->setPayload($payload); + throw $ex; } // Check if this token has expired. if (isset($payload->exp) && ($timestamp - static::$leeway) >= $payload->exp) { - throw new ExpiredException('Expired token'); + $ex = new ExpiredException('Expired token'); + $ex->setPayload($payload); + throw $ex; } return $payload; } /** - * Converts and signs a PHP object or array into a JWT string. + * Converts and signs a PHP array into a JWT string. * * @param array $payload PHP array * @param string|resource|OpenSSLAsymmetricKey|OpenSSLCertificate $key The secret key. - * @param string $alg Supported algorithms are 'ES384','ES256', 'HS256', 'HS384', - * 'HS512', 'RS256', 'RS384', and 'RS512' + * @param string $alg Supported algorithms are 'ES384','ES256', 'ES256K', 'HS256', + * 'HS384', 'HS512', 'RS256', 'RS384', and 'RS512' * @param string $keyId * @param array $head An array with header elements to attach * @@ -184,16 +200,17 @@ public static function encode( array $payload, $key, string $alg, - string $keyId = null, - array $head = null + ?string $keyId = null, + ?array $head = null ): string { - $header = ['typ' => 'JWT', 'alg' => $alg]; + $header = ['typ' => 'JWT']; + if (isset($head)) { + $header = \array_merge($header, $head); + } + $header['alg'] = $alg; if ($keyId !== null) { $header['kid'] = $keyId; } - if (isset($head) && \is_array($head)) { - $header = \array_merge($head, $header); - } $segments = []; $segments[] = static::urlsafeB64Encode((string) static::jsonEncode($header)); $segments[] = static::urlsafeB64Encode((string) static::jsonEncode($payload)); @@ -210,8 +227,8 @@ public static function encode( * * @param string $msg The message to sign * @param string|resource|OpenSSLAsymmetricKey|OpenSSLCertificate $key The secret key. - * @param string $alg Supported algorithms are 'ES384','ES256', 'HS256', 'HS384', - * 'HS512', 'RS256', 'RS384', and 'RS512' + * @param string $alg Supported algorithms are 'EdDSA', 'ES384', 'ES256', 'ES256K', 'HS256', + * 'HS384', 'HS512', 'RS256', 'RS384', and 'RS512' * * @return string An encrypted message * @@ -234,11 +251,14 @@ public static function sign( return \hash_hmac($algorithm, $msg, $key, true); case 'openssl': $signature = ''; + if (!\is_resource($key) && !openssl_pkey_get_private($key)) { + throw new DomainException('OpenSSL unable to validate key'); + } $success = \openssl_sign($msg, $signature, $key, $algorithm); // @phpstan-ignore-line if (!$success) { throw new DomainException('OpenSSL unable to sign data'); } - if ($alg === 'ES256') { + if ($alg === 'ES256' || $alg === 'ES256K') { $signature = self::signatureFromDER($signature, 256); } elseif ($alg === 'ES384') { $signature = self::signatureFromDER($signature, 384); @@ -255,6 +275,9 @@ public static function sign( // The last non-empty line is used as the key. $lines = array_filter(explode("\n", $key)); $key = base64_decode((string) end($lines)); + if (\strlen($key) === 0) { + throw new DomainException('Key cannot be empty string'); + } return sodium_crypto_sign_detached($msg, $key); } catch (Exception $e) { throw new DomainException($e->getMessage(), 0, $e); @@ -270,7 +293,7 @@ public static function sign( * * @param string $msg The original message (header and body) * @param string $signature The original signature - * @param string|resource|OpenSSLAsymmetricKey|OpenSSLCertificate $keyMaterial For HS*, a string key works. for RS*, must be an instance of OpenSSLAsymmetricKey + * @param string|resource|OpenSSLAsymmetricKey|OpenSSLCertificate $keyMaterial For Ed*, ES*, HS*, a string key works. for RS*, must be an instance of OpenSSLAsymmetricKey * @param string $alg The algorithm * * @return bool @@ -293,7 +316,8 @@ private static function verify( $success = \openssl_verify($msg, $signature, $keyMaterial, $algorithm); // @phpstan-ignore-line if ($success === 1) { return true; - } elseif ($success === 0) { + } + if ($success === 0) { return false; } // returns 1 on success, 0 on failure, -1 on error. @@ -301,20 +325,26 @@ private static function verify( 'OpenSSL error: ' . \openssl_error_string() ); case 'sodium_crypto': - if (!\function_exists('sodium_crypto_sign_verify_detached')) { - throw new DomainException('libsodium is not available'); - } - if (!\is_string($keyMaterial)) { - throw new InvalidArgumentException('key must be a string when using EdDSA'); - } - try { - // The last non-empty line is used as the key. - $lines = array_filter(explode("\n", $keyMaterial)); - $key = base64_decode((string) end($lines)); - return sodium_crypto_sign_verify_detached($signature, $msg, $key); - } catch (Exception $e) { - throw new DomainException($e->getMessage(), 0, $e); - } + if (!\function_exists('sodium_crypto_sign_verify_detached')) { + throw new DomainException('libsodium is not available'); + } + if (!\is_string($keyMaterial)) { + throw new InvalidArgumentException('key must be a string when using EdDSA'); + } + try { + // The last non-empty line is used as the key. + $lines = array_filter(explode("\n", $keyMaterial)); + $key = base64_decode((string) end($lines)); + if (\strlen($key) === 0) { + throw new DomainException('Key cannot be empty string'); + } + if (\strlen($signature) === 0) { + throw new DomainException('Signature cannot be empty string'); + } + return sodium_crypto_sign_verify_detached($signature, $msg, $key); + } catch (Exception $e) { + throw new DomainException($e->getMessage(), 0, $e); + } case 'hash_hmac': default: if (!\is_string($keyMaterial)) { @@ -357,15 +387,10 @@ public static function jsonDecode(string $input) */ public static function jsonEncode(array $input): string { - if (PHP_VERSION_ID >= 50400) { - $json = \json_encode($input, \JSON_UNESCAPED_SLASHES); - } else { - // PHP 5.3 only - $json = \json_encode($input); - } + $json = \json_encode($input, \JSON_UNESCAPED_SLASHES); if ($errno = \json_last_error()) { self::handleJsonError($errno); - } elseif ($json === 'null' && $input !== null) { + } elseif ($json === 'null') { throw new DomainException('Null result with non-null input'); } if ($json === false) { @@ -384,13 +409,28 @@ public static function jsonEncode(array $input): string * @throws InvalidArgumentException invalid base64 characters */ public static function urlsafeB64Decode(string $input): string + { + return \base64_decode(self::convertBase64UrlToBase64($input)); + } + + /** + * Convert a string in the base64url (URL-safe Base64) encoding to standard base64. + * + * @param string $input A Base64 encoded string with URL-safe characters (-_ and no padding) + * + * @return string A Base64 encoded string with standard characters (+/) and padding (=), when + * needed. + * + * @see https://www.rfc-editor.org/rfc/rfc4648 + */ + public static function convertBase64UrlToBase64(string $input): string { $remainder = \strlen($input) % 4; if ($remainder) { $padlen = 4 - $remainder; $input .= \str_repeat('=', $padlen); } - return \base64_decode(\strtr($input, '-_', '+/')); + return \strtr($input, '-_', '+/'); } /** @@ -424,14 +464,15 @@ private static function getKey( return $keyOrKeyArray; } + if (empty($kid) && $kid !== '0') { + throw new UnexpectedValueException('"kid" empty, unable to lookup correct key'); + } + if ($keyOrKeyArray instanceof CachedKeySet) { // Skip "isset" check, as this will automatically refresh if not set return $keyOrKeyArray[$kid]; } - if (empty($kid)) { - throw new UnexpectedValueException('"kid" empty, unable to lookup correct key'); - } if (!isset($keyOrKeyArray[$kid])) { throw new UnexpectedValueException('"kid" invalid, unable to lookup correct key'); } @@ -510,7 +551,7 @@ private static function signatureToDER(string $sig): string { // Separate the signature into r-value and s-value $length = max(1, (int) (\strlen($sig) / 2)); - list($r, $s) = \str_split($sig, $length > 0 ? $length : 1); + list($r, $s) = \str_split($sig, $length); // Trim leading zeros $r = \ltrim($r, "\x00"); @@ -610,7 +651,7 @@ private static function readDER(string $der, int $offset = 0): array } // Value - if ($type == self::ASN1_BIT_STRING) { + if ($type === self::ASN1_BIT_STRING) { $pos++; // Skip the first contents octet (padding indicator) $data = \substr($der, $pos, $len - 1); $pos += $len - 1; diff --git a/vendor/firebase/php-jwt/src/JWTExceptionWithPayloadInterface.php b/vendor/firebase/php-jwt/src/JWTExceptionWithPayloadInterface.php new file mode 100644 index 0000000..7933ed6 --- /dev/null +++ b/vendor/firebase/php-jwt/src/JWTExceptionWithPayloadInterface.php @@ -0,0 +1,20 @@ +keyMaterial = $keyMaterial; - $this->algorithm = $algorithm; } /**