Skip to content

Commit 6db032b

Browse files
authored
Merge branch 'main' into consistent-error-text
2 parents 4572cb4 + 27179e1 commit 6db032b

File tree

13 files changed

+223
-50
lines changed

13 files changed

+223
-50
lines changed

.github/workflows/tests.yml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ jobs:
1010
runs-on: ubuntu-latest
1111
strategy:
1212
matrix:
13-
php: [ "7.4", "8.0", "8.1", "8.2", "8.3" ]
13+
php: [ "8.0", "8.1", "8.2", "8.3", "8.4" ]
1414
name: PHP ${{matrix.php }} Unit Test
1515
steps:
1616
- uses: actions/checkout@v2
@@ -35,7 +35,7 @@ jobs:
3535
- name: Setup PHP
3636
uses: shivammathur/setup-php@v2
3737
with:
38-
php-version: "8.2"
38+
php-version: "8.3"
3939
- name: Run Script
4040
run: |
4141
composer global require friendsofphp/php-cs-fixer
@@ -49,9 +49,9 @@ jobs:
4949
- name: Install PHP
5050
uses: shivammathur/setup-php@v2
5151
with:
52-
php-version: '8.2'
52+
php-version: '8.3'
5353
- name: Run Script
5454
run: |
5555
composer install
56-
composer global require phpstan/phpstan
56+
composer global require phpstan/phpstan:~1.10.0
5757
~/.composer/vendor/bin/phpstan analyse

