From 415ff35be6a2ddabfb2c16391ebd073b1cca13ca Mon Sep 17 00:00:00 2001 From: Daniel Ziegenberg Date: Fri, 31 Jan 2025 23:29:23 +0100 Subject: [PATCH] MDL-84164 libaries: Upgrade PHP-JWT to 6.11.0 The library raises the minimum required PHP version to 8.0 and comes with an advertised PHP 8.4 compatibility. Signed-off-by: Daniel Ziegenberg --- lib/php-jwt/CHANGELOG.md | 28 ++++++++++++++++++++++++++++ lib/php-jwt/README.md | 5 +++-- lib/php-jwt/composer.json | 6 +++--- lib/php-jwt/readme_moodle.txt | 13 +++++++------ lib/php-jwt/src/CachedKeySet.php | 20 +++++++++++++------- lib/php-jwt/src/JWK.php | 12 +++++++++--- lib/php-jwt/src/JWT.php | 18 ++++++++---------- lib/php-jwt/src/Key.php | 13 ++----------- lib/thirdpartylibs.xml | 2 +- 9 files changed, 74 insertions(+), 43 deletions(-) diff --git a/lib/php-jwt/CHANGELOG.md b/lib/php-jwt/CHANGELOG.md index 644fa0beafa..01fcc077414 100644 --- a/lib/php-jwt/CHANGELOG.md +++ b/lib/php-jwt/CHANGELOG.md @@ -1,5 +1,33 @@ # Changelog +## [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) diff --git a/lib/php-jwt/README.md b/lib/php-jwt/README.md index 701de23a828..04252693ca9 100644 --- a/lib/php-jwt/README.md +++ b/lib/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 @@ -48,7 +48,8 @@ $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 -$decoded = JWT::decode($jwt, new Key($key, 'HS256'), $headers = new stdClass()); +$headers = new stdClass(); +$decoded = JWT::decode($jwt, new Key($key, 'HS256'), $headers); print_r($headers); /* diff --git a/lib/php-jwt/composer.json b/lib/php-jwt/composer.json index e23dfe378d8..816cfd0bd9a 100644 --- a/lib/php-jwt/composer.json +++ b/lib/php-jwt/composer.json @@ -20,7 +20,7 @@ ], "license": "BSD-3-Clause", "require": { - "php": "^7.4||^8.0" + "php": "^8.0" }, "suggest": { "paragonie/sodium_compat": "Support EdDSA (Ed25519) signatures when libsodium is not present", @@ -32,10 +32,10 @@ } }, "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" } diff --git a/lib/php-jwt/readme_moodle.txt b/lib/php-jwt/readme_moodle.txt index f454f109324..e12cdcc6e92 100644 --- a/lib/php-jwt/readme_moodle.txt +++ b/lib/php-jwt/readme_moodle.txt @@ -7,10 +7,11 @@ Dependencies Instructions ------------ -1. Check dependencies to confirm suitability of the new version of the library (see above). -2. Visit [https://github.com/firebase/php-jwt]. -3. Click on 'X releases'. -4. Download the latest release. -5. Unzip it in lib as php-jwt. -6. Update entry for this library in lib/thirdpartylibs.xml. +1. Check dependencies to confirm suitability of the new version of the library (see above). +2. Visit [https://github.com/firebase/php-jwt]. +3. Click on 'X releases'. +4. Download the latest release. +5. Remove everything under lib/php-jwt/ except this file (readme_moodle.txt). +6. Unzip the release and put its content as into lib/php-jwt. +7. Update entry for this library in lib/thirdpartylibs.xml. diff --git a/lib/php-jwt/src/CachedKeySet.php b/lib/php-jwt/src/CachedKeySet.php index ee529f9f50a..8e8e8d68cae 100644 --- a/lib/php-jwt/src/CachedKeySet.php +++ b/lib/php-jwt/src/CachedKeySet.php @@ -80,9 +80,9 @@ class CachedKeySet implements ArrayAccess 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; @@ -180,7 +180,7 @@ class CachedKeySet implements ArrayAccess $jwksResponse = $this->httpClient->sendRequest($request); if ($jwksResponse->getStatusCode() !== 200) { throw new UnexpectedValueException( - sprintf('HTTP Error: %d %s for URI "%s"', + \sprintf('HTTP Error: %d %s for URI "%s"', $jwksResponse->getStatusCode(), $jwksResponse->getReasonPhrase(), $this->jwksUri, @@ -212,15 +212,21 @@ class CachedKeySet implements ArrayAccess } $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/lib/php-jwt/src/JWK.php b/lib/php-jwt/src/JWK.php index 63fb2484b32..405dcc49b70 100644 --- a/lib/php-jwt/src/JWK.php +++ b/lib/php-jwt/src/JWK.php @@ -52,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 = []; @@ -93,7 +93,7 @@ class JWK * * @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'); @@ -172,6 +172,12 @@ class JWK // 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: break; } @@ -212,7 +218,7 @@ class JWK ) ); - return sprintf( + return \sprintf( "-----BEGIN PUBLIC KEY-----\n%s\n-----END PUBLIC KEY-----\n", wordwrap(base64_encode($pem), 64, "\n", true) ); diff --git a/lib/php-jwt/src/JWT.php b/lib/php-jwt/src/JWT.php index 263492068cb..dd9292a4314 100644 --- a/lib/php-jwt/src/JWT.php +++ b/lib/php-jwt/src/JWT.php @@ -96,7 +96,7 @@ class JWT public static function decode( string $jwt, $keyOrKeyArray, - stdClass &$headers = null + ?stdClass &$headers = null ): stdClass { // Validate JWT $timestamp = \is_null(static::$timestamp) ? \time() : static::$timestamp; @@ -200,11 +200,11 @@ class JWT array $payload, $key, string $alg, - string $keyId = null, - array $head = null + ?string $keyId = null, + ?array $head = null ): string { $header = ['typ' => 'JWT']; - if (isset($head) && \is_array($head)) { + if (isset($head)) { $header = \array_merge($header, $head); } $header['alg'] = $alg; @@ -251,6 +251,9 @@ class JWT 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'); @@ -384,12 +387,7 @@ class JWT */ 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') { diff --git a/lib/php-jwt/src/Key.php b/lib/php-jwt/src/Key.php index 00cf7f2edf3..b34eae25858 100644 --- a/lib/php-jwt/src/Key.php +++ b/lib/php-jwt/src/Key.php @@ -9,18 +9,13 @@ use TypeError; class Key { - /** @var string|resource|OpenSSLAsymmetricKey|OpenSSLCertificate */ - private $keyMaterial; - /** @var string */ - private $algorithm; - /** * @param string|resource|OpenSSLAsymmetricKey|OpenSSLCertificate $keyMaterial * @param string $algorithm */ public function __construct( - $keyMaterial, - string $algorithm + private $keyMaterial, + private string $algorithm ) { if ( !\is_string($keyMaterial) @@ -38,10 +33,6 @@ class Key if (empty($algorithm)) { throw new InvalidArgumentException('Algorithm must not be empty'); } - - // TODO: Remove in PHP 8.0 in favor of class constructor property promotion - $this->keyMaterial = $keyMaterial; - $this->algorithm = $algorithm; } /** diff --git a/lib/thirdpartylibs.xml b/lib/thirdpartylibs.xml index 56cc1e89214..349c0603c81 100644 --- a/lib/thirdpartylibs.xml +++ b/lib/thirdpartylibs.xml @@ -515,7 +515,7 @@ All rights reserved. php-jwt PHP-JWT A simple library to encode and decode JSON Web Tokens (JWT) in PHP, conforming to RFC 7519 - 6.10.0 + 6.11.0 BSD 3-Clause https://github.com/firebase/php-jwt