diff --git a/docs/src/docs/asciidoc/protocol-endpoints.adoc b/docs/src/docs/asciidoc/protocol-endpoints.adoc index 6e5c3f9a..7a3222b3 100644 --- a/docs/src/docs/asciidoc/protocol-endpoints.adoc +++ b/docs/src/docs/asciidoc/protocol-endpoints.adoc @@ -353,8 +353,10 @@ The guide xref:guides/how-to-userinfo.adoc#how-to-userinfo[How-to: Customize the [[oidc-client-registration-endpoint]] == OpenID Connect 1.0 Client Registration Endpoint -`OidcClientRegistrationEndpointConfigurer` configures the https://openid.net/specs/openid-connect-registration-1_0.html#ClientRegistration[OpenID Connect 1.0 Client Registration endpoint]. -The following example shows how to enable (disabled by default) the OpenID Connect 1.0 Client Registration endpoint: +`OidcClientRegistrationEndpointConfigurer` provides the ability to customize the https://openid.net/specs/openid-connect-registration-1_0.html#ClientRegistration[OpenID Connect 1.0 Client Registration endpoint]. +It defines extension points that let you customize the pre-processing, main processing, and post-processing logic for https://openid.net/specs/openid-connect-registration-1_0.html#RegistrationRequest[Client Registration requests] or https://openid.net/specs/openid-connect-registration-1_0.html#ReadRequest[Client Read requests]. + +`OidcClientRegistrationEndpointConfigurer` provides the following configuration options: [source,java] ---- @@ -375,18 +377,18 @@ public SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity h .authenticationProviders(authenticationProvidersConsumer) <4> .clientRegistrationResponseHandler(clientRegistrationResponseHandler) <5> .errorResponseHandler(errorResponseHandler) <6> - ) + ) ); return http.build(); } ---- -<1> `clientRegistrationRequestConverter()`: Adds an `AuthenticationConverter` (_pre-processor_) used when attempting to extract a https://openid.net/specs/openid-connect-registration-1_0.html#RegistrationRequest[Client Registration Request] or https://openid.net/specs/openid-connect-registration-1_0.html#ReadRequest[Client Read Request] from `HttpServletRequest` to an instance of `OidcClientRegistrationAuthenticationToken`. +<1> `clientRegistrationRequestConverter()`: Adds an `AuthenticationConverter` (_pre-processor_) used when attempting to extract a https://openid.net/specs/openid-connect-registration-1_0.html#RegistrationRequest[Client Registration request] or https://openid.net/specs/openid-connect-registration-1_0.html#ReadRequest[Client Read request] from `HttpServletRequest` to an instance of `OidcClientRegistrationAuthenticationToken`. <2> `clientRegistrationRequestConverters()`: Sets the `Consumer` providing access to the `List` of default and (optionally) added ``AuthenticationConverter``'s allowing the ability to add, remove, or customize a specific `AuthenticationConverter`. <3> `authenticationProvider()`: Adds an `AuthenticationProvider` (_main processor_) used for authenticating the `OidcClientRegistrationAuthenticationToken`. <4> `authenticationProviders()`: Sets the `Consumer` providing access to the `List` of default and (optionally) added ``AuthenticationProvider``'s allowing the ability to add, remove, or customize a specific `AuthenticationProvider`. -<5> `clientRegistrationResponseHandler()`: The `AuthenticationSuccessHandler` (_post-processor_) used for handling an "`authenticated`" `OidcClientRegistrationAuthenticationToken` and returning the https://openid.net/specs/openid-connect-registration-1_0.html#RegistrationResponse[Client Registration Response] or https://openid.net/specs/openid-connect-registration-1_0.html#ReadResponse[Client Read Response]. -<6> `errorResponseHandler()`: The `AuthenticationFailureHandler` (_post-processor_) used for handling an `OAuth2AuthenticationException` and returning the https://openid.net/specs/openid-connect-registration-1_0.html#RegistrationError[Client Registration Error Response] or https://openid.net/specs/openid-connect-registration-1_0.html#ReadError[Client Read Error Response]. +<5> `clientRegistrationResponseHandler()`: The `AuthenticationSuccessHandler` (_post-processor_) used for handling an "`authenticated`" `OidcClientRegistrationAuthenticationToken` and returning the https://openid.net/specs/openid-connect-registration-1_0.html#RegistrationResponse[Client Registration response] or https://openid.net/specs/openid-connect-registration-1_0.html#ReadResponse[Client Read response]. +<6> `errorResponseHandler()`: The `AuthenticationFailureHandler` (_post-processor_) used for handling an `OAuth2AuthenticationException` and returning the https://openid.net/specs/openid-connect-registration-1_0.html#RegistrationError[Client Registration Error response] or https://openid.net/specs/openid-connect-registration-1_0.html#ReadError[Client Read Error response]. [NOTE] The OpenID Connect 1.0 Client Registration endpoint is disabled by default because many deployments do not require dynamic client registration. @@ -401,7 +403,7 @@ The OpenID Connect 1.0 Client Registration endpoint is disabled by default becau * `*AuthenticationConverter*` -- An `OidcClientRegistrationAuthenticationConverter`. * `*AuthenticationManager*` -- An `AuthenticationManager` composed of `OidcClientRegistrationAuthenticationProvider` and `OidcClientConfigurationAuthenticationProvider`. -* `*AuthenticationSuccessHandler*` -- An internal implementation that handles an "`authenticated`" `OidcClientRegistrationAuthenticationToken` and returns the Client Registration or Client Read response. +* `*AuthenticationSuccessHandler*` -- An internal implementation that handles an "`authenticated`" `OidcClientRegistrationAuthenticationToken` and returns the `OidcClientRegistration` response. * `*AuthenticationFailureHandler*` -- An internal implementation that uses the `OAuth2Error` associated with the `OAuth2AuthenticationException` and returns the `OAuth2Error` response. The OpenID Connect 1.0 Client Registration endpoint is an https://openid.net/specs/openid-connect-registration-1_0.html#ClientRegistration[OAuth2 protected resource], which *REQUIRES* an access token to be sent as a bearer token in the Client Registration (or Client Read) request. diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/OidcClientRegistrationEndpointConfigurer.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/OidcClientRegistrationEndpointConfigurer.java index a8bf0c95..4e5d3f06 100644 --- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/OidcClientRegistrationEndpointConfigurer.java +++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/OidcClientRegistrationEndpointConfigurer.java @@ -28,7 +28,7 @@ import org.springframework.security.config.annotation.ObjectPostProcessor; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.oauth2.core.OAuth2AuthenticationException; import org.springframework.security.oauth2.core.OAuth2Error; -import org.springframework.security.oauth2.core.oidc.OidcUserInfo; +import org.springframework.security.oauth2.server.authorization.oidc.OidcClientRegistration; import org.springframework.security.oauth2.server.authorization.oidc.authentication.OidcClientConfigurationAuthenticationProvider; import org.springframework.security.oauth2.server.authorization.oidc.authentication.OidcClientRegistrationAuthenticationProvider; import org.springframework.security.oauth2.server.authorization.oidc.authentication.OidcClientRegistrationAuthenticationToken; @@ -46,7 +46,7 @@ import org.springframework.security.web.util.matcher.RequestMatcher; import org.springframework.util.Assert; /** - * Configurer for OpenID Connect Dynamic Client Registration 1.0 Endpoint. + * Configurer for OpenID Connect 1.0 Dynamic Client Registration Endpoint. * * @author Joe Grandja * @author Daniel Garnier-Moiroux @@ -57,7 +57,7 @@ import org.springframework.util.Assert; public final class OidcClientRegistrationEndpointConfigurer extends AbstractOAuth2Configurer { private RequestMatcher requestMatcher; private final List clientRegistrationRequestConverters = new ArrayList<>(); - private Consumer> clientRegistrationRequestConvertersConsumer = (authenticationConverters) -> {}; + private Consumer> clientRegistrationRequestConvertersConsumer = (clientRegistrationRequestConverters) -> {}; private final List authenticationProviders = new ArrayList<>(); private Consumer> authenticationProvidersConsumer = (authenticationProviders) -> {}; private AuthenticationSuccessHandler clientRegistrationResponseHandler; @@ -71,12 +71,10 @@ public final class OidcClientRegistrationEndpointConfigurer extends AbstractOAut } /** - * Sets the {@link AuthenticationConverter} used when attempting to extract the OIDC Client Registration Request - * from {@link HttpServletRequest} to an instance of {@link OidcClientRegistrationAuthenticationToken} used for - * creating the Client Registration or returning the Client Read Response. + * Adds an {@link AuthenticationConverter} used when attempting to extract a Client Registration Request from {@link HttpServletRequest} + * to an instance of {@link OidcClientRegistrationAuthenticationToken} used for authenticating the request. * - * @param clientRegistrationRequestConverter the {@link AuthenticationConverter} used when attempting to extract an - * OIDC Client Registration Request from {@link HttpServletRequest} + * @param clientRegistrationRequestConverter an {@link AuthenticationConverter} used when attempting to extract a Client Registration Request from {@link HttpServletRequest} * @return the {@link OidcClientRegistrationEndpointConfigurer} for further configuration * @since 0.4.0 */ @@ -96,16 +94,17 @@ public final class OidcClientRegistrationEndpointConfigurer extends AbstractOAut * @return the {@link OidcUserInfoEndpointConfigurer} for further configuration * @since 0.4.0 */ - public OidcClientRegistrationEndpointConfigurer clientRegistrationRequestConverters(Consumer> clientRegistrationRequestConvertersConsumer) { + public OidcClientRegistrationEndpointConfigurer clientRegistrationRequestConverters( + Consumer> clientRegistrationRequestConvertersConsumer) { Assert.notNull(clientRegistrationRequestConvertersConsumer, "clientRegistrationRequestConvertersConsumer cannot be null"); this.clientRegistrationRequestConvertersConsumer = clientRegistrationRequestConvertersConsumer; return this; } /** - * Adds an {@link AuthenticationProvider} used for authenticating a type of {@link OidcClientRegistrationAuthenticationToken}. + * Adds an {@link AuthenticationProvider} used for authenticating an {@link OidcClientRegistrationAuthenticationToken}. * - * @param authenticationProvider a {@link AuthenticationProvider} used for authenticating a type of {@link OidcClientRegistrationAuthenticationToken} + * @param authenticationProvider an {@link AuthenticationProvider} used for authenticating an {@link OidcClientRegistrationAuthenticationToken} * @return the {@link OidcClientRegistrationEndpointConfigurer} for further configuration * @since 0.4.0 */ @@ -132,8 +131,8 @@ public final class OidcClientRegistrationEndpointConfigurer extends AbstractOAut } /** - * Sets the {@link AuthenticationSuccessHandler} used for handling an {@link OidcClientRegistrationAuthenticationToken} and - * returning the {@link OidcUserInfo User Info Response}. + * Sets the {@link AuthenticationSuccessHandler} used for handling an {@link OidcClientRegistrationAuthenticationToken} + * and returning the {@link OidcClientRegistration Client Registration Response}. * * @param clientRegistrationResponseHandler the {@link AuthenticationSuccessHandler} used for handling an {@link OidcClientRegistrationAuthenticationToken} * @return the {@link OidcClientRegistrationEndpointConfigurer} for further configuration @@ -145,8 +144,8 @@ public final class OidcClientRegistrationEndpointConfigurer extends AbstractOAut } /** - * Sets the {@link AuthenticationFailureHandler} used for handling an {@link OAuth2AuthenticationException} and - * returning the {@link OAuth2Error Error Response}. + * Sets the {@link AuthenticationFailureHandler} used for handling an {@link OAuth2AuthenticationException} + * and returning the {@link OAuth2Error Error Response}. * * @param errorResponseHandler the {@link AuthenticationFailureHandler} used for handling an {@link OAuth2AuthenticationException} * @return the {@link OidcClientRegistrationEndpointConfigurer} for further configuration @@ -160,18 +159,17 @@ public final class OidcClientRegistrationEndpointConfigurer extends AbstractOAut @Override void init(HttpSecurity httpSecurity) { AuthorizationServerSettings authorizationServerSettings = OAuth2ConfigurerUtils.getAuthorizationServerSettings(httpSecurity); + String clientRegistrationEndpointUri = authorizationServerSettings.getOidcClientRegistrationEndpoint(); this.requestMatcher = new OrRequestMatcher( - new AntPathRequestMatcher(authorizationServerSettings.getOidcClientRegistrationEndpoint(), HttpMethod.POST.name()), - new AntPathRequestMatcher(authorizationServerSettings.getOidcClientRegistrationEndpoint(), HttpMethod.GET.name()) + new AntPathRequestMatcher(clientRegistrationEndpointUri, HttpMethod.POST.name()), + new AntPathRequestMatcher(clientRegistrationEndpointUri, HttpMethod.GET.name()) ); List authenticationProviders = createDefaultAuthenticationProviders(httpSecurity); - if (!this.authenticationProviders.isEmpty()) { authenticationProviders.addAll(0, this.authenticationProviders); } this.authenticationProvidersConsumer.accept(authenticationProviders); - authenticationProviders.forEach(authenticationProvider -> httpSecurity.authenticationProvider(postProcess(authenticationProvider))); } @@ -185,7 +183,6 @@ public final class OidcClientRegistrationEndpointConfigurer extends AbstractOAut new OidcClientRegistrationEndpointFilter( authenticationManager, authorizationServerSettings.getOidcClientRegistrationEndpoint()); - List authenticationConverters = createDefaultAuthenticationConverters(); if (!this.clientRegistrationRequestConverters.isEmpty()) { authenticationConverters.addAll(0, this.clientRegistrationRequestConverters); @@ -193,7 +190,6 @@ public final class OidcClientRegistrationEndpointConfigurer extends AbstractOAut this.clientRegistrationRequestConvertersConsumer.accept(authenticationConverters); oidcClientRegistrationEndpointFilter.setAuthenticationConverter( new DelegatingAuthenticationConverter(authenticationConverters)); - if (this.clientRegistrationResponseHandler != null) { oidcClientRegistrationEndpointFilter .setAuthenticationSuccessHandler(this.clientRegistrationResponseHandler); @@ -209,6 +205,14 @@ public final class OidcClientRegistrationEndpointConfigurer extends AbstractOAut return this.requestMatcher; } + private static List createDefaultAuthenticationConverters() { + List authenticationConverters = new ArrayList<>(); + + authenticationConverters.add(new OidcClientRegistrationAuthenticationConverter()); + + return authenticationConverters; + } + private static List createDefaultAuthenticationProviders(HttpSecurity httpSecurity) { List authenticationProviders = new ArrayList<>(); @@ -224,13 +228,8 @@ public final class OidcClientRegistrationEndpointConfigurer extends AbstractOAut OAuth2ConfigurerUtils.getRegisteredClientRepository(httpSecurity), OAuth2ConfigurerUtils.getAuthorizationService(httpSecurity)); authenticationProviders.add(oidcClientConfigurationAuthenticationProvider); + return authenticationProviders; } - private static List createDefaultAuthenticationConverters() { - List authenticationConverters = new ArrayList<>(); - authenticationConverters.add(new OidcClientRegistrationAuthenticationConverter()); - return authenticationConverters; - } - } diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/oidc/web/OidcClientRegistrationEndpointFilter.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/oidc/web/OidcClientRegistrationEndpointFilter.java index f737b01d..031755d3 100644 --- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/oidc/web/OidcClientRegistrationEndpointFilter.java +++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/oidc/web/OidcClientRegistrationEndpointFilter.java @@ -74,11 +74,11 @@ public final class OidcClientRegistrationEndpointFilter extends OncePerRequestFi private final AuthenticationManager authenticationManager; private final RequestMatcher clientRegistrationEndpointMatcher; - private AuthenticationConverter authenticationConverter = new OidcClientRegistrationAuthenticationConverter(); private final HttpMessageConverter clientRegistrationHttpMessageConverter = new OidcClientRegistrationHttpMessageConverter(); private final HttpMessageConverter errorHttpResponseConverter = new OAuth2ErrorHttpMessageConverter(); + private AuthenticationConverter authenticationConverter = new OidcClientRegistrationAuthenticationConverter(); private AuthenticationSuccessHandler authenticationSuccessHandler = this::sendClientRegistrationResponse; private AuthenticationFailureHandler authenticationFailureHandler = this::sendErrorResponse; @@ -130,11 +130,10 @@ public final class OidcClientRegistrationEndpointFilter extends OncePerRequestFi } try { - OidcClientRegistrationAuthenticationToken clientRegistrationAuthentication = - (OidcClientRegistrationAuthenticationToken) this.authenticationConverter.convert(request); + Authentication clientRegistrationAuthentication = this.authenticationConverter.convert(request); - OidcClientRegistrationAuthenticationToken clientRegistrationAuthenticationResult = - (OidcClientRegistrationAuthenticationToken) this.authenticationManager.authenticate(clientRegistrationAuthentication); + Authentication clientRegistrationAuthenticationResult = + this.authenticationManager.authenticate(clientRegistrationAuthentication); this.authenticationSuccessHandler.onAuthenticationSuccess(request, response, clientRegistrationAuthenticationResult); } catch (OAuth2AuthenticationException ex) { @@ -142,7 +141,7 @@ public final class OidcClientRegistrationEndpointFilter extends OncePerRequestFi } catch (Exception ex) { OAuth2Error error = new OAuth2Error( OAuth2ErrorCodes.INVALID_REQUEST, - "OpenID Client Registration Error: " + ex.getMessage(), + "OpenID Connect 1.0 Client Registration Error: " + ex.getMessage(), "https://openid.net/specs/openid-connect-registration-1_0.html#RegistrationError"); this.authenticationFailureHandler.onAuthenticationFailure(request, response, new OAuth2AuthenticationException(error)); @@ -152,12 +151,10 @@ public final class OidcClientRegistrationEndpointFilter extends OncePerRequestFi } /** - * Sets the {@link AuthenticationConverter} used when attempting to extract the OIDC Client Registration Request - * from {@link HttpServletRequest} to an instance of {@link OidcClientRegistrationAuthenticationToken} used for - * creating the Client Registration or returning the Client Read Response. + * Sets the {@link AuthenticationConverter} used when attempting to extract a Client Registration Request from {@link HttpServletRequest} + * to an instance of {@link OidcClientRegistrationAuthenticationToken} used for authenticating the request. * - * @param authenticationConverter the {@link AuthenticationConverter} used when attempting to extract an - * OIDC Client Registration Request from {@link HttpServletRequest} + * @param authenticationConverter an {@link AuthenticationConverter} used when attempting to extract a Client Registration Request from {@link HttpServletRequest} * @since 0.4.0 */ public void setAuthenticationConverter(AuthenticationConverter authenticationConverter) { @@ -178,11 +175,10 @@ public final class OidcClientRegistrationEndpointFilter extends OncePerRequestFi } /** - * Sets the {@link AuthenticationFailureHandler} used for handling an - * {@link OAuth2AuthenticationException} and returning the {@link OAuth2Error Error - * Response}. - * @param authenticationFailureHandler the {@link AuthenticationFailureHandler} used - * for handling an {@link OAuth2AuthenticationException} + * Sets the {@link AuthenticationFailureHandler} used for handling an {@link OAuth2AuthenticationException} + * and returning the {@link OAuth2Error Error Response}. + * + * @param authenticationFailureHandler the {@link AuthenticationFailureHandler} used for handling an {@link OAuth2AuthenticationException} * @since 0.4.0 */ public void setAuthenticationFailureHandler(AuthenticationFailureHandler authenticationFailureHandler) { @@ -197,8 +193,7 @@ public final class OidcClientRegistrationEndpointFilter extends OncePerRequestFi ServletServerHttpResponse httpResponse = new ServletServerHttpResponse(response); if (HttpMethod.POST.name().equals(request.getMethod())) { httpResponse.setStatusCode(HttpStatus.CREATED); - } - else { + } else { httpResponse.setStatusCode(HttpStatus.OK); } this.clientRegistrationHttpMessageConverter.write(clientRegistration, null, httpResponse); diff --git a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/OidcClientRegistrationTests.java b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/OidcClientRegistrationTests.java index 787a143b..06ffbe78 100644 --- a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/OidcClientRegistrationTests.java +++ b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/OidcClientRegistrationTests.java @@ -88,6 +88,7 @@ import org.springframework.security.oauth2.server.authorization.oidc.authenticat import org.springframework.security.oauth2.server.authorization.oidc.authentication.OidcClientRegistrationAuthenticationProvider; import org.springframework.security.oauth2.server.authorization.oidc.authentication.OidcClientRegistrationAuthenticationToken; import org.springframework.security.oauth2.server.authorization.oidc.http.converter.OidcClientRegistrationHttpMessageConverter; +import org.springframework.security.oauth2.server.authorization.oidc.web.authentication.OidcClientRegistrationAuthenticationConverter; import org.springframework.security.oauth2.server.authorization.settings.AuthorizationServerSettings; import org.springframework.security.oauth2.server.authorization.settings.ClientSettings; import org.springframework.security.oauth2.server.authorization.test.SpringTestRule; @@ -308,7 +309,7 @@ public class OidcClientRegistrationTests { } @Test - public void requestWhenUserInfoEndpointCustomizedThenUsed() throws Exception { + public void requestWhenClientRegistrationEndpointCustomizedThenUsed() throws Exception { this.spring.register(CustomClientRegistrationConfiguration.class).autowire(); // @formatter:off @@ -333,15 +334,17 @@ public class OidcClientRegistrationTests { registerClient(clientRegistration); verify(authenticationConverter).convert(any()); - ArgumentCaptor> authenticationConvertersCaptor = ArgumentCaptor - .forClass(List.class); + ArgumentCaptor> authenticationConvertersCaptor = + ArgumentCaptor.forClass(List.class); verify(authenticationConvertersConsumer).accept(authenticationConvertersCaptor.capture()); List authenticationConverters = authenticationConvertersCaptor.getValue(); - assertThat(authenticationConverters).hasSize(2).contains(authenticationConverter); + assertThat(authenticationConverters).hasSize(2) + .allMatch(converter -> converter == authenticationConverter + || converter instanceof OidcClientRegistrationAuthenticationConverter); verify(authenticationProvider).authenticate(any()); - ArgumentCaptor> authenticationProvidersCaptor = ArgumentCaptor - .forClass(List.class); + ArgumentCaptor> authenticationProvidersCaptor = + ArgumentCaptor.forClass(List.class); verify(authenticationProvidersConsumer).accept(authenticationProvidersCaptor.capture()); List authenticationProviders = authenticationProvidersCaptor.getValue(); assertThat(authenticationProviders).hasSize(3) @@ -354,7 +357,7 @@ public class OidcClientRegistrationTests { } @Test - public void requestWhenUserInfoEndpointCustomizedAndErrorThenUsed() throws Exception { + public void requestWhenClientRegistrationEndpointCustomizedWithAuthenticationFailureHandlerThenUsed() throws Exception { this.spring.register(CustomClientRegistrationConfiguration.class).autowire(); when(authenticationProvider.authenticate(any())).thenThrow(new OAuth2AuthenticationException("error")); @@ -461,27 +464,38 @@ public class OidcClientRegistrationTests { @EnableWebSecurity static class CustomClientRegistrationConfiguration extends AuthorizationServerConfiguration { + // @formatter:off @Bean @Override public SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http) throws Exception { - OAuth2AuthorizationServerConfigurer authorizationServerConfigurer = new OAuth2AuthorizationServerConfigurer(); - authorizationServerConfigurer.oidc(oidc -> oidc.clientRegistrationEndpoint( - clientRegistration -> clientRegistration.clientRegistrationRequestConverter(authenticationConverter) - .clientRegistrationRequestConverters(authenticationConvertersConsumer) - .authenticationProvider(authenticationProvider) - .authenticationProviders(authenticationProvidersConsumer) - .clientRegistrationResponseHandler(authenticationSuccessHandler) - .errorResponseHandler(authenticationFailureHandler))); + OAuth2AuthorizationServerConfigurer authorizationServerConfigurer = + new OAuth2AuthorizationServerConfigurer(); + authorizationServerConfigurer + .oidc(oidc -> + oidc + .clientRegistrationEndpoint(clientRegistration -> + clientRegistration + .clientRegistrationRequestConverter(authenticationConverter) + .clientRegistrationRequestConverters(authenticationConvertersConsumer) + .authenticationProvider(authenticationProvider) + .authenticationProviders(authenticationProvidersConsumer) + .clientRegistrationResponseHandler(authenticationSuccessHandler) + .errorResponseHandler(authenticationFailureHandler) + ) + ); RequestMatcher endpointsMatcher = authorizationServerConfigurer.getEndpointsMatcher(); - http.requestMatcher(endpointsMatcher) - .authorizeRequests(authorizeRequests -> authorizeRequests.anyRequest().authenticated()) + http + .requestMatcher(endpointsMatcher) + .authorizeRequests(authorizeRequests -> + authorizeRequests.anyRequest().authenticated() + ) .csrf(csrf -> csrf.ignoringRequestMatchers(endpointsMatcher)) - .oauth2ResourceServer(OAuth2ResourceServerConfigurer::jwt).apply(authorizationServerConfigurer); + .oauth2ResourceServer(OAuth2ResourceServerConfigurer::jwt) + .apply(authorizationServerConfigurer); return http.build(); - } - + // @formatter:on } @EnableWebSecurity diff --git a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/oidc/web/OidcClientRegistrationEndpointFilterTests.java b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/oidc/web/OidcClientRegistrationEndpointFilterTests.java index f6de9fe9..f5e9890e 100644 --- a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/oidc/web/OidcClientRegistrationEndpointFilterTests.java +++ b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/oidc/web/OidcClientRegistrationEndpointFilterTests.java @@ -371,27 +371,6 @@ public class OidcClientRegistrationEndpointFilterTests { OAuth2ErrorCodes.INVALID_CLIENT, HttpStatus.UNAUTHORIZED); } - @Test - public void doFilterWhenCustomAuthenticationFailureHandlerThenUsed() throws Exception { - AuthenticationFailureHandler authenticationFailureHandler = mock(AuthenticationFailureHandler.class); - this.filter.setAuthenticationFailureHandler(authenticationFailureHandler); - - when(this.authenticationManager.authenticate(any())) - .thenThrow(new OAuth2AuthenticationException(OAuth2ErrorCodes.INVALID_TOKEN)); - - String requestUri = DEFAULT_OIDC_CLIENT_REGISTRATION_ENDPOINT_URI; - MockHttpServletRequest request = new MockHttpServletRequest("GET", requestUri); - request.setServletPath(requestUri); - request.setParameter(OAuth2ParameterNames.CLIENT_ID, "client1"); - MockHttpServletResponse response = new MockHttpServletResponse(); - FilterChain filterChain = mock(FilterChain.class); - - this.filter.doFilter(request, response, filterChain); - - verify(authenticationFailureHandler).onAuthenticationFailure(eq(request), eq(response), - any(OAuth2AuthenticationException.class)); - } - private void doFilterWhenClientConfigurationRequestInvalidThenError( String errorCode, HttpStatus status) throws Exception { Jwt jwt = createJwt("client.read"); @@ -475,6 +454,24 @@ public class OidcClientRegistrationEndpointFilterTests { .isEqualTo(expectedClientRegistrationResponse.getRegistrationClientUrl()); } + @Test + public void doFilterWhenCustomAuthenticationConverterThenUsed() throws ServletException, IOException { + AuthenticationConverter authenticationConverter = mock(AuthenticationConverter.class); + this.filter.setAuthenticationConverter(authenticationConverter); + + String requestUri = DEFAULT_OIDC_CLIENT_REGISTRATION_ENDPOINT_URI; + MockHttpServletRequest request = new MockHttpServletRequest("GET", requestUri); + request.setServletPath(requestUri); + request.setParameter(OAuth2ParameterNames.CLIENT_ID, "client-id"); + + MockHttpServletResponse response = new MockHttpServletResponse(); + FilterChain filterChain = mock(FilterChain.class); + + this.filter.doFilter(request, response, filterChain); + + verify(authenticationConverter).convert(request); + } + @Test public void doFilterWhenCustomAuthenticationSuccessHandlerThenUsed() throws Exception { OidcClientRegistration expectedClientRegistrationResponse = createClientRegistration(); @@ -504,43 +501,25 @@ public class OidcClientRegistrationEndpointFilterTests { verify(successHandler).onAuthenticationSuccess(request, response, clientRegistrationAuthenticationResult); } - private static OidcClientRegistration createClientRegistration() { - // @formatter:off - OidcClientRegistration expectedClientRegistrationResponse = OidcClientRegistration.builder() - .clientId("client-id") - .clientIdIssuedAt(Instant.now()) - .clientSecret("client-secret") - .clientName("client-name") - .redirectUri("https://client.example.com") - .grantType(AuthorizationGrantType.AUTHORIZATION_CODE.getValue()) - .grantType(AuthorizationGrantType.CLIENT_CREDENTIALS.getValue()) - .tokenEndpointAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC.getValue()) - .responseType(OAuth2AuthorizationResponseType.CODE.getValue()) - .idTokenSignedResponseAlgorithm(SignatureAlgorithm.RS256.getName()) - .scope("scope1") - .scope("scope2") - .registrationClientUrl("https://auth-server:9000/connect/register?client_id=client-id") - .build(); - return expectedClientRegistrationResponse; - // @formatter:on - } - @Test - public void doFilterWhenCustomAuthenticationConverterThenUsed() throws ServletException, IOException { - AuthenticationConverter authenticationConverter = mock(AuthenticationConverter.class); - this.filter.setAuthenticationConverter(authenticationConverter); + public void doFilterWhenCustomAuthenticationFailureHandlerThenUsed() throws Exception { + AuthenticationFailureHandler authenticationFailureHandler = mock(AuthenticationFailureHandler.class); + this.filter.setAuthenticationFailureHandler(authenticationFailureHandler); + + when(this.authenticationManager.authenticate(any())) + .thenThrow(new OAuth2AuthenticationException(OAuth2ErrorCodes.INVALID_TOKEN)); String requestUri = DEFAULT_OIDC_CLIENT_REGISTRATION_ENDPOINT_URI; MockHttpServletRequest request = new MockHttpServletRequest("GET", requestUri); request.setServletPath(requestUri); - request.setParameter(OAuth2ParameterNames.CLIENT_ID, "client-id"); - + request.setParameter(OAuth2ParameterNames.CLIENT_ID, "client1"); MockHttpServletResponse response = new MockHttpServletResponse(); FilterChain filterChain = mock(FilterChain.class); this.filter.doFilter(request, response, filterChain); - verify(authenticationConverter).convert(request); + verify(authenticationFailureHandler).onAuthenticationFailure(eq(request), eq(response), + any(OAuth2AuthenticationException.class)); } private OAuth2Error readError(MockHttpServletResponse response) throws Exception { @@ -562,6 +541,27 @@ public class OidcClientRegistrationEndpointFilterTests { return this.clientRegistrationHttpMessageConverter.read(OidcClientRegistration.class, httpResponse); } + private static OidcClientRegistration createClientRegistration() { + // @formatter:off + OidcClientRegistration clientRegistration = OidcClientRegistration.builder() + .clientId("client-id") + .clientIdIssuedAt(Instant.now()) + .clientSecret("client-secret") + .clientName("client-name") + .redirectUri("https://client.example.com") + .grantType(AuthorizationGrantType.AUTHORIZATION_CODE.getValue()) + .grantType(AuthorizationGrantType.CLIENT_CREDENTIALS.getValue()) + .tokenEndpointAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC.getValue()) + .responseType(OAuth2AuthorizationResponseType.CODE.getValue()) + .idTokenSignedResponseAlgorithm(SignatureAlgorithm.RS256.getName()) + .scope("scope1") + .scope("scope2") + .registrationClientUrl("https://auth-server:9000/connect/register?client_id=client-id") + .build(); + return clientRegistration; + // @formatter:on + } + private static Jwt createJwt(String scope) { // @formatter:off JwsHeader jwsHeader = TestJwsHeaders.jwsHeader()