diff --git a/docs/modules/ROOT/pages/protocol-endpoints.adoc b/docs/modules/ROOT/pages/protocol-endpoints.adoc index 778365e0..4bdc9abf 100644 --- a/docs/modules/ROOT/pages/protocol-endpoints.adoc +++ b/docs/modules/ROOT/pages/protocol-endpoints.adoc @@ -557,15 +557,62 @@ public SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity h [[oidc-logout-endpoint-customizing-logout-request-validation]] === Customizing Logout Request Validation -`OidcLogoutAuthenticationValidator` is the default validator used for validating specific OpenID Connect Logout request parameters used in the RP-Initiated Logout flow. +`OidcLogoutAuthenticationValidator` is the default validator used for validating specific OpenID Connect RP-Initiated Logout Request parameters. The default implementation validates the `post_logout_redirect_uri` parameter. If validation fails, an `OAuth2AuthenticationException` is thrown. `OidcLogoutAuthenticationProvider` provides the ability to override the default logout request validation by supplying a custom authentication validator of type `Consumer` to `setAuthenticationValidator()`. +[TIP] +`OidcLogoutAuthenticationContext` holds the `OidcLogoutAuthenticationToken`, which contains the logout request parameters. + [IMPORTANT] If validation fails, the authentication validator *MUST* throw `OAuth2AuthenticationException`. +The following example shows how to configure `OidcLogoutAuthenticationProvider` with a custom authentication validator: + +[source,java] +---- +@Bean +public SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http) throws Exception { + OAuth2AuthorizationServerConfigurer authorizationServerConfigurer = + new OAuth2AuthorizationServerConfigurer(); + http.apply(authorizationServerConfigurer); + + authorizationServerConfigurer + .oidc(oidc -> + oidc + .logoutEndpoint(logoutEndpoint -> + logoutEndpoint.authenticationProviders(configureAuthenticationValidator())) + ); + + return http.build(); +} + +private Consumer> configureAuthenticationValidator() { + return (authenticationProviders) -> + authenticationProviders.forEach((authenticationProvider) -> { + if (authenticationProvider instanceof OidcLogoutAuthenticationProvider oidcLogoutAuthenticationProvider) { + Consumer authenticationValidator = new CustomPostLogoutRedirectUriValidator(); + oidcLogoutAuthenticationProvider.setAuthenticationValidator(authenticationValidator); + } + }); +} + +static class CustomPostLogoutRedirectUriValidator implements Consumer { + + @Override + public void accept(OidcLogoutAuthenticationContext authenticationContext) { + OidcLogoutAuthenticationToken oidcLogoutAuthentication = + authenticationContext.getAuthentication(); + RegisteredClient registeredClient = authenticationContext.getRegisteredClient(); + + // TODO + + } +} +---- + [[oidc-user-info-endpoint]] == OpenID Connect 1.0 UserInfo Endpoint diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/oidc/authentication/OidcLogoutAuthenticationContext.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/oidc/authentication/OidcLogoutAuthenticationContext.java index 39cce5c9..08b810ba 100644 --- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/oidc/authentication/OidcLogoutAuthenticationContext.java +++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/oidc/authentication/OidcLogoutAuthenticationContext.java @@ -15,11 +15,12 @@ */ package org.springframework.security.oauth2.server.authorization.oidc.authentication; +import java.util.Collections; +import java.util.HashMap; import java.util.Map; import java.util.function.Consumer; import org.springframework.lang.Nullable; -import org.springframework.security.core.Authentication; import org.springframework.security.oauth2.server.authorization.authentication.OAuth2AuthenticationContext; import org.springframework.security.oauth2.server.authorization.client.RegisteredClient; import org.springframework.util.Assert; @@ -40,7 +41,7 @@ public final class OidcLogoutAuthenticationContext implements OAuth2Authenticati private final Map context; private OidcLogoutAuthenticationContext(Map context) { - this.context = context; + this.context = Collections.unmodifiableMap(new HashMap<>(context)); } @SuppressWarnings("unchecked") @@ -79,7 +80,7 @@ public final class OidcLogoutAuthenticationContext implements OAuth2Authenticati */ public static final class Builder extends AbstractBuilder { - private Builder(Authentication authentication) { + private Builder(OidcLogoutAuthenticationToken authentication) { super(authentication); } @@ -98,6 +99,7 @@ public final class OidcLogoutAuthenticationContext implements OAuth2Authenticati */ @Override public OidcLogoutAuthenticationContext build() { + Assert.notNull(get(RegisteredClient.class), "registeredClient cannot be null"); return new OidcLogoutAuthenticationContext(getContext()); } 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 352e4676..65dde288 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 @@ -188,7 +188,7 @@ public final class OidcLogoutAuthenticationProvider implements AuthenticationPro /** * Sets the {@code Consumer} providing access to the * {@link OidcLogoutAuthenticationContext} and is responsible for validating specific - * Open ID Connect RP-Initiated Logout Request parameters associated in the + * OpenID Connect RP-Initiated Logout Request parameters associated in the * {@link OidcLogoutAuthenticationToken}. The default authentication validator is * {@link OidcLogoutAuthenticationValidator}. * @@ -197,7 +197,7 @@ public final class OidcLogoutAuthenticationProvider implements AuthenticationPro * {@link OAuth2AuthenticationException} if validation fails. * @param authenticationValidator the {@code Consumer} providing access to the * {@link OidcLogoutAuthenticationContext} and is responsible for validating specific - * Open ID Connect RP-Initiated Logout Request parameters + * OpenID Connect RP-Initiated Logout Request parameters * @since 1.4 */ public void setAuthenticationValidator(Consumer authenticationValidator) { diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/oidc/authentication/OidcLogoutAuthenticationValidator.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/oidc/authentication/OidcLogoutAuthenticationValidator.java index cc9ac11f..55e32ee1 100644 --- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/oidc/authentication/OidcLogoutAuthenticationValidator.java +++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/oidc/authentication/OidcLogoutAuthenticationValidator.java @@ -20,7 +20,6 @@ import java.util.function.Consumer; import org.springframework.security.oauth2.core.OAuth2AuthenticationException; import org.springframework.security.oauth2.core.OAuth2Error; import org.springframework.security.oauth2.core.OAuth2ErrorCodes; -import org.springframework.security.oauth2.core.oidc.OidcIdToken; import org.springframework.security.oauth2.server.authorization.client.RegisteredClient; import org.springframework.util.StringUtils; @@ -29,10 +28,10 @@ import org.springframework.util.StringUtils; * containing an {@link OidcLogoutAuthenticationToken} and is the default * {@link OidcLogoutAuthenticationProvider#setAuthenticationValidator(Consumer) * authentication validator} used for validating specific OpenID Connect RP-Initiated - * Logout parameters used in the Authorization Code Grant. + * Logout Request parameters. * *

- * The default implementation first validates {@link OidcIdToken#getAudience()}, and then + * The default implementation validates * {@link OidcLogoutAuthenticationToken#getPostLogoutRedirectUri()}. If validation fails, * an {@link OAuth2AuthenticationException} is thrown. * 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 50dd1a8f..6b15c9c1 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 @@ -317,7 +317,7 @@ public class OidcLogoutAuthenticationProviderTests { } @Test - void setAuthenticationValidatorWhenNullThenThrowIllegalArgumentException() { + public void setAuthenticationValidatorWhenNullThenThrowIllegalArgumentException() { assertThatThrownBy(() -> this.authenticationProvider.setAuthenticationValidator(null)) .isInstanceOf(IllegalArgumentException.class) .hasMessage("authenticationValidator cannot be null"); @@ -342,7 +342,7 @@ public class OidcLogoutAuthenticationProviderTests { this.authenticationProvider.setAuthenticationValidator(authenticationValidator); authenticateValidIdToken(principal, registeredClient, sessionId, idToken); - verify(authenticationValidator).accept(any()); + verify(authenticationValidator).accept(any(OidcLogoutAuthenticationContext.class)); } @Test