Polish gh-946

This commit is contained in:
Joe Grandja
2022-10-28 15:37:41 -04:00
parent efbfdc234c
commit bfd7a09c3b
5 changed files with 130 additions and 120 deletions

View File

@@ -353,8 +353,10 @@ The guide xref:guides/how-to-userinfo.adoc#how-to-userinfo[How-to: Customize the
[[oidc-client-registration-endpoint]] [[oidc-client-registration-endpoint]]
== OpenID Connect 1.0 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]. `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].
The following example shows how to enable (disabled by default) the 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] [source,java]
---- ----
@@ -375,18 +377,18 @@ public SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity h
.authenticationProviders(authenticationProvidersConsumer) <4> .authenticationProviders(authenticationProvidersConsumer) <4>
.clientRegistrationResponseHandler(clientRegistrationResponseHandler) <5> .clientRegistrationResponseHandler(clientRegistrationResponseHandler) <5>
.errorResponseHandler(errorResponseHandler) <6> .errorResponseHandler(errorResponseHandler) <6>
) )
); );
return http.build(); 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`. <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`. <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`. <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]. <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]. <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] [NOTE]
The OpenID Connect 1.0 Client Registration endpoint is disabled by default because many deployments do not require dynamic client registration. 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`. * `*AuthenticationConverter*` -- An `OidcClientRegistrationAuthenticationConverter`.
* `*AuthenticationManager*` -- An `AuthenticationManager` composed of `OidcClientRegistrationAuthenticationProvider` and `OidcClientConfigurationAuthenticationProvider`. * `*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. * `*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. 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.

View File

@@ -28,7 +28,7 @@ import org.springframework.security.config.annotation.ObjectPostProcessor;
import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.oauth2.core.OAuth2AuthenticationException; import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
import org.springframework.security.oauth2.core.OAuth2Error; 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.OidcClientConfigurationAuthenticationProvider;
import org.springframework.security.oauth2.server.authorization.oidc.authentication.OidcClientRegistrationAuthenticationProvider; 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.authentication.OidcClientRegistrationAuthenticationToken;
@@ -46,7 +46,7 @@ import org.springframework.security.web.util.matcher.RequestMatcher;
import org.springframework.util.Assert; 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 Joe Grandja
* @author Daniel Garnier-Moiroux * @author Daniel Garnier-Moiroux
@@ -57,7 +57,7 @@ import org.springframework.util.Assert;
public final class OidcClientRegistrationEndpointConfigurer extends AbstractOAuth2Configurer { public final class OidcClientRegistrationEndpointConfigurer extends AbstractOAuth2Configurer {
private RequestMatcher requestMatcher; private RequestMatcher requestMatcher;
private final List<AuthenticationConverter> clientRegistrationRequestConverters = new ArrayList<>(); private final List<AuthenticationConverter> clientRegistrationRequestConverters = new ArrayList<>();
private Consumer<List<AuthenticationConverter>> clientRegistrationRequestConvertersConsumer = (authenticationConverters) -> {}; private Consumer<List<AuthenticationConverter>> clientRegistrationRequestConvertersConsumer = (clientRegistrationRequestConverters) -> {};
private final List<AuthenticationProvider> authenticationProviders = new ArrayList<>(); private final List<AuthenticationProvider> authenticationProviders = new ArrayList<>();
private Consumer<List<AuthenticationProvider>> authenticationProvidersConsumer = (authenticationProviders) -> {}; private Consumer<List<AuthenticationProvider>> authenticationProvidersConsumer = (authenticationProviders) -> {};
private AuthenticationSuccessHandler clientRegistrationResponseHandler; 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 * Adds an {@link AuthenticationConverter} used when attempting to extract a Client Registration Request from {@link HttpServletRequest}
* from {@link HttpServletRequest} to an instance of {@link OidcClientRegistrationAuthenticationToken} used for * to an instance of {@link OidcClientRegistrationAuthenticationToken} used for authenticating the request.
* creating the Client Registration or returning the Client Read Response.
* *
* @param clientRegistrationRequestConverter the {@link AuthenticationConverter} used when attempting to extract an * @param clientRegistrationRequestConverter an {@link AuthenticationConverter} used when attempting to extract a Client Registration Request from {@link HttpServletRequest}
* OIDC Client Registration Request from {@link HttpServletRequest}
* @return the {@link OidcClientRegistrationEndpointConfigurer} for further configuration * @return the {@link OidcClientRegistrationEndpointConfigurer} for further configuration
* @since 0.4.0 * @since 0.4.0
*/ */
@@ -96,16 +94,17 @@ public final class OidcClientRegistrationEndpointConfigurer extends AbstractOAut
* @return the {@link OidcUserInfoEndpointConfigurer} for further configuration * @return the {@link OidcUserInfoEndpointConfigurer} for further configuration
* @since 0.4.0 * @since 0.4.0
*/ */
public OidcClientRegistrationEndpointConfigurer clientRegistrationRequestConverters(Consumer<List<AuthenticationConverter>> clientRegistrationRequestConvertersConsumer) { public OidcClientRegistrationEndpointConfigurer clientRegistrationRequestConverters(
Consumer<List<AuthenticationConverter>> clientRegistrationRequestConvertersConsumer) {
Assert.notNull(clientRegistrationRequestConvertersConsumer, "clientRegistrationRequestConvertersConsumer cannot be null"); Assert.notNull(clientRegistrationRequestConvertersConsumer, "clientRegistrationRequestConvertersConsumer cannot be null");
this.clientRegistrationRequestConvertersConsumer = clientRegistrationRequestConvertersConsumer; this.clientRegistrationRequestConvertersConsumer = clientRegistrationRequestConvertersConsumer;
return this; 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 * @return the {@link OidcClientRegistrationEndpointConfigurer} for further configuration
* @since 0.4.0 * @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 * Sets the {@link AuthenticationSuccessHandler} used for handling an {@link OidcClientRegistrationAuthenticationToken}
* returning the {@link OidcUserInfo User Info Response}. * and returning the {@link OidcClientRegistration Client Registration Response}.
* *
* @param clientRegistrationResponseHandler the {@link AuthenticationSuccessHandler} used for handling an {@link OidcClientRegistrationAuthenticationToken} * @param clientRegistrationResponseHandler the {@link AuthenticationSuccessHandler} used for handling an {@link OidcClientRegistrationAuthenticationToken}
* @return the {@link OidcClientRegistrationEndpointConfigurer} for further configuration * @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 * Sets the {@link AuthenticationFailureHandler} used for handling an {@link OAuth2AuthenticationException}
* returning the {@link OAuth2Error Error Response}. * and returning the {@link OAuth2Error Error Response}.
* *
* @param errorResponseHandler the {@link AuthenticationFailureHandler} used for handling an {@link OAuth2AuthenticationException} * @param errorResponseHandler the {@link AuthenticationFailureHandler} used for handling an {@link OAuth2AuthenticationException}
* @return the {@link OidcClientRegistrationEndpointConfigurer} for further configuration * @return the {@link OidcClientRegistrationEndpointConfigurer} for further configuration
@@ -160,18 +159,17 @@ public final class OidcClientRegistrationEndpointConfigurer extends AbstractOAut
@Override @Override
void init(HttpSecurity httpSecurity) { void init(HttpSecurity httpSecurity) {
AuthorizationServerSettings authorizationServerSettings = OAuth2ConfigurerUtils.getAuthorizationServerSettings(httpSecurity); AuthorizationServerSettings authorizationServerSettings = OAuth2ConfigurerUtils.getAuthorizationServerSettings(httpSecurity);
String clientRegistrationEndpointUri = authorizationServerSettings.getOidcClientRegistrationEndpoint();
this.requestMatcher = new OrRequestMatcher( this.requestMatcher = new OrRequestMatcher(
new AntPathRequestMatcher(authorizationServerSettings.getOidcClientRegistrationEndpoint(), HttpMethod.POST.name()), new AntPathRequestMatcher(clientRegistrationEndpointUri, HttpMethod.POST.name()),
new AntPathRequestMatcher(authorizationServerSettings.getOidcClientRegistrationEndpoint(), HttpMethod.GET.name()) new AntPathRequestMatcher(clientRegistrationEndpointUri, HttpMethod.GET.name())
); );
List<AuthenticationProvider> authenticationProviders = createDefaultAuthenticationProviders(httpSecurity); List<AuthenticationProvider> authenticationProviders = createDefaultAuthenticationProviders(httpSecurity);
if (!this.authenticationProviders.isEmpty()) { if (!this.authenticationProviders.isEmpty()) {
authenticationProviders.addAll(0, this.authenticationProviders); authenticationProviders.addAll(0, this.authenticationProviders);
} }
this.authenticationProvidersConsumer.accept(authenticationProviders); this.authenticationProvidersConsumer.accept(authenticationProviders);
authenticationProviders.forEach(authenticationProvider -> authenticationProviders.forEach(authenticationProvider ->
httpSecurity.authenticationProvider(postProcess(authenticationProvider))); httpSecurity.authenticationProvider(postProcess(authenticationProvider)));
} }
@@ -185,7 +183,6 @@ public final class OidcClientRegistrationEndpointConfigurer extends AbstractOAut
new OidcClientRegistrationEndpointFilter( new OidcClientRegistrationEndpointFilter(
authenticationManager, authenticationManager,
authorizationServerSettings.getOidcClientRegistrationEndpoint()); authorizationServerSettings.getOidcClientRegistrationEndpoint());
List<AuthenticationConverter> authenticationConverters = createDefaultAuthenticationConverters(); List<AuthenticationConverter> authenticationConverters = createDefaultAuthenticationConverters();
if (!this.clientRegistrationRequestConverters.isEmpty()) { if (!this.clientRegistrationRequestConverters.isEmpty()) {
authenticationConverters.addAll(0, this.clientRegistrationRequestConverters); authenticationConverters.addAll(0, this.clientRegistrationRequestConverters);
@@ -193,7 +190,6 @@ public final class OidcClientRegistrationEndpointConfigurer extends AbstractOAut
this.clientRegistrationRequestConvertersConsumer.accept(authenticationConverters); this.clientRegistrationRequestConvertersConsumer.accept(authenticationConverters);
oidcClientRegistrationEndpointFilter.setAuthenticationConverter( oidcClientRegistrationEndpointFilter.setAuthenticationConverter(
new DelegatingAuthenticationConverter(authenticationConverters)); new DelegatingAuthenticationConverter(authenticationConverters));
if (this.clientRegistrationResponseHandler != null) { if (this.clientRegistrationResponseHandler != null) {
oidcClientRegistrationEndpointFilter oidcClientRegistrationEndpointFilter
.setAuthenticationSuccessHandler(this.clientRegistrationResponseHandler); .setAuthenticationSuccessHandler(this.clientRegistrationResponseHandler);
@@ -209,6 +205,14 @@ public final class OidcClientRegistrationEndpointConfigurer extends AbstractOAut
return this.requestMatcher; return this.requestMatcher;
} }
private static List<AuthenticationConverter> createDefaultAuthenticationConverters() {
List<AuthenticationConverter> authenticationConverters = new ArrayList<>();
authenticationConverters.add(new OidcClientRegistrationAuthenticationConverter());
return authenticationConverters;
}
private static List<AuthenticationProvider> createDefaultAuthenticationProviders(HttpSecurity httpSecurity) { private static List<AuthenticationProvider> createDefaultAuthenticationProviders(HttpSecurity httpSecurity) {
List<AuthenticationProvider> authenticationProviders = new ArrayList<>(); List<AuthenticationProvider> authenticationProviders = new ArrayList<>();
@@ -224,13 +228,8 @@ public final class OidcClientRegistrationEndpointConfigurer extends AbstractOAut
OAuth2ConfigurerUtils.getRegisteredClientRepository(httpSecurity), OAuth2ConfigurerUtils.getRegisteredClientRepository(httpSecurity),
OAuth2ConfigurerUtils.getAuthorizationService(httpSecurity)); OAuth2ConfigurerUtils.getAuthorizationService(httpSecurity));
authenticationProviders.add(oidcClientConfigurationAuthenticationProvider); authenticationProviders.add(oidcClientConfigurationAuthenticationProvider);
return authenticationProviders; return authenticationProviders;
} }
private static List<AuthenticationConverter> createDefaultAuthenticationConverters() {
List<AuthenticationConverter> authenticationConverters = new ArrayList<>();
authenticationConverters.add(new OidcClientRegistrationAuthenticationConverter());
return authenticationConverters;
}
} }

View File

@@ -74,11 +74,11 @@ public final class OidcClientRegistrationEndpointFilter extends OncePerRequestFi
private final AuthenticationManager authenticationManager; private final AuthenticationManager authenticationManager;
private final RequestMatcher clientRegistrationEndpointMatcher; private final RequestMatcher clientRegistrationEndpointMatcher;
private AuthenticationConverter authenticationConverter = new OidcClientRegistrationAuthenticationConverter();
private final HttpMessageConverter<OidcClientRegistration> clientRegistrationHttpMessageConverter = private final HttpMessageConverter<OidcClientRegistration> clientRegistrationHttpMessageConverter =
new OidcClientRegistrationHttpMessageConverter(); new OidcClientRegistrationHttpMessageConverter();
private final HttpMessageConverter<OAuth2Error> errorHttpResponseConverter = private final HttpMessageConverter<OAuth2Error> errorHttpResponseConverter =
new OAuth2ErrorHttpMessageConverter(); new OAuth2ErrorHttpMessageConverter();
private AuthenticationConverter authenticationConverter = new OidcClientRegistrationAuthenticationConverter();
private AuthenticationSuccessHandler authenticationSuccessHandler = this::sendClientRegistrationResponse; private AuthenticationSuccessHandler authenticationSuccessHandler = this::sendClientRegistrationResponse;
private AuthenticationFailureHandler authenticationFailureHandler = this::sendErrorResponse; private AuthenticationFailureHandler authenticationFailureHandler = this::sendErrorResponse;
@@ -130,11 +130,10 @@ public final class OidcClientRegistrationEndpointFilter extends OncePerRequestFi
} }
try { try {
OidcClientRegistrationAuthenticationToken clientRegistrationAuthentication = Authentication clientRegistrationAuthentication = this.authenticationConverter.convert(request);
(OidcClientRegistrationAuthenticationToken) this.authenticationConverter.convert(request);
OidcClientRegistrationAuthenticationToken clientRegistrationAuthenticationResult = Authentication clientRegistrationAuthenticationResult =
(OidcClientRegistrationAuthenticationToken) this.authenticationManager.authenticate(clientRegistrationAuthentication); this.authenticationManager.authenticate(clientRegistrationAuthentication);
this.authenticationSuccessHandler.onAuthenticationSuccess(request, response, clientRegistrationAuthenticationResult); this.authenticationSuccessHandler.onAuthenticationSuccess(request, response, clientRegistrationAuthenticationResult);
} catch (OAuth2AuthenticationException ex) { } catch (OAuth2AuthenticationException ex) {
@@ -142,7 +141,7 @@ public final class OidcClientRegistrationEndpointFilter extends OncePerRequestFi
} catch (Exception ex) { } catch (Exception ex) {
OAuth2Error error = new OAuth2Error( OAuth2Error error = new OAuth2Error(
OAuth2ErrorCodes.INVALID_REQUEST, 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"); "https://openid.net/specs/openid-connect-registration-1_0.html#RegistrationError");
this.authenticationFailureHandler.onAuthenticationFailure(request, response, this.authenticationFailureHandler.onAuthenticationFailure(request, response,
new OAuth2AuthenticationException(error)); 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 * Sets the {@link AuthenticationConverter} used when attempting to extract a Client Registration Request from {@link HttpServletRequest}
* from {@link HttpServletRequest} to an instance of {@link OidcClientRegistrationAuthenticationToken} used for * to an instance of {@link OidcClientRegistrationAuthenticationToken} used for authenticating the request.
* creating the Client Registration or returning the Client Read Response.
* *
* @param authenticationConverter the {@link AuthenticationConverter} used when attempting to extract an * @param authenticationConverter an {@link AuthenticationConverter} used when attempting to extract a Client Registration Request from {@link HttpServletRequest}
* OIDC Client Registration Request from {@link HttpServletRequest}
* @since 0.4.0 * @since 0.4.0
*/ */
public void setAuthenticationConverter(AuthenticationConverter authenticationConverter) { public void setAuthenticationConverter(AuthenticationConverter authenticationConverter) {
@@ -178,11 +175,10 @@ public final class OidcClientRegistrationEndpointFilter extends OncePerRequestFi
} }
/** /**
* Sets the {@link AuthenticationFailureHandler} used for handling an * Sets the {@link AuthenticationFailureHandler} used for handling an {@link OAuth2AuthenticationException}
* {@link OAuth2AuthenticationException} and returning the {@link OAuth2Error Error * and returning the {@link OAuth2Error Error Response}.
* Response}. *
* @param authenticationFailureHandler the {@link AuthenticationFailureHandler} used * @param authenticationFailureHandler the {@link AuthenticationFailureHandler} used for handling an {@link OAuth2AuthenticationException}
* for handling an {@link OAuth2AuthenticationException}
* @since 0.4.0 * @since 0.4.0
*/ */
public void setAuthenticationFailureHandler(AuthenticationFailureHandler authenticationFailureHandler) { public void setAuthenticationFailureHandler(AuthenticationFailureHandler authenticationFailureHandler) {
@@ -197,8 +193,7 @@ public final class OidcClientRegistrationEndpointFilter extends OncePerRequestFi
ServletServerHttpResponse httpResponse = new ServletServerHttpResponse(response); ServletServerHttpResponse httpResponse = new ServletServerHttpResponse(response);
if (HttpMethod.POST.name().equals(request.getMethod())) { if (HttpMethod.POST.name().equals(request.getMethod())) {
httpResponse.setStatusCode(HttpStatus.CREATED); httpResponse.setStatusCode(HttpStatus.CREATED);
} } else {
else {
httpResponse.setStatusCode(HttpStatus.OK); httpResponse.setStatusCode(HttpStatus.OK);
} }
this.clientRegistrationHttpMessageConverter.write(clientRegistration, null, httpResponse); this.clientRegistrationHttpMessageConverter.write(clientRegistration, null, httpResponse);

View File

@@ -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.OidcClientRegistrationAuthenticationProvider;
import org.springframework.security.oauth2.server.authorization.oidc.authentication.OidcClientRegistrationAuthenticationToken; 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.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.AuthorizationServerSettings;
import org.springframework.security.oauth2.server.authorization.settings.ClientSettings; import org.springframework.security.oauth2.server.authorization.settings.ClientSettings;
import org.springframework.security.oauth2.server.authorization.test.SpringTestRule; import org.springframework.security.oauth2.server.authorization.test.SpringTestRule;
@@ -308,7 +309,7 @@ public class OidcClientRegistrationTests {
} }
@Test @Test
public void requestWhenUserInfoEndpointCustomizedThenUsed() throws Exception { public void requestWhenClientRegistrationEndpointCustomizedThenUsed() throws Exception {
this.spring.register(CustomClientRegistrationConfiguration.class).autowire(); this.spring.register(CustomClientRegistrationConfiguration.class).autowire();
// @formatter:off // @formatter:off
@@ -333,15 +334,17 @@ public class OidcClientRegistrationTests {
registerClient(clientRegistration); registerClient(clientRegistration);
verify(authenticationConverter).convert(any()); verify(authenticationConverter).convert(any());
ArgumentCaptor<List<AuthenticationConverter>> authenticationConvertersCaptor = ArgumentCaptor ArgumentCaptor<List<AuthenticationConverter>> authenticationConvertersCaptor =
.forClass(List.class); ArgumentCaptor.forClass(List.class);
verify(authenticationConvertersConsumer).accept(authenticationConvertersCaptor.capture()); verify(authenticationConvertersConsumer).accept(authenticationConvertersCaptor.capture());
List<AuthenticationConverter> authenticationConverters = authenticationConvertersCaptor.getValue(); List<AuthenticationConverter> authenticationConverters = authenticationConvertersCaptor.getValue();
assertThat(authenticationConverters).hasSize(2).contains(authenticationConverter); assertThat(authenticationConverters).hasSize(2)
.allMatch(converter -> converter == authenticationConverter
|| converter instanceof OidcClientRegistrationAuthenticationConverter);
verify(authenticationProvider).authenticate(any()); verify(authenticationProvider).authenticate(any());
ArgumentCaptor<List<AuthenticationProvider>> authenticationProvidersCaptor = ArgumentCaptor ArgumentCaptor<List<AuthenticationProvider>> authenticationProvidersCaptor =
.forClass(List.class); ArgumentCaptor.forClass(List.class);
verify(authenticationProvidersConsumer).accept(authenticationProvidersCaptor.capture()); verify(authenticationProvidersConsumer).accept(authenticationProvidersCaptor.capture());
List<AuthenticationProvider> authenticationProviders = authenticationProvidersCaptor.getValue(); List<AuthenticationProvider> authenticationProviders = authenticationProvidersCaptor.getValue();
assertThat(authenticationProviders).hasSize(3) assertThat(authenticationProviders).hasSize(3)
@@ -354,7 +357,7 @@ public class OidcClientRegistrationTests {
} }
@Test @Test
public void requestWhenUserInfoEndpointCustomizedAndErrorThenUsed() throws Exception { public void requestWhenClientRegistrationEndpointCustomizedWithAuthenticationFailureHandlerThenUsed() throws Exception {
this.spring.register(CustomClientRegistrationConfiguration.class).autowire(); this.spring.register(CustomClientRegistrationConfiguration.class).autowire();
when(authenticationProvider.authenticate(any())).thenThrow(new OAuth2AuthenticationException("error")); when(authenticationProvider.authenticate(any())).thenThrow(new OAuth2AuthenticationException("error"));
@@ -461,27 +464,38 @@ public class OidcClientRegistrationTests {
@EnableWebSecurity @EnableWebSecurity
static class CustomClientRegistrationConfiguration extends AuthorizationServerConfiguration { static class CustomClientRegistrationConfiguration extends AuthorizationServerConfiguration {
// @formatter:off
@Bean @Bean
@Override @Override
public SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http) throws Exception { public SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http) throws Exception {
OAuth2AuthorizationServerConfigurer authorizationServerConfigurer = new OAuth2AuthorizationServerConfigurer(); OAuth2AuthorizationServerConfigurer authorizationServerConfigurer =
authorizationServerConfigurer.oidc(oidc -> oidc.clientRegistrationEndpoint( new OAuth2AuthorizationServerConfigurer();
clientRegistration -> clientRegistration.clientRegistrationRequestConverter(authenticationConverter) authorizationServerConfigurer
.clientRegistrationRequestConverters(authenticationConvertersConsumer) .oidc(oidc ->
.authenticationProvider(authenticationProvider) oidc
.authenticationProviders(authenticationProvidersConsumer) .clientRegistrationEndpoint(clientRegistration ->
.clientRegistrationResponseHandler(authenticationSuccessHandler) clientRegistration
.errorResponseHandler(authenticationFailureHandler))); .clientRegistrationRequestConverter(authenticationConverter)
.clientRegistrationRequestConverters(authenticationConvertersConsumer)
.authenticationProvider(authenticationProvider)
.authenticationProviders(authenticationProvidersConsumer)
.clientRegistrationResponseHandler(authenticationSuccessHandler)
.errorResponseHandler(authenticationFailureHandler)
)
);
RequestMatcher endpointsMatcher = authorizationServerConfigurer.getEndpointsMatcher(); RequestMatcher endpointsMatcher = authorizationServerConfigurer.getEndpointsMatcher();
http.requestMatcher(endpointsMatcher) http
.authorizeRequests(authorizeRequests -> authorizeRequests.anyRequest().authenticated()) .requestMatcher(endpointsMatcher)
.authorizeRequests(authorizeRequests ->
authorizeRequests.anyRequest().authenticated()
)
.csrf(csrf -> csrf.ignoringRequestMatchers(endpointsMatcher)) .csrf(csrf -> csrf.ignoringRequestMatchers(endpointsMatcher))
.oauth2ResourceServer(OAuth2ResourceServerConfigurer::jwt).apply(authorizationServerConfigurer); .oauth2ResourceServer(OAuth2ResourceServerConfigurer::jwt)
.apply(authorizationServerConfigurer);
return http.build(); return http.build();
} }
// @formatter:on
} }
@EnableWebSecurity @EnableWebSecurity

View File

@@ -371,27 +371,6 @@ public class OidcClientRegistrationEndpointFilterTests {
OAuth2ErrorCodes.INVALID_CLIENT, HttpStatus.UNAUTHORIZED); 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( private void doFilterWhenClientConfigurationRequestInvalidThenError(
String errorCode, HttpStatus status) throws Exception { String errorCode, HttpStatus status) throws Exception {
Jwt jwt = createJwt("client.read"); Jwt jwt = createJwt("client.read");
@@ -475,6 +454,24 @@ public class OidcClientRegistrationEndpointFilterTests {
.isEqualTo(expectedClientRegistrationResponse.getRegistrationClientUrl()); .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 @Test
public void doFilterWhenCustomAuthenticationSuccessHandlerThenUsed() throws Exception { public void doFilterWhenCustomAuthenticationSuccessHandlerThenUsed() throws Exception {
OidcClientRegistration expectedClientRegistrationResponse = createClientRegistration(); OidcClientRegistration expectedClientRegistrationResponse = createClientRegistration();
@@ -504,43 +501,25 @@ public class OidcClientRegistrationEndpointFilterTests {
verify(successHandler).onAuthenticationSuccess(request, response, clientRegistrationAuthenticationResult); 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 @Test
public void doFilterWhenCustomAuthenticationConverterThenUsed() throws ServletException, IOException { public void doFilterWhenCustomAuthenticationFailureHandlerThenUsed() throws Exception {
AuthenticationConverter authenticationConverter = mock(AuthenticationConverter.class); AuthenticationFailureHandler authenticationFailureHandler = mock(AuthenticationFailureHandler.class);
this.filter.setAuthenticationConverter(authenticationConverter); this.filter.setAuthenticationFailureHandler(authenticationFailureHandler);
when(this.authenticationManager.authenticate(any()))
.thenThrow(new OAuth2AuthenticationException(OAuth2ErrorCodes.INVALID_TOKEN));
String requestUri = DEFAULT_OIDC_CLIENT_REGISTRATION_ENDPOINT_URI; String requestUri = DEFAULT_OIDC_CLIENT_REGISTRATION_ENDPOINT_URI;
MockHttpServletRequest request = new MockHttpServletRequest("GET", requestUri); MockHttpServletRequest request = new MockHttpServletRequest("GET", requestUri);
request.setServletPath(requestUri); request.setServletPath(requestUri);
request.setParameter(OAuth2ParameterNames.CLIENT_ID, "client-id"); request.setParameter(OAuth2ParameterNames.CLIENT_ID, "client1");
MockHttpServletResponse response = new MockHttpServletResponse(); MockHttpServletResponse response = new MockHttpServletResponse();
FilterChain filterChain = mock(FilterChain.class); FilterChain filterChain = mock(FilterChain.class);
this.filter.doFilter(request, response, filterChain); 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 { private OAuth2Error readError(MockHttpServletResponse response) throws Exception {
@@ -562,6 +541,27 @@ public class OidcClientRegistrationEndpointFilterTests {
return this.clientRegistrationHttpMessageConverter.read(OidcClientRegistration.class, httpResponse); 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) { private static Jwt createJwt(String scope) {
// @formatter:off // @formatter:off
JwsHeader jwsHeader = TestJwsHeaders.jwsHeader() JwsHeader jwsHeader = TestJwsHeaders.jwsHeader()