From 6ab8afb4e4ec1bf61219a411924c8e0da492e657 Mon Sep 17 00:00:00 2001 From: Maarten Date: Wed, 16 Jul 2025 17:06:33 +0200 Subject: [PATCH] add endSessionEndpoint to configuration to support ending the session --- .../OpenIDConfiguration.php | 1 + .../OpenIDConfigurationLoader.php | 3 +- .../LoginControllerResponseTest.php | 1 + .../Http/Controllers/LoginControllerTest.php | 1 + .../OpenIDConfigurationLoaderTest.php | 8 +++- tests/Feature/OpenIDConnectClientTest.php | 40 +++++++++++++++++++ 6 files changed, 52 insertions(+), 2 deletions(-) create mode 100644 tests/Feature/OpenIDConnectClientTest.php diff --git a/src/OpenIDConfiguration/OpenIDConfiguration.php b/src/OpenIDConfiguration/OpenIDConfiguration.php index 35f6b51..5a62ee1 100644 --- a/src/OpenIDConfiguration/OpenIDConfiguration.php +++ b/src/OpenIDConfiguration/OpenIDConfiguration.php @@ -58,6 +58,7 @@ public function __construct( public array $idTokenSigningAlgValuesSupported = [], public string $userinfoEndpoint = '', public array $codeChallengeMethodsSupported = [], + public string $endSessionEndpoint = '', ) { } } diff --git a/src/OpenIDConfiguration/OpenIDConfigurationLoader.php b/src/OpenIDConfiguration/OpenIDConfigurationLoader.php index d24a2fe..1154b75 100644 --- a/src/OpenIDConfiguration/OpenIDConfigurationLoader.php +++ b/src/OpenIDConfiguration/OpenIDConfigurationLoader.php @@ -91,7 +91,8 @@ protected function getConfigurationFromIssuer(): OpenIDConfiguration subjectTypesSupported: $response->json('subject_types_supported', []), idTokenSigningAlgValuesSupported: $response->json('id_token_signing_alg_values_supported', []), userinfoEndpoint: $response->json('userinfo_endpoint', ''), - codeChallengeMethodsSupported: $response->json('code_challenge_methods_supported', []) + codeChallengeMethodsSupported: $response->json('code_challenge_methods_supported', []), + endSessionEndpoint: $response->json('end_session_endpoint', ''), ); } diff --git a/tests/Feature/Http/Controllers/LoginControllerResponseTest.php b/tests/Feature/Http/Controllers/LoginControllerResponseTest.php index 669e78c..3b38e96 100644 --- a/tests/Feature/Http/Controllers/LoginControllerResponseTest.php +++ b/tests/Feature/Http/Controllers/LoginControllerResponseTest.php @@ -620,6 +620,7 @@ protected function exampleOpenIDConfiguration( idTokenSigningAlgValuesSupported: ["RS256"], userinfoEndpoint: "https://provider.example.com/userinfo", codeChallengeMethodsSupported: $codeChallengeMethodsSupported, + endSessionEndpoint: "https://provider.example.com/endSession", ); } diff --git a/tests/Feature/Http/Controllers/LoginControllerTest.php b/tests/Feature/Http/Controllers/LoginControllerTest.php index 5133e09..2631c99 100644 --- a/tests/Feature/Http/Controllers/LoginControllerTest.php +++ b/tests/Feature/Http/Controllers/LoginControllerTest.php @@ -149,6 +149,7 @@ protected function exampleOpenIDConfiguration(): OpenIDConfiguration idTokenSigningAlgValuesSupported: ["RS256"], userinfoEndpoint: "https://provider.example.com/userinfo", codeChallengeMethodsSupported: ["S256"], + endSessionEndpoint: "https://provider.example.com/endSession", ); } } diff --git a/tests/Feature/OpenIDConfiguration/OpenIDConfigurationLoaderTest.php b/tests/Feature/OpenIDConfiguration/OpenIDConfigurationLoaderTest.php index 251d3dc..0ba9880 100644 --- a/tests/Feature/OpenIDConfiguration/OpenIDConfigurationLoaderTest.php +++ b/tests/Feature/OpenIDConfiguration/OpenIDConfigurationLoaderTest.php @@ -38,6 +38,7 @@ public function testConfigurationIsLoaded(): void $this->assertSame("https://provider.example.com/jwks", $configuration->jwksUri); $this->assertSame("https://provider.example.com/token", $configuration->tokenEndpoint); $this->assertSame("https://provider.example.com/userinfo", $configuration->userinfoEndpoint); + $this->assertSame("https://provider.example.com/logout", $configuration->endSessionEndpoint); } public function testConfigurationIsLoadedMultipleTimesWhenNotCached(): void @@ -60,6 +61,7 @@ public function testConfigurationIsLoadedMultipleTimesWhenNotCached(): void $this->assertSame("https://provider.example.com/jwks", $configuration->jwksUri); $this->assertSame("https://provider.example.com/token", $configuration->tokenEndpoint); $this->assertSame("https://provider.example.com/userinfo", $configuration->userinfoEndpoint); + $this->assertSame("https://provider.example.com/logout", $configuration->endSessionEndpoint); } public function testConfigurationIsCached(): void @@ -87,6 +89,7 @@ public function testConfigurationIsCached(): void $this->assertSame("https://provider.example.com/jwks", $configuration->jwksUri); $this->assertSame("https://provider.example.com/token", $configuration->tokenEndpoint); $this->assertSame("https://provider.example.com/userinfo", $configuration->userinfoEndpoint); + $this->assertSame("https://provider.example.com/logout", $configuration->endSessionEndpoint); } public function testLoaderThrowsExceptionWhenProviderReturns400ResponseCode(): void @@ -198,6 +201,7 @@ public function testLoaderReturnsEmptyConfigurationOnEmptyJsonResponse(): void $this->assertEmpty($configuration->authorizationEndpoint); $this->assertEmpty($configuration->jwksUri); $this->assertEmpty($configuration->tokenEndpoint); + $this->assertEmpty($configuration->userinfoEndpoint); } public function testConfigurationIsLoadedMultipleTimesWhenCacheStoreIsNull(): void @@ -221,6 +225,7 @@ public function testConfigurationIsLoadedMultipleTimesWhenCacheStoreIsNull(): vo $this->assertSame("https://provider.example.com/jwks", $configuration->jwksUri); $this->assertSame("https://provider.example.com/token", $configuration->tokenEndpoint); $this->assertSame("https://provider.example.com/userinfo", $configuration->userinfoEndpoint); + $this->assertSame("https://provider.example.com/logout", $configuration->endSessionEndpoint); } protected function fakeSuccessfulResponse(): void @@ -264,7 +269,8 @@ protected function fakeSuccessfulResponse(): void ], "code_challenge_methods_supported" => [ "S256" - ] + ], + "end_session_endpoint" => "https://provider.example.com/logout" ]) ]); } diff --git a/tests/Feature/OpenIDConnectClientTest.php b/tests/Feature/OpenIDConnectClientTest.php new file mode 100644 index 0000000..f4861e4 --- /dev/null +++ b/tests/Feature/OpenIDConnectClientTest.php @@ -0,0 +1,40 @@ + Http::response([ + "end_session_endpoint" => "https://provider.example.com/logout", + ]) + ]); + Config::set('oidc.issuer', 'https://provider.example.com'); + Config::set('oidc.configuration_cache.store', null); + + $client = app(OpenIDConnectClient::class); + + try { + $client->signOut('idToken', 'redirect'); + } catch (HttpResponseException $e) { + $this->assertEquals(Response::HTTP_FOUND, $e->getResponse()->getStatusCode()); + $this->assertEquals( + 'https://provider.example.com/logout?id_token_hint=idToken&post_logout_redirect_uri=redirect', + $e->getResponse()->getTargetUrl() + ); + + return; + } + } +}