From 597abe18c3021525710259f58f1de57e6b95fe48 Mon Sep 17 00:00:00 2001 From: Joe Grandja Date: Thu, 30 Mar 2023 12:30:01 -0400 Subject: [PATCH] Ensure ID Token is active before processing logout request Issue gh-1077 --- .../OidcLogoutAuthenticationProvider.java | 7 +++- ...OidcLogoutAuthenticationProviderTests.java | 35 +++++++++++++++++++ 2 files changed, 41 insertions(+), 1 deletion(-) diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/oidc/authentication/OidcLogoutAuthenticationProvider.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/oidc/authentication/OidcLogoutAuthenticationProvider.java index 3fa3c459..c680781f 100644 --- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/oidc/authentication/OidcLogoutAuthenticationProvider.java +++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/oidc/authentication/OidcLogoutAuthenticationProvider.java @@ -91,6 +91,11 @@ public final class OidcLogoutAuthenticationProvider implements AuthenticationPro this.logger.trace("Retrieved authorization with ID Token"); } + OAuth2Authorization.Token authorizedIdToken = authorization.getToken(OidcIdToken.class); + if (!authorizedIdToken.isActive()) { + throwError(OAuth2ErrorCodes.INVALID_TOKEN, "id_token_hint"); + } + RegisteredClient registeredClient = this.registeredClientRepository.findById( authorization.getRegisteredClientId()); @@ -98,7 +103,7 @@ public final class OidcLogoutAuthenticationProvider implements AuthenticationPro this.logger.trace("Retrieved registered client"); } - OidcIdToken idToken = authorization.getToken(OidcIdToken.class).getToken(); + OidcIdToken idToken = authorizedIdToken.getToken(); // Validate client identity List audClaim = idToken.getAudience(); diff --git a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/oidc/authentication/OidcLogoutAuthenticationProviderTests.java b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/oidc/authentication/OidcLogoutAuthenticationProviderTests.java index bcb00451..e5866514 100644 --- a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/oidc/authentication/OidcLogoutAuthenticationProviderTests.java +++ b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/oidc/authentication/OidcLogoutAuthenticationProviderTests.java @@ -129,6 +129,41 @@ public class OidcLogoutAuthenticationProviderTests { eq(authentication.getIdTokenHint()), eq(ID_TOKEN_TOKEN_TYPE)); } + @Test + public void authenticateWhenIdTokenNotActiveThenThrowOAuth2AuthenticationException() { + TestingAuthenticationToken principal = new TestingAuthenticationToken("principal", "credentials"); + RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build(); + OidcIdToken idToken = OidcIdToken.withTokenValue("id-token") + .issuer("https://provider.com") + .subject(principal.getName()) + .issuedAt(Instant.now().minusSeconds(60).truncatedTo(ChronoUnit.MILLIS)) + .expiresAt(Instant.now().plusSeconds(60).truncatedTo(ChronoUnit.MILLIS)) + .build(); + OAuth2Authorization authorization = TestOAuth2Authorizations.authorization(registeredClient) + .principalName(principal.getName()) + .token(idToken, (metadata) -> { + metadata.put(OAuth2Authorization.Token.CLAIMS_METADATA_NAME, idToken.getClaims()); + metadata.put(OAuth2Authorization.Token.INVALIDATED_METADATA_NAME, true); + }) + .build(); + when(this.authorizationService.findByToken(eq(idToken.getTokenValue()), eq(ID_TOKEN_TOKEN_TYPE))) + .thenReturn(authorization); + + OidcLogoutAuthenticationToken authentication = new OidcLogoutAuthenticationToken( + idToken.getTokenValue(), principal, "session-1", null, null, null); + + assertThatThrownBy(() -> this.authenticationProvider.authenticate(authentication)) + .isInstanceOf(OAuth2AuthenticationException.class) + .extracting(ex -> ((OAuth2AuthenticationException) ex).getError()) + .satisfies(error -> { + assertThat(error.getErrorCode()).isEqualTo(OAuth2ErrorCodes.INVALID_TOKEN); + assertThat(error.getDescription()).contains("id_token_hint"); + }); + + verify(this.authorizationService).findByToken( + eq(authentication.getIdTokenHint()), eq(ID_TOKEN_TOKEN_TYPE)); + } + @Test public void authenticateWhenMissingAudienceThenThrowOAuth2AuthenticationException() { TestingAuthenticationToken principal = new TestingAuthenticationToken("principal", "credentials");