From 74058e555c76c5ea6bb84e4d9b8d588bf48eb366 Mon Sep 17 00:00:00 2001 From: Joe Grandja Date: Thu, 16 Nov 2023 14:29:07 -0500 Subject: [PATCH] Polish gh-1376 --- .../how-to-dynamic-client-registration.adoc | 34 +++----- .../sample/registration/ClientRegistrar.java | 2 +- ...g.java => CustomClientMetadataConfig.java} | 85 ++++++++++--------- .../sample/registration/SecurityConfig.java | 12 +-- .../DynamicClientRegistrationTests.java | 2 +- 5 files changed, 67 insertions(+), 68 deletions(-) rename docs/src/main/java/sample/registration/{CustomMetadataConfig.java => CustomClientMetadataConfig.java} (64%) diff --git a/docs/modules/ROOT/pages/guides/how-to-dynamic-client-registration.adoc b/docs/modules/ROOT/pages/guides/how-to-dynamic-client-registration.adoc index eefaa74a..fc08c36a 100644 --- a/docs/modules/ROOT/pages/guides/how-to-dynamic-client-registration.adoc +++ b/docs/modules/ROOT/pages/guides/how-to-dynamic-client-registration.adoc @@ -23,32 +23,24 @@ To enable, add the following configuration: include::{examples-dir}/main/java/sample/registration/SecurityConfig.java[] ---- -<1> Enable the xref:protocol-endpoints.adoc#oidc-client-registration-endpoint[OpenID Connect 1.0 Client Registration Endpoint] with client registration endpoint authentication providers for providing custom metadata. Providing custom metadata is optional. +<1> Enable the xref:protocol-endpoints.adoc#oidc-client-registration-endpoint[OpenID Connect 1.0 Client Registration Endpoint] with the default configuration. +<2> Optionally, customize the default ``AuthenticationProvider``'s to support custom client metadata parameters. -In order to accept custom client metadata when registering a client, a few additional implementation details -are necessary. +In order to support custom client metadata parameters when registering a client, a few additional implementation details are required. -[NOTE] -==== -The following example depicts custom metadata `logo_uri` (string type) and `contacts` (string array type) -==== +The following example shows a sample implementation of ``Converter``'s that support custom client metadata parameters (`logo_uri` and `contacts`) and are configured in `OidcClientRegistrationAuthenticationProvider` and `OidcClientConfigurationAuthenticationProvider`. -Create a set of custom `Converter` classes in order to retain custom client claims. - -[[sample.CustomMetadataConfig]] +[[sample.CustomClientMetadataConfig]] [source,java] ---- -include::{examples-dir}/main/java/sample/registration/CustomMetadataConfig.java[] +include::{examples-dir}/main/java/sample/registration/CustomClientMetadataConfig.java[] ---- -<1> Create a `Consumer>` implementation. -<2> Identify custom fields that should be accepted during client registration. -<3> Filter for `OidcClientRegistrationAuthenticationProvider` and `OidcClientConfigurationAuthenticationProvider` instances. -<4> Add a custom registered client `Converter` (implementation in #7) -<5> Add a custom client registration `Converter` to `OidcClientRegistrationAuthenticationProvider` (implementation in #8) -<6> Add a custom client registration `Converter` to `OidcClientConfigurationAuthenticationProvider` (implementation in #8) -<7> Custom registered client `Converter` implementation that adds custom claims to registered client settings. -<8> Custom client registration `Converter` implementation that modifies client registration claims with custom metadata. +<1> Define a `Consumer>` providing the ability to customize the default ``AuthenticationProvider``'s. +<2> Define custom client metadata parameters that are supported for client registration. +<3> Configure `OidcClientRegistrationAuthenticationProvider.setRegisteredClientConverter()` with a `CustomRegisteredClientConverter`. +<4> Configure `OidcClientRegistrationAuthenticationProvider.setClientRegistrationConverter()` with a `CustomClientRegistrationConverter`. +<5> Configure `OidcClientConfigurationAuthenticationProvider.setClientRegistrationConverter()` with a `CustomClientRegistrationConverter`. [[configure-client-registrar]] == Configure client registrar @@ -114,8 +106,8 @@ After the client is registered, the access token is invalidated. include::{examples-dir}/main/java/sample/registration/ClientRegistrar.java[] ---- -<1> A minimal representation of a client registration request. You may add additional client metadata parameters as per https://openid.net/specs/openid-connect-registration-1_0.html#RegistrationRequest[Client Registration Request]. This example request contains custom metadata fields `logo_uri` and `contacts`. -<2> A minimal representation of a client registration response. You may add additional client metadata parameters as per https://openid.net/specs/openid-connect-registration-1_0.html#RegistrationResponse[Client Registration Response]. This example response contains custom metadata fields `logo_uri` and `contacts`. +<1> A minimal representation of a client registration request. You may add additional client metadata parameters as per https://openid.net/specs/openid-connect-registration-1_0.html#RegistrationRequest[Client Registration Request]. This example request contains custom client metadata parameters `logo_uri` and `contacts`. +<2> A minimal representation of a client registration response. You may add additional client metadata parameters as per https://openid.net/specs/openid-connect-registration-1_0.html#RegistrationResponse[Client Registration Response]. This example response contains custom client metadata parameters `logo_uri` and `contacts`. <3> Example demonstrating client registration and client retrieval. <4> A sample client registration request object. <5> Register the client using the "initial" access token and client registration request object. diff --git a/docs/src/main/java/sample/registration/ClientRegistrar.java b/docs/src/main/java/sample/registration/ClientRegistrar.java index 942387f1..f0c805a5 100644 --- a/docs/src/main/java/sample/registration/ClientRegistrar.java +++ b/docs/src/main/java/sample/registration/ClientRegistrar.java @@ -78,7 +78,7 @@ public class ClientRegistrar { assert (clientRegistrationResponse.redirectUris().contains("https://client.example.org/callback2")); assert (!clientRegistrationResponse.registrationAccessToken().isEmpty()); assert (!clientRegistrationResponse.registrationClientUri().isEmpty()); - assert (clientRegistrationResponse.logoUri().contentEquals("https://client.example.org/logo")); // <6> + assert (clientRegistrationResponse.logoUri().contentEquals("https://client.example.org/logo")); assert (clientRegistrationResponse.contacts().size() == 2); assert (clientRegistrationResponse.contacts().contains("contact-1")); assert (clientRegistrationResponse.contacts().contains("contact-2")); diff --git a/docs/src/main/java/sample/registration/CustomMetadataConfig.java b/docs/src/main/java/sample/registration/CustomClientMetadataConfig.java similarity index 64% rename from docs/src/main/java/sample/registration/CustomMetadataConfig.java rename to docs/src/main/java/sample/registration/CustomClientMetadataConfig.java index 874b3c55..18dd6d85 100644 --- a/docs/src/main/java/sample/registration/CustomMetadataConfig.java +++ b/docs/src/main/java/sample/registration/CustomClientMetadataConfig.java @@ -15,6 +15,13 @@ */ package sample.registration; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.function.Consumer; +import java.util.function.Function; +import java.util.stream.Collectors; + import org.springframework.core.convert.converter.Converter; import org.springframework.security.authentication.AuthenticationProvider; import org.springframework.security.oauth2.server.authorization.client.RegisteredClient; @@ -26,84 +33,84 @@ import org.springframework.security.oauth2.server.authorization.oidc.converter.R import org.springframework.security.oauth2.server.authorization.settings.ClientSettings; import org.springframework.util.CollectionUtils; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.function.Consumer; -import java.util.function.Function; -import java.util.stream.Collectors; +public class CustomClientMetadataConfig { -public class CustomMetadataConfig { - public static Consumer> registeredClientConverters() { - List customClientMetadata = List.of("logo_uri", "contacts"); // <1> + public static Consumer> configureCustomClientMetadataConverters() { // <1> + List customClientMetadata = List.of("logo_uri", "contacts"); // <2> - return authenticationProviders -> // <2> - { - CustomRegisteredClientConverter registeredClientConverter = new CustomRegisteredClientConverter(customClientMetadata); - CustomClientRegistrationConverter clientRegistrationConverter = new CustomClientRegistrationConverter(customClientMetadata); + return (authenticationProviders) -> { + CustomRegisteredClientConverter registeredClientConverter = + new CustomRegisteredClientConverter(customClientMetadata); + CustomClientRegistrationConverter clientRegistrationConverter = + new CustomClientRegistrationConverter(customClientMetadata); - authenticationProviders.forEach(authenticationProvider -> { - if (authenticationProvider instanceof OidcClientRegistrationAuthenticationProvider provider) { // <3> - provider.setRegisteredClientConverter(registeredClientConverter); // <4> - provider.setClientRegistrationConverter(clientRegistrationConverter); // <5> + authenticationProviders.forEach((authenticationProvider) -> { + if (authenticationProvider instanceof OidcClientRegistrationAuthenticationProvider provider) { + provider.setRegisteredClientConverter(registeredClientConverter); // <3> + provider.setClientRegistrationConverter(clientRegistrationConverter); // <4> } - if (authenticationProvider instanceof OidcClientConfigurationAuthenticationProvider provider) { - provider.setClientRegistrationConverter(clientRegistrationConverter); // <6> + provider.setClientRegistrationConverter(clientRegistrationConverter); // <5> } }); }; } - static class CustomRegisteredClientConverter implements Converter { // <7> - private final List customMetadata; + private static class CustomRegisteredClientConverter + implements Converter { + private final List customClientMetadata; private final OidcClientRegistrationRegisteredClientConverter delegate; - CustomRegisteredClientConverter(List customMetadata) { - this.customMetadata = customMetadata; + private CustomRegisteredClientConverter(List customClientMetadata) { + this.customClientMetadata = customClientMetadata; this.delegate = new OidcClientRegistrationRegisteredClientConverter(); } + @Override public RegisteredClient convert(OidcClientRegistration clientRegistration) { - RegisteredClient convertedClient = delegate.convert(clientRegistration); - ClientSettings.Builder clientSettingsBuilder = ClientSettings - .withSettings(convertedClient.getClientSettings().getSettings()); - - if (!CollectionUtils.isEmpty(this.customMetadata)) { + RegisteredClient registeredClient = this.delegate.convert(clientRegistration); + ClientSettings.Builder clientSettingsBuilder = ClientSettings.withSettings( + registeredClient.getClientSettings().getSettings()); + if (!CollectionUtils.isEmpty(this.customClientMetadata)) { clientRegistration.getClaims().forEach((claim, value) -> { - if (this.customMetadata.contains(claim)) { + if (this.customClientMetadata.contains(claim)) { clientSettingsBuilder.setting(claim, value); } }); } - return RegisteredClient.from(convertedClient).clientSettings(clientSettingsBuilder.build()).build(); + return RegisteredClient.from(registeredClient) + .clientSettings(clientSettingsBuilder.build()) + .build(); } } - static class CustomClientRegistrationConverter implements Converter { // <8> - private final List customMetadata; + private static class CustomClientRegistrationConverter + implements Converter { + private final List customClientMetadata; private final RegisteredClientOidcClientRegistrationConverter delegate; - CustomClientRegistrationConverter(List customMetadata) { - this.customMetadata = customMetadata; + private CustomClientRegistrationConverter(List customClientMetadata) { + this.customClientMetadata = customClientMetadata; this.delegate = new RegisteredClientOidcClientRegistrationConverter(); } + @Override public OidcClientRegistration convert(RegisteredClient registeredClient) { - var clientRegistration = delegate.convert(registeredClient); + OidcClientRegistration clientRegistration = this.delegate.convert(registeredClient); Map claims = new HashMap<>(clientRegistration.getClaims()); - if (!CollectionUtils.isEmpty(customMetadata)) { + if (!CollectionUtils.isEmpty(this.customClientMetadata)) { ClientSettings clientSettings = registeredClient.getClientSettings(); - - claims.putAll(customMetadata.stream() - .filter(metadatum -> clientSettings.getSetting(metadatum) != null) + claims.putAll(this.customClientMetadata.stream() + .filter(metadata -> clientSettings.getSetting(metadata) != null) .collect(Collectors.toMap(Function.identity(), clientSettings::getSetting))); } + return OidcClientRegistration.withClaims(claims).build(); } + } } diff --git a/docs/src/main/java/sample/registration/SecurityConfig.java b/docs/src/main/java/sample/registration/SecurityConfig.java index 247edd7a..4a42255a 100644 --- a/docs/src/main/java/sample/registration/SecurityConfig.java +++ b/docs/src/main/java/sample/registration/SecurityConfig.java @@ -24,7 +24,7 @@ import org.springframework.security.oauth2.server.authorization.config.annotatio import org.springframework.security.oauth2.server.authorization.config.annotation.web.configurers.OAuth2AuthorizationServerConfigurer; import org.springframework.security.web.SecurityFilterChain; -import static sample.registration.CustomMetadataConfig.registeredClientConverters; +import static sample.registration.CustomClientMetadataConfig.configureCustomClientMetadataConverters; @Configuration @EnableWebSecurity @@ -33,13 +33,13 @@ public class SecurityConfig { @Bean public SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http) throws Exception { OAuth2AuthorizationServerConfiguration.applyDefaultSecurity(http); - http.getConfigurer(OAuth2AuthorizationServerConfigurer.class) - .oidc(oidc -> oidc.clientRegistrationEndpoint(endpoint -> { - endpoint.authenticationProviders(registeredClientConverters()); // <1> + .oidc(oidc -> oidc.clientRegistrationEndpoint(clientRegistrationEndpoint -> { // <1> + clientRegistrationEndpoint + .authenticationProviders(configureCustomClientMetadataConverters()); // <2> })); - - http.oauth2ResourceServer(oauth2ResourceServer -> oauth2ResourceServer.jwt(Customizer.withDefaults())); + http.oauth2ResourceServer(oauth2ResourceServer -> + oauth2ResourceServer.jwt(Customizer.withDefaults())); return http.build(); } diff --git a/docs/src/test/java/sample/registration/DynamicClientRegistrationTests.java b/docs/src/test/java/sample/registration/DynamicClientRegistrationTests.java index eb4cdcb4..e96ad15a 100644 --- a/docs/src/test/java/sample/registration/DynamicClientRegistrationTests.java +++ b/docs/src/test/java/sample/registration/DynamicClientRegistrationTests.java @@ -56,7 +56,7 @@ public class DynamicClientRegistrationTests { private String port; @Test - public void dynamicallyRegisterClientWithCustomMetadata() throws Exception { + public void dynamicallyRegisterClientWithCustomClientMetadata() throws Exception { MockHttpServletResponse tokenResponse = this.mvc.perform(post("/oauth2/token") .with(httpBasic("registrar-client", "secret")) .param(OAuth2ParameterNames.GRANT_TYPE, AuthorizationGrantType.CLIENT_CREDENTIALS.getValue())