.php-cs-fixer.dist.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,10 @@
1616
'native_function_invocation' => [
1717
'strict' => false
1818
],
19+
'nullable_type_declaration' => [
20+
'syntax' => 'question_mark',
21+
],
22+
'nullable_type_declaration_for_default_null_value' => true,
1923
])
2024
->setFinder(
2125
PhpCsFixer\Finder::create()

CHANGELOG.md

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,33 @@
11
# Changelog
22

3+
## [6.11.0](https://github.com/firebase/php-jwt/compare/v6.10.2...v6.11.0) (2025-01-23)
4+
5+
6+
### Features
7+
8+
* support octet typed JWK ([#587](https://github.com/firebase/php-jwt/issues/587)) ([7cb8a26](https://github.com/firebase/php-jwt/commit/7cb8a265fa81edf2fa6ef8098f5bc5ae573c33ad))
9+
10+
11+
### Bug Fixes
12+
13+
* 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))
14+
15+
## [6.10.2](https://github.com/firebase/php-jwt/compare/v6.10.1...v6.10.2) (2024-11-24)
16+
17+
18+
### Bug Fixes
19+
20+
* Mitigate PHP8.4 deprecation warnings ([#570](https://github.com/firebase/php-jwt/issues/570)) ([76808fa](https://github.com/firebase/php-jwt/commit/76808fa227f3811aa5cdb3bf81233714b799a5b5))
21+
* support php 8.4 ([#583](https://github.com/firebase/php-jwt/issues/583)) ([e3d68b0](https://github.com/firebase/php-jwt/commit/e3d68b044421339443c74199edd020e03fb1887e))
22+
23+
## [6.10.1](https://github.com/firebase/php-jwt/compare/v6.10.0...v6.10.1) (2024-05-18)
24+
25+
26+
### Bug Fixes
27+
28+
* 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))
29+
* ratelimit cache expiration ([#550](https://github.com/firebase/php-jwt/issues/550)) ([dda7250](https://github.com/firebase/php-jwt/commit/dda725033585ece30ff8cae8937320d7e9f18bae))
30+
331
## [6.10.0](https://github.com/firebase/php-jwt/compare/v6.9.0...v6.10.0) (2023-11-28)
432

533

README.md

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ composer require firebase/php-jwt
1717
```
1818

1919
Optionally, install the `paragonie/sodium_compat` package from composer if your
20-
php is < 7.2 or does not have libsodium installed:
20+
php env does not have libsodium installed:
2121

2222
```bash
2323
composer require paragonie/sodium_compat
@@ -48,7 +48,8 @@ $decoded = JWT::decode($jwt, new Key($key, 'HS256'));
4848
print_r($decoded);
4949

5050
// Pass a stdClass in as the third parameter to get the decoded header values
51-
$decoded = JWT::decode($jwt, new Key($key, 'HS256'), $headers = new stdClass());
51+
$headers = new stdClass();
52+
$decoded = JWT::decode($jwt, new Key($key, 'HS256'), $headers);
5253
print_r($headers);
5354

5455
/*
@@ -290,7 +291,7 @@ $jwks = ['keys' => []];
290291

291292
// JWK::parseKeySet($jwks) returns an associative array of **kid** to Firebase\JWT\Key
292293
// objects. Pass this as the second parameter to JWT::decode.
293-
JWT::decode($payload, JWK::parseKeySet($jwks));
294+
JWT::decode($jwt, JWK::parseKeySet($jwks));
294295
```
295296

296297
Using Cached Key Sets
@@ -349,7 +350,7 @@ use InvalidArgumentException;
349350
use UnexpectedValueException;
350351

351352
try {
352-
$decoded = JWT::decode($payload, $keys);
353+
$decoded = JWT::decode($jwt, $keys);
353354
} catch (InvalidArgumentException $e) {
354355
// provided key/key-array is empty or malformed.
355356
} catch (DomainException $e) {
@@ -379,7 +380,7 @@ like this:
379380
use Firebase\JWT\JWT;
380381
use UnexpectedValueException;
381382
try {
382-
$decoded = JWT::decode($payload, $keys);
383+
$decoded = JWT::decode($jwt, $keys);
383384
} catch (LogicException $e) {
384385
// errors having to do with environmental setup or malformed JWT Keys
385386
} catch (UnexpectedValueException $e) {
@@ -394,7 +395,7 @@ instead, you can do the following:
394395

395396
```php
396397
// return type is stdClass
397-
$decoded = JWT::decode($payload, $keys);
398+
$decoded = JWT::decode($jwt, $keys);
398399

399400
// cast to array
400401
$decoded = json_decode(json_encode($decoded), true);

composer.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
],
2121
"license": "BSD-3-Clause",
2222
"require": {
23-
"php": "^7.4||^8.0"
23+
"php": "^8.0"
2424
},
2525
"suggest": {
2626
"paragonie/sodium_compat": "Support EdDSA (Ed25519) signatures when libsodium is not present",
@@ -32,10 +32,10 @@
3232
}
3333
},
3434
"require-dev": {
35-
"guzzlehttp/guzzle": "^6.5||^7.4",
35+
"guzzlehttp/guzzle": "^7.4",
3636
"phpspec/prophecy-phpunit": "^2.0",
3737
"phpunit/phpunit": "^9.5",
38-
"psr/cache": "^1.0||^2.0",
38+
"psr/cache": "^2.0||^3.0",
3939
"psr/http-client": "^1.0",
4040
"psr/http-factory": "^1.0"
4141
}

src/CachedKeySet.php

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -80,9 +80,9 @@ public function __construct(
8080
ClientInterface $httpClient,
8181
RequestFactoryInterface $httpFactory,
8282
CacheItemPoolInterface $cache,
83-
int $expiresAfter = null,
83+
?int $expiresAfter = null,
8484
bool $rateLimit = false,
85-
string $defaultAlg = null
85+
?string $defaultAlg = null
8686
) {
8787
$this->jwksUri = $jwksUri;
8888
$this->httpClient = $httpClient;
@@ -180,7 +180,7 @@ private function keyIdExists(string $keyId): bool
180180
$jwksResponse = $this->httpClient->sendRequest($request);
181181
if ($jwksResponse->getStatusCode() !== 200) {
182182
throw new UnexpectedValueException(
183-
sprintf('HTTP Error: %d %s for URI "%s"',
183+
\sprintf('HTTP Error: %d %s for URI "%s"',
184184
$jwksResponse->getStatusCode(),
185185
$jwksResponse->getReasonPhrase(),
186186
$this->jwksUri,
@@ -212,15 +212,21 @@ private function rateLimitExceeded(): bool
212212
}
213213

214214
$cacheItem = $this->cache->getItem($this->rateLimitCacheKey);
215-
if (!$cacheItem->isHit()) {
216-
$cacheItem->expiresAfter(60); // # of calls are cached each minute
215+
216+
$cacheItemData = [];
217+
if ($cacheItem->isHit() && \is_array($data = $cacheItem->get())) {
218+
$cacheItemData = $data;
217219
}
218220

219-
$callsPerMinute = (int) $cacheItem->get();
221+
$callsPerMinute = $cacheItemData['callsPerMinute'] ?? 0;
222+
$expiry = $cacheItemData['expiry'] ?? new \DateTime('+60 seconds', new \DateTimeZone('UTC'));
223+
220224
if (++$callsPerMinute > $this->maxCallsPerMinute) {
221225
return true;
222226
}
223-
$cacheItem->set($callsPerMinute);
227+
228+
$cacheItem->set(['expiry' => $expiry, 'callsPerMinute' => $callsPerMinute]);
229+
$cacheItem->expiresAt($expiry);
224230
$this->cache->save($cacheItem);
225231
return false;
226232
}

src/JWK.php

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ class JWK
5252
*
5353
* @uses parseKey
5454
*/
55-
public static function parseKeySet(array $jwks, string $defaultAlg = null): array
55+
public static function parseKeySet(array $jwks, ?string $defaultAlg = null): array
5656
{
5757
$keys = [];
5858

@@ -93,7 +93,7 @@ public static function parseKeySet(array $jwks, string $defaultAlg = null): arra
9393
*
9494
* @uses createPemFromModulusAndExponent
9595
*/
96-
public static function parseKey(array $jwk, string $defaultAlg = null): ?Key
96+
public static function parseKey(array $jwk, ?string $defaultAlg = null): ?Key
9797
{
9898
if (empty($jwk)) {
9999
throw new InvalidArgumentException('JWK must not be empty');
@@ -172,6 +172,12 @@ public static function parseKey(array $jwk, string $defaultAlg = null): ?Key
172172
// This library works internally with EdDSA keys (Ed25519) encoded in standard base64.
173173
$publicKey = JWT::convertBase64urlToBase64($jwk['x']);
174174
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']);
175181
default:
176182
break;
177183
}
@@ -212,7 +218,7 @@ private static function createPemFromCrvAndXYCoordinates(string $crv, string $x,
212218
)
213219
);
214220

215-
return sprintf(
221+
return \sprintf(
216222
"-----BEGIN PUBLIC KEY-----\n%s\n-----END PUBLIC KEY-----\n",
217223
wordwrap(base64_encode($pem), 64, "\n", true)
218224
);

src/JWT.php

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ class JWT
9696
public static function decode(
9797
string $jwt,
9898
$keyOrKeyArray,
99-
stdClass &$headers = null
99+
?stdClass &$headers = null
100100
): stdClass {
101101
// Validate JWT
102102
$timestamp = \is_null(static::$timestamp) ? \time() : static::$timestamp;
@@ -200,11 +200,11 @@ public static function encode(
200200
array $payload,
201201
$key,
202202
string $alg,
203-
string $keyId = null,
204-
array $head = null
203+
?string $keyId = null,
204+
?array $head = null
205205
): string {
206206
$header = ['typ' => 'JWT'];
207-
if (isset($head) && \is_array($head)) {
207+
if (isset($head)) {
208208
$header = \array_merge($header, $head);
209209
}
210210
$header['alg'] = $alg;
@@ -251,6 +251,9 @@ public static function sign(
251251
return \hash_hmac($algorithm, $msg, $key, true);
252252
case 'openssl':
253253
$signature = '';
254+
if (!\is_resource($key) && !openssl_pkey_get_private($key)) {
255+
throw new DomainException('OpenSSL unable to validate key');
256+
}
254257
$success = \openssl_sign($msg, $signature, $key, $algorithm); // @phpstan-ignore-line
255258
if (!$success) {
256259
throw new DomainException('OpenSSL unable to sign data');
@@ -384,12 +387,7 @@ public static function jsonDecode(string $input)
384387
*/
385388
public static function jsonEncode(array $input): string
386389
{
387-
if (PHP_VERSION_ID >= 50400) {
388-
$json = \json_encode($input, \JSON_UNESCAPED_SLASHES);
389-
} else {
390-
// PHP 5.3 only
391-
$json = \json_encode($input);
392-
}
390+
$json = \json_encode($input, \JSON_UNESCAPED_SLASHES);
393391
if ($errno = \json_last_error()) {
394392
self::handleJsonError($errno);
395393
} elseif ($json === 'null') {

src/Key.php

Lines changed: 2 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -9,18 +9,13 @@
99

1010
class Key
1111
{
12-
/** @var string|resource|OpenSSLAsymmetricKey|OpenSSLCertificate */
13-
private $keyMaterial;
14-
/** @var string */
15-
private $algorithm;
16-
1712
/**
1813
* @param string|resource|OpenSSLAsymmetricKey|OpenSSLCertificate $keyMaterial
1914
* @param string $algorithm
2015
*/
2116
public function __construct(
22-
$keyMaterial,
23-
string $algorithm
17+
private $keyMaterial,
18+
private string $algorithm
2419
) {
2520
if (
2621
!\is_string($keyMaterial)
@@ -38,10 +33,6 @@ public function __construct(
3833
if (empty($algorithm)) {
3934
throw new InvalidArgumentException('Algorithm must not be empty');
4035
}
41-
42-
// TODO: Remove in PHP 8.0 in favor of class constructor property promotion
43-
$this->keyMaterial = $keyMaterial;
44-
$this->algorithm = $algorithm;
4536
}
4637

4738
/**

0 commit comments

Comments
 (0)