From 347a9693c1292f403fad65bb2b1d879e17838c4e Mon Sep 17 00:00:00 2001 From: Marwin Sorce Date: Thu, 25 Apr 2024 13:07:49 +0200 Subject: [PATCH] Add token algorithm encryption configuration support (#113) * Add token algorithm encryption configuration support * Add test on encryption token algorithm configuration --------- Co-authored-by: m.sorce --- README.md | 8 +++++++- config/keycloak.php | 2 ++ src/KeycloakGuard.php | 2 +- src/Token.php | 4 ++-- tests/AuthenticateTest.php | 16 ++++++++++++++++ tests/TestCase.php | 22 +++++++++++++--------- 6 files changed, 41 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index 1939fea..b4a6d9c 100644 --- a/README.md +++ b/README.md @@ -94,7 +94,13 @@ _Required._ The Keycloak Server realm public key (string). -> How to get realm public key? Click on "Realm Settings" > "Keys" > "Algorithm RS256" Line > "Public Key" Button +> How to get realm public key? Click on "Realm Settings" > "Keys" > "Algorithm RS256 (or defined under token_encryption_algorithm configuration)" Line > "Public Key" Button + +✔️ **token_encryption_algorithm** + +_Default is `RS256`._ + +The JWT token encryption algorithm used by Keycloak (string). ✔️ **load_user_from_database** diff --git a/config/keycloak.php b/config/keycloak.php index c733553..38f98cd 100644 --- a/config/keycloak.php +++ b/config/keycloak.php @@ -3,6 +3,8 @@ return [ 'realm_public_key' => env('KEYCLOAK_REALM_PUBLIC_KEY', null), + 'token_encryption_algorithm' => env('KEYCLOAK_TOKEN_ENCRYPTION_ALGORITHM', 'RS256'), + 'load_user_from_database' => env('KEYCLOAK_LOAD_USER_FROM_DATABASE', true), 'user_provider_custom_retrieve_method' => null, diff --git a/src/KeycloakGuard.php b/src/KeycloakGuard.php index 1a06cec..7cc293c 100644 --- a/src/KeycloakGuard.php +++ b/src/KeycloakGuard.php @@ -37,7 +37,7 @@ public function __construct(UserProvider $provider, Request $request) protected function authenticate() { try { - $this->decodedToken = Token::decode($this->getTokenForRequest(), $this->config['realm_public_key'], $this->config['leeway']); + $this->decodedToken = Token::decode($this->getTokenForRequest(), $this->config['realm_public_key'], $this->config['leeway'], $this->config['token_encryption_algorithm']); } catch (\Exception $e) { throw new TokenException($e->getMessage()); } diff --git a/src/Token.php b/src/Token.php index b6aa25a..0ab74b8 100644 --- a/src/Token.php +++ b/src/Token.php @@ -14,12 +14,12 @@ class Token * @param string $publicKey * @return mixed|null */ - public static function decode(string $token = null, string $publicKey, int $leeway = 0) + public static function decode(string $token = null, string $publicKey, int $leeway = 0, string $algorithm = 'RS256') { JWT::$leeway = $leeway; $publicKey = self::buildPublicKey($publicKey); - return $token ? JWT::decode($token, new Key($publicKey, 'RS256')) : null; + return $token ? JWT::decode($token, new Key($publicKey, $algorithm)) : null; } /** diff --git a/tests/AuthenticateTest.php b/tests/AuthenticateTest.php index 032a795..609c8b1 100644 --- a/tests/AuthenticateTest.php +++ b/tests/AuthenticateTest.php @@ -12,6 +12,7 @@ use KeycloakGuard\KeycloakGuard; use KeycloakGuard\Tests\Extensions\CustomUserProvider; use KeycloakGuard\Tests\Models\User; +use KeycloakGuard\Token; class AuthenticateTest extends TestCase { @@ -425,4 +426,19 @@ public function test_acting_as_keycloak_user_trait_without_user() $this->assertFalse(Auth::guest()); } + public function test_it_decodes_token_with_the_configured_encryption_algorithm() + { + $this->prepareCredentials('ES256', [ + 'private_key_type' => OPENSSL_KEYTYPE_EC, + 'curve_name' => 'prime256v1' + ]); + + config([ + 'keycloak.token_encryption_algorithm' => 'ES256', + 'keycloak.realm_public_key' => Token::plainPublicKey($this->publicKey) + ]); + + $this->withKeycloakToken()->json('GET', '/foo/secret'); + $this->assertEquals($this->user->username, Auth::user()->username); + } } diff --git a/tests/TestCase.php b/tests/TestCase.php index e67a833..6429b3e 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -38,14 +38,18 @@ protected function setUp(): void ]); } - protected function prepareCredentials() + protected function prepareCredentials(string $encryptionAlgorithm = 'RS256', ?array $openSSLConfig = null) { // Prepare private/public keys and a default JWT token, with a simple payload - $this->privateKey = openssl_pkey_new([ - 'digest_alg' => 'sha256', - 'private_key_bits' => 1024, - 'private_key_type' => OPENSSL_KEYTYPE_RSA - ]); + if (!$openSSLConfig) { + $openSSLConfig = [ + 'digest_alg' => 'sha256', + 'private_key_bits' => 1024, + 'private_key_type' => OPENSSL_KEYTYPE_RSA + ]; + } + + $this->privateKey = openssl_pkey_new($openSSLConfig); $this->publicKey = openssl_pkey_get_details($this->privateKey)['key']; @@ -54,7 +58,7 @@ protected function prepareCredentials() 'resource_access' => ['myapp-backend' => []] ]; - $this->token = JWT::encode($this->payload, $this->privateKey, 'RS256'); + $this->token = JWT::encode($this->payload, $this->privateKey, $encryptionAlgorithm); } // Default configs to make it running @@ -96,11 +100,11 @@ protected function getPackageProviders($app) } // Build a different token with custom payload - protected function buildCustomToken(array $payload) + protected function buildCustomToken(array $payload, string $encryptionAlgorithm = 'RS256') { $payload = array_replace($this->payload, $payload); - $this->token = JWT::encode($payload, $this->privateKey, 'RS256'); + $this->token = JWT::encode($payload, $this->privateKey, $encryptionAlgorithm); } // Setup default token, for the default user