From 2c25fc231a6a961d26b565d93bd65b0654146ab7 Mon Sep 17 00:00:00 2001 From: James Read Date: Sun, 17 Dec 2023 17:43:06 +0800 Subject: [PATCH] Updating docs to explain change in behavour Updatig the readme to explain whe the KID is now needed when passing when decodig the token also why the token need the KID to be set in the header --- README.md | 13 +++++++++++-- src/JwtAuthentication.php | 5 +++-- tests/JwtAuthenticationTest.php | 27 +++++++++++++++++++++++++++ 3 files changed, 41 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index e4152cb..a12e7a4 100644 --- a/README.md +++ b/README.md @@ -127,17 +127,26 @@ $app->add(new Tuupola\Middleware\JwtAuthentication([ ### Algorithm -You can set supported algorithms via `algorithm` parameter. This can be either string or array of strings. Default value is `["HS256", "HS512", "HS384"]`. Supported algorithms are `HS256`, `HS384`, `HS512` and `RS256`. Note that enabling both `HS256` and `RS256` is a [security risk](https://auth0.com/blog/critical-vulnerabilities-in-json-web-token-libraries/). +You can set supported algorithms via `algorithm` parameter. This can be either string or array of strings. Default value is `["HS256"]`. Supported algorithms are `HS256`, `HS384`, `HS512` and `RS256`. Note that enabling both `HS256` and `RS256` is a [security risk](https://auth0.com/blog/critical-vulnerabilities-in-json-web-token-libraries/). + +When passing multiple algorithm it be a key value array, with the key being the `KID` of the jwt. ``` php $app = new Slim\App; $app->add(new Tuupola\Middleware\JwtAuthentication([ "secret" => "supersecretkeyyoushouldnotcommittogithub", - "algorithm" => ["HS256", "HS384"] + "algorithm" => [ + "amce" => "HS256", + "beta" => "HS384" + ] ])); ``` +> :warning: **Warning**:
+Because of changes in `firebase/php-jwt` the `kid` is now checked when multiple algorithm are passed, failing to provide a key the algorithm will be used for the kid. +this also means the `kid` will now need to be present in the JWT header as well. + ### Attribute When the token is decoded successfully and authentication succeeds the contents of the decoded token is saved as `token` attribute to the `$request` object. You can change this with. `attribute` parameter. Set to `null` or `false` to disable this behavour diff --git a/src/JwtAuthentication.php b/src/JwtAuthentication.php index 444ddcb..0faab67 100644 --- a/src/JwtAuthentication.php +++ b/src/JwtAuthentication.php @@ -79,7 +79,7 @@ final class JwtAuthentication implements MiddlewareInterface * Stores all the options passed to the middleware. * * @var array{ - * secret?: string|array, + * secret?: string|array|array, * secure: bool, * relaxed: array, * algorithm: array, @@ -321,7 +321,8 @@ private function decodeToken(string $token): array try { $decoded = JWT::decode( $token, - $keys + $keys, + $this->options['algorithm'] ); return (array) $decoded; } catch (Exception $exception) { diff --git a/tests/JwtAuthenticationTest.php b/tests/JwtAuthenticationTest.php index 3e0e925..9f232f6 100644 --- a/tests/JwtAuthenticationTest.php +++ b/tests/JwtAuthenticationTest.php @@ -220,6 +220,33 @@ public function testShouldReturn200WithSecretArray(): void $this->assertEquals("Success", $response->getBody()); } + public function testShouldReturn200WithSecretArrayCheckKid(): void + { + $request = (new ServerRequestFactory) + ->createServerRequest("GET", "https://example.com/api") + ->withHeader("Authorization", "Bearer " . self::$betaToken); + + $default = function (ServerRequestInterface $request) { + $response = (new ResponseFactory)->createResponse(); + $response->getBody()->write("Success"); + return $response; + }; + + $collection = new MiddlewareCollection([ + new JwtAuthentication([ + "algorithm" => ["acme" => "HS256", "beta" => "HS256"], + "secret" => [ + "acme" =>"supersecretkeyyoushouldnotcommittogithub", + "beta" =>"anothersecretkeyfornevertocommittogithub" + ], + ]) + ]); + + $response = $collection->dispatch($request, $default); + $this->assertEquals(200, $response->getStatusCode()); + $this->assertEquals("Success", $response->getBody()); + } + public function testShouldReturn401WithSecretArray(): void { $request = (new ServerRequestFactory)