diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/oidc/authentication/OidcClientConfigurationAuthenticationProvider.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/oidc/authentication/OidcClientConfigurationAuthenticationProvider.java
index c33c76fde..2df82b9b1 100644
--- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/oidc/authentication/OidcClientConfigurationAuthenticationProvider.java
+++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/oidc/authentication/OidcClientConfigurationAuthenticationProvider.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2020-2022 the original author or authors.
+ * Copyright 2020-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -36,6 +36,7 @@
import org.springframework.security.oauth2.server.authorization.client.RegisteredClient;
import org.springframework.security.oauth2.server.authorization.client.RegisteredClientRepository;
import org.springframework.security.oauth2.server.authorization.oidc.OidcClientRegistration;
+import org.springframework.security.oauth2.server.authorization.oidc.converter.RegisteredClientOidcClientRegistrationConverter;
import org.springframework.security.oauth2.server.resource.authentication.AbstractOAuth2TokenAuthenticationToken;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
@@ -46,11 +47,13 @@
* @author Ovidiu Popa
* @author Joe Grandja
* @author Rafal Lewczuk
+ * @author Dmitriy Dubson
* @since 0.4.0
* @see RegisteredClientRepository
* @see OAuth2AuthorizationService
* @see OidcClientRegistrationAuthenticationToken
* @see OidcClientRegistrationAuthenticationProvider
+ * @see RegisteredClientOidcClientRegistrationConverter
* @see 4. Client Configuration Endpoint
*/
public final class OidcClientConfigurationAuthenticationProvider implements AuthenticationProvider {
@@ -58,7 +61,7 @@ public final class OidcClientConfigurationAuthenticationProvider implements Auth
private final Log logger = LogFactory.getLog(getClass());
private final RegisteredClientRepository registeredClientRepository;
private final OAuth2AuthorizationService authorizationService;
- private final Converter clientRegistrationConverter;
+ private Converter clientRegistrationConverter;
/**
* Constructs an {@code OidcClientConfigurationAuthenticationProvider} using the provided parameters.
@@ -75,6 +78,17 @@ public OidcClientConfigurationAuthenticationProvider(RegisteredClientRepository
this.clientRegistrationConverter = new RegisteredClientOidcClientRegistrationConverter();
}
+ /**
+ * Sets the {@link Converter} used for converting an {@link RegisteredClient} to a {@link OidcClientRegistration}.
+ *
+ * @param clientRegistrationConverter the {@link Converter} used for converting an {@link RegisteredClient} to a {@link OidcClientRegistration}
+ * @since 1.2.0
+ */
+ public void setClientRegistrationConverter(Converter clientRegistrationConverter) {
+ Assert.notNull(clientRegistrationConverter, "clientRegistrationConverter cannot be null");
+ this.clientRegistrationConverter = clientRegistrationConverter;
+ }
+
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
OidcClientRegistrationAuthenticationToken clientRegistrationAuthentication =
diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/oidc/authentication/OidcClientRegistrationAuthenticationProvider.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/oidc/authentication/OidcClientRegistrationAuthenticationProvider.java
index fa7a9049e..9bb66511e 100644
--- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/oidc/authentication/OidcClientRegistrationAuthenticationProvider.java
+++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/oidc/authentication/OidcClientRegistrationAuthenticationProvider.java
@@ -17,15 +17,12 @@
import java.net.URI;
import java.net.URISyntaxException;
-import java.time.Instant;
-import java.util.Base64;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
-import java.util.UUID;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
@@ -35,8 +32,6 @@
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.crypto.factory.PasswordEncoderFactories;
-import org.springframework.security.crypto.keygen.Base64StringKeyGenerator;
-import org.springframework.security.crypto.keygen.StringKeyGenerator;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.core.AuthorizationGrantType;
import org.springframework.security.oauth2.core.ClaimAccessor;
@@ -46,7 +41,6 @@
import org.springframework.security.oauth2.core.OAuth2Error;
import org.springframework.security.oauth2.core.OAuth2ErrorCodes;
import org.springframework.security.oauth2.core.OAuth2Token;
-import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationResponseType;
import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;
import org.springframework.security.oauth2.jose.jws.MacAlgorithm;
import org.springframework.security.oauth2.jose.jws.SignatureAlgorithm;
@@ -59,8 +53,8 @@
import org.springframework.security.oauth2.server.authorization.context.AuthorizationServerContextHolder;
import org.springframework.security.oauth2.server.authorization.oidc.OidcClientMetadataClaimNames;
import org.springframework.security.oauth2.server.authorization.oidc.OidcClientRegistration;
-import org.springframework.security.oauth2.server.authorization.settings.ClientSettings;
-import org.springframework.security.oauth2.server.authorization.settings.TokenSettings;
+import org.springframework.security.oauth2.server.authorization.oidc.converter.OidcClientRegistrationRegisteredClientConverter;
+import org.springframework.security.oauth2.server.authorization.oidc.converter.RegisteredClientOidcClientRegistrationConverter;
import org.springframework.security.oauth2.server.authorization.token.DefaultOAuth2TokenContext;
import org.springframework.security.oauth2.server.authorization.token.OAuth2TokenContext;
import org.springframework.security.oauth2.server.authorization.token.OAuth2TokenGenerator;
@@ -75,6 +69,7 @@
* @author Ovidiu Popa
* @author Joe Grandja
* @author Rafal Lewczuk
+ * @author Dmitriy Dubson
* @since 0.1.1
* @see RegisteredClientRepository
* @see OAuth2AuthorizationService
@@ -91,7 +86,7 @@ public final class OidcClientRegistrationAuthenticationProvider implements Authe
private final RegisteredClientRepository registeredClientRepository;
private final OAuth2AuthorizationService authorizationService;
private final OAuth2TokenGenerator extends OAuth2Token> tokenGenerator;
- private final Converter clientRegistrationConverter;
+ private Converter clientRegistrationConverter;
private Converter registeredClientConverter;
private PasswordEncoder passwordEncoder;
@@ -172,6 +167,17 @@ public void setRegisteredClientConverter(Converter clientRegistrationConverter) {
+ Assert.notNull(clientRegistrationConverter, "clientRegistrationConverter cannot be null");
+ this.clientRegistrationConverter = clientRegistrationConverter;
+ }
+
/**
* Sets the {@link PasswordEncoder} used to encode the {@link RegisteredClient#getClientSecret() client secret}.
* If not set, the client secret will be encoded using {@link PasswordEncoderFactories#createDelegatingPasswordEncoder()}.
@@ -368,89 +374,4 @@ private static void throwInvalidClientRegistration(String errorCode, String fiel
throw new OAuth2AuthenticationException(error);
}
- private static final class OidcClientRegistrationRegisteredClientConverter implements Converter {
- private static final StringKeyGenerator CLIENT_ID_GENERATOR = new Base64StringKeyGenerator(
- Base64.getUrlEncoder().withoutPadding(), 32);
- private static final StringKeyGenerator CLIENT_SECRET_GENERATOR = new Base64StringKeyGenerator(
- Base64.getUrlEncoder().withoutPadding(), 48);
-
- @Override
- public RegisteredClient convert(OidcClientRegistration clientRegistration) {
- // @formatter:off
- RegisteredClient.Builder builder = RegisteredClient.withId(UUID.randomUUID().toString())
- .clientId(CLIENT_ID_GENERATOR.generateKey())
- .clientIdIssuedAt(Instant.now())
- .clientName(clientRegistration.getClientName());
-
- if (ClientAuthenticationMethod.CLIENT_SECRET_POST.getValue().equals(clientRegistration.getTokenEndpointAuthenticationMethod())) {
- builder
- .clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_POST)
- .clientSecret(CLIENT_SECRET_GENERATOR.generateKey());
- } else if (ClientAuthenticationMethod.CLIENT_SECRET_JWT.getValue().equals(clientRegistration.getTokenEndpointAuthenticationMethod())) {
- builder
- .clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_JWT)
- .clientSecret(CLIENT_SECRET_GENERATOR.generateKey());
- } else if (ClientAuthenticationMethod.PRIVATE_KEY_JWT.getValue().equals(clientRegistration.getTokenEndpointAuthenticationMethod())) {
- builder.clientAuthenticationMethod(ClientAuthenticationMethod.PRIVATE_KEY_JWT);
- } else {
- builder
- .clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC)
- .clientSecret(CLIENT_SECRET_GENERATOR.generateKey());
- }
-
- builder.redirectUris(redirectUris ->
- redirectUris.addAll(clientRegistration.getRedirectUris()));
-
- if (!CollectionUtils.isEmpty(clientRegistration.getPostLogoutRedirectUris())) {
- builder.postLogoutRedirectUris(postLogoutRedirectUris ->
- postLogoutRedirectUris.addAll(clientRegistration.getPostLogoutRedirectUris()));
- }
-
- if (!CollectionUtils.isEmpty(clientRegistration.getGrantTypes())) {
- builder.authorizationGrantTypes(authorizationGrantTypes ->
- clientRegistration.getGrantTypes().forEach(grantType ->
- authorizationGrantTypes.add(new AuthorizationGrantType(grantType))));
- } else {
- builder.authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE);
- }
- if (CollectionUtils.isEmpty(clientRegistration.getResponseTypes()) ||
- clientRegistration.getResponseTypes().contains(OAuth2AuthorizationResponseType.CODE.getValue())) {
- builder.authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE);
- }
-
- if (!CollectionUtils.isEmpty(clientRegistration.getScopes())) {
- builder.scopes(scopes ->
- scopes.addAll(clientRegistration.getScopes()));
- }
-
- ClientSettings.Builder clientSettingsBuilder = ClientSettings.builder()
- .requireProofKey(true)
- .requireAuthorizationConsent(true);
-
- if (ClientAuthenticationMethod.CLIENT_SECRET_JWT.getValue().equals(clientRegistration.getTokenEndpointAuthenticationMethod())) {
- MacAlgorithm macAlgorithm = MacAlgorithm.from(clientRegistration.getTokenEndpointAuthenticationSigningAlgorithm());
- if (macAlgorithm == null) {
- macAlgorithm = MacAlgorithm.HS256;
- }
- clientSettingsBuilder.tokenEndpointAuthenticationSigningAlgorithm(macAlgorithm);
- } else if (ClientAuthenticationMethod.PRIVATE_KEY_JWT.getValue().equals(clientRegistration.getTokenEndpointAuthenticationMethod())) {
- SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.from(clientRegistration.getTokenEndpointAuthenticationSigningAlgorithm());
- if (signatureAlgorithm == null) {
- signatureAlgorithm = SignatureAlgorithm.RS256;
- }
- clientSettingsBuilder.tokenEndpointAuthenticationSigningAlgorithm(signatureAlgorithm);
- clientSettingsBuilder.jwkSetUrl(clientRegistration.getJwkSetUrl().toString());
- }
-
- builder
- .clientSettings(clientSettingsBuilder.build())
- .tokenSettings(TokenSettings.builder()
- .idTokenSignatureAlgorithm(SignatureAlgorithm.RS256)
- .build());
-
- return builder.build();
- // @formatter:on
- }
-
- }
}
diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/oidc/converter/OidcClientRegistrationRegisteredClientConverter.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/oidc/converter/OidcClientRegistrationRegisteredClientConverter.java
new file mode 100644
index 000000000..e4ce51009
--- /dev/null
+++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/oidc/converter/OidcClientRegistrationRegisteredClientConverter.java
@@ -0,0 +1,126 @@
+/*
+ * Copyright 2020-2023 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.springframework.security.oauth2.server.authorization.oidc.converter;
+
+import org.springframework.core.convert.converter.Converter;
+import org.springframework.security.crypto.keygen.Base64StringKeyGenerator;
+import org.springframework.security.crypto.keygen.StringKeyGenerator;
+import org.springframework.security.oauth2.core.AuthorizationGrantType;
+import org.springframework.security.oauth2.core.ClientAuthenticationMethod;
+import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationResponseType;
+import org.springframework.security.oauth2.jose.jws.MacAlgorithm;
+import org.springframework.security.oauth2.jose.jws.SignatureAlgorithm;
+import org.springframework.security.oauth2.server.authorization.client.RegisteredClient;
+import org.springframework.security.oauth2.server.authorization.oidc.OidcClientRegistration;
+import org.springframework.security.oauth2.server.authorization.settings.ClientSettings;
+import org.springframework.security.oauth2.server.authorization.settings.TokenSettings;
+import org.springframework.util.CollectionUtils;
+
+import java.time.Instant;
+import java.util.Base64;
+import java.util.UUID;
+
+/**
+ * @author Joe Grandja
+ * @author Dmitriy Dubson
+ * @since 1.2.0
+ */
+public final class OidcClientRegistrationRegisteredClientConverter implements Converter {
+ private static final StringKeyGenerator CLIENT_ID_GENERATOR = new Base64StringKeyGenerator(
+ Base64.getUrlEncoder().withoutPadding(), 32);
+ private static final StringKeyGenerator CLIENT_SECRET_GENERATOR = new Base64StringKeyGenerator(
+ Base64.getUrlEncoder().withoutPadding(), 48);
+
+ @Override
+ public RegisteredClient convert(OidcClientRegistration clientRegistration) {
+ // @formatter:off
+ RegisteredClient.Builder builder = RegisteredClient.withId(UUID.randomUUID().toString())
+ .clientId(CLIENT_ID_GENERATOR.generateKey())
+ .clientIdIssuedAt(Instant.now())
+ .clientName(clientRegistration.getClientName());
+
+ if (ClientAuthenticationMethod.CLIENT_SECRET_POST.getValue().equals(clientRegistration.getTokenEndpointAuthenticationMethod())) {
+ builder
+ .clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_POST)
+ .clientSecret(CLIENT_SECRET_GENERATOR.generateKey());
+ } else if (ClientAuthenticationMethod.CLIENT_SECRET_JWT.getValue().equals(clientRegistration.getTokenEndpointAuthenticationMethod())) {
+ builder
+ .clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_JWT)
+ .clientSecret(CLIENT_SECRET_GENERATOR.generateKey());
+ } else if (ClientAuthenticationMethod.PRIVATE_KEY_JWT.getValue().equals(clientRegistration.getTokenEndpointAuthenticationMethod())) {
+ builder.clientAuthenticationMethod(ClientAuthenticationMethod.PRIVATE_KEY_JWT);
+ } else {
+ builder
+ .clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC)
+ .clientSecret(CLIENT_SECRET_GENERATOR.generateKey());
+ }
+
+ builder.redirectUris(redirectUris ->
+ redirectUris.addAll(clientRegistration.getRedirectUris()));
+
+ if (!CollectionUtils.isEmpty(clientRegistration.getPostLogoutRedirectUris())) {
+ builder.postLogoutRedirectUris(postLogoutRedirectUris ->
+ postLogoutRedirectUris.addAll(clientRegistration.getPostLogoutRedirectUris()));
+ }
+
+ if (!CollectionUtils.isEmpty(clientRegistration.getGrantTypes())) {
+ builder.authorizationGrantTypes(authorizationGrantTypes ->
+ clientRegistration.getGrantTypes().forEach(grantType ->
+ authorizationGrantTypes.add(new AuthorizationGrantType(grantType))));
+ } else {
+ builder.authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE);
+ }
+ if (CollectionUtils.isEmpty(clientRegistration.getResponseTypes()) ||
+ clientRegistration.getResponseTypes().contains(OAuth2AuthorizationResponseType.CODE.getValue())) {
+ builder.authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE);
+ }
+
+ if (!CollectionUtils.isEmpty(clientRegistration.getScopes())) {
+ builder.scopes(scopes ->
+ scopes.addAll(clientRegistration.getScopes()));
+ }
+
+ ClientSettings.Builder clientSettingsBuilder = ClientSettings.builder()
+ .requireProofKey(true)
+ .requireAuthorizationConsent(true);
+
+ if (ClientAuthenticationMethod.CLIENT_SECRET_JWT.getValue().equals(clientRegistration.getTokenEndpointAuthenticationMethod())) {
+ MacAlgorithm macAlgorithm = MacAlgorithm.from(clientRegistration.getTokenEndpointAuthenticationSigningAlgorithm());
+ if (macAlgorithm == null) {
+ macAlgorithm = MacAlgorithm.HS256;
+ }
+ clientSettingsBuilder.tokenEndpointAuthenticationSigningAlgorithm(macAlgorithm);
+ } else if (ClientAuthenticationMethod.PRIVATE_KEY_JWT.getValue().equals(clientRegistration.getTokenEndpointAuthenticationMethod())) {
+ SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.from(clientRegistration.getTokenEndpointAuthenticationSigningAlgorithm());
+ if (signatureAlgorithm == null) {
+ signatureAlgorithm = SignatureAlgorithm.RS256;
+ }
+ clientSettingsBuilder.tokenEndpointAuthenticationSigningAlgorithm(signatureAlgorithm);
+ clientSettingsBuilder.jwkSetUrl(clientRegistration.getJwkSetUrl().toString());
+ }
+
+ builder
+ .clientSettings(clientSettingsBuilder.build())
+ .tokenSettings(TokenSettings.builder()
+ .idTokenSignatureAlgorithm(SignatureAlgorithm.RS256)
+ .build());
+
+ return builder.build();
+ // @formatter:on
+ }
+
+}
+
diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/oidc/authentication/RegisteredClientOidcClientRegistrationConverter.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/oidc/converter/RegisteredClientOidcClientRegistrationConverter.java
similarity index 96%
rename from oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/oidc/authentication/RegisteredClientOidcClientRegistrationConverter.java
rename to oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/oidc/converter/RegisteredClientOidcClientRegistrationConverter.java
index 7cd62d223..76c433d46 100644
--- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/oidc/authentication/RegisteredClientOidcClientRegistrationConverter.java
+++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/oidc/converter/RegisteredClientOidcClientRegistrationConverter.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.springframework.security.oauth2.server.authorization.oidc.authentication;
+package org.springframework.security.oauth2.server.authorization.oidc.converter;
import org.springframework.core.convert.converter.Converter;
import org.springframework.security.oauth2.core.AuthorizationGrantType;
@@ -29,9 +29,9 @@
/**
* @author Joe Grandja
- * @since 0.4.0
+ * @since 1.2.0
*/
-final class RegisteredClientOidcClientRegistrationConverter implements Converter {
+public final class RegisteredClientOidcClientRegistrationConverter implements Converter {
@Override
public OidcClientRegistration convert(RegisteredClient registeredClient) {
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 141bfb56e..aaf762bc2 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
@@ -17,11 +17,13 @@
import java.time.Instant;
import java.time.temporal.ChronoUnit;
-import java.util.Base64;
import java.util.Collections;
+import java.util.HashMap;
import java.util.List;
-import java.util.UUID;
+import java.util.Map;
import java.util.function.Consumer;
+import java.util.function.Function;
+import java.util.stream.Collectors;
import com.nimbusds.jose.jwk.JWKSet;
import com.nimbusds.jose.jwk.source.JWKSource;
@@ -59,8 +61,6 @@
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.crypto.factory.PasswordEncoderFactories;
-import org.springframework.security.crypto.keygen.Base64StringKeyGenerator;
-import org.springframework.security.crypto.keygen.StringKeyGenerator;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.core.AuthorizationGrantType;
import org.springframework.security.oauth2.core.ClientAuthenticationMethod;
@@ -71,7 +71,6 @@
import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;
import org.springframework.security.oauth2.core.http.converter.OAuth2AccessTokenResponseHttpMessageConverter;
import org.springframework.security.oauth2.jose.TestJwks;
-import org.springframework.security.oauth2.jose.jws.MacAlgorithm;
import org.springframework.security.oauth2.jose.jws.SignatureAlgorithm;
import org.springframework.security.oauth2.jwt.JwsHeader;
import org.springframework.security.oauth2.jwt.Jwt;
@@ -92,11 +91,12 @@
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;
+import org.springframework.security.oauth2.server.authorization.oidc.converter.OidcClientRegistrationRegisteredClientConverter;
+import org.springframework.security.oauth2.server.authorization.oidc.converter.RegisteredClientOidcClientRegistrationConverter;
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.settings.TokenSettings;
import org.springframework.security.oauth2.server.authorization.test.SpringTestContext;
import org.springframework.security.oauth2.server.authorization.test.SpringTestContextExtension;
import org.springframework.security.web.SecurityFilterChain;
@@ -131,6 +131,7 @@
*
* @author Ovidiu Popa
* @author Joe Grandja
+ * @author Dmitriy Dubson
*/
@ExtendWith(SpringTestContextExtension.class)
public class OidcClientRegistrationTests {
@@ -420,6 +421,7 @@ public void requestWhenClientRegistersWithCustomMetadataThenSavedToRegisteredCli
.scope("scope2")
.claim("custom-metadata-name-1", "value-1")
.claim("custom-metadata-name-2", "value-2")
+ .claim("non-registered-custom-metadata", "value-3")
.build();
// @formatter:on
@@ -428,10 +430,15 @@ public void requestWhenClientRegistersWithCustomMetadataThenSavedToRegisteredCli
RegisteredClient registeredClient = this.registeredClientRepository.findByClientId(
clientRegistrationResponse.getClientId());
+ assertThat(clientRegistrationResponse.getClaim("custom-metadata-name-1")).isEqualTo("value-1");
+ assertThat(clientRegistrationResponse.getClaim("custom-metadata-name-2")).isEqualTo("value-2");
+ assertThat(clientRegistrationResponse.getClaim("non-registered-custom-metadata")).isNull();
+
assertThat(registeredClient.getClientSettings().getSetting("custom-metadata-name-1"))
.isEqualTo("value-1");
assertThat(registeredClient.getClientSettings().getSetting("custom-metadata-name-2"))
.isEqualTo("value-2");
+ assertThat(registeredClient.getClientSettings().getSetting("non-registered-custom-metadata")).isNull();
}
private OidcClientRegistration registerClient(OidcClientRegistration clientRegistration) throws Exception {
@@ -581,7 +588,7 @@ public SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity h
oidc
.clientRegistrationEndpoint(clientRegistration ->
clientRegistration
- .authenticationProviders(configureRegisteredClientConverter())
+ .authenticationProviders(configureRegisteredClientConverters())
)
);
RequestMatcher endpointsMatcher = authorizationServerConfigurer.getEndpointsMatcher();
@@ -600,111 +607,18 @@ public SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity h
}
// @formatter:on
- private Consumer> configureRegisteredClientConverter() {
- return (authenticationProviders) -> {
- authenticationProviders.forEach((authenticationProvider) -> {
- if (authenticationProvider instanceof OidcClientRegistrationAuthenticationProvider) {
- ((OidcClientRegistrationAuthenticationProvider) authenticationProvider)
- .setRegisteredClientConverter(new OidcClientRegistrationRegisteredClientConverter());
- }
- });
- };
- }
-
- // NOTE:
- // This is a copy of OidcClientRegistrationAuthenticationProvider.OidcClientRegistrationRegisteredClientConverter
- // with a minor enhancement supporting custom metadata claims.
- private static final class OidcClientRegistrationRegisteredClientConverter implements Converter {
- private static final StringKeyGenerator CLIENT_ID_GENERATOR = new Base64StringKeyGenerator(
- Base64.getUrlEncoder().withoutPadding(), 32);
- private static final StringKeyGenerator CLIENT_SECRET_GENERATOR = new Base64StringKeyGenerator(
- Base64.getUrlEncoder().withoutPadding(), 48);
-
- @Override
- public RegisteredClient convert(OidcClientRegistration clientRegistration) {
- // @formatter:off
- RegisteredClient.Builder builder = RegisteredClient.withId(UUID.randomUUID().toString())
- .clientId(CLIENT_ID_GENERATOR.generateKey())
- .clientIdIssuedAt(Instant.now())
- .clientName(clientRegistration.getClientName());
-
- if (ClientAuthenticationMethod.CLIENT_SECRET_POST.getValue().equals(clientRegistration.getTokenEndpointAuthenticationMethod())) {
- builder
- .clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_POST)
- .clientSecret(CLIENT_SECRET_GENERATOR.generateKey());
- } else if (ClientAuthenticationMethod.CLIENT_SECRET_JWT.getValue().equals(clientRegistration.getTokenEndpointAuthenticationMethod())) {
- builder
- .clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_JWT)
- .clientSecret(CLIENT_SECRET_GENERATOR.generateKey());
- } else if (ClientAuthenticationMethod.PRIVATE_KEY_JWT.getValue().equals(clientRegistration.getTokenEndpointAuthenticationMethod())) {
- builder.clientAuthenticationMethod(ClientAuthenticationMethod.PRIVATE_KEY_JWT);
- } else {
- builder
- .clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC)
- .clientSecret(CLIENT_SECRET_GENERATOR.generateKey());
- }
-
- builder.redirectUris(redirectUris ->
- redirectUris.addAll(clientRegistration.getRedirectUris()));
-
- if (!CollectionUtils.isEmpty(clientRegistration.getPostLogoutRedirectUris())) {
- builder.postLogoutRedirectUris(postLogoutRedirectUris ->
- postLogoutRedirectUris.addAll(clientRegistration.getPostLogoutRedirectUris()));
- }
-
- if (!CollectionUtils.isEmpty(clientRegistration.getGrantTypes())) {
- builder.authorizationGrantTypes(authorizationGrantTypes ->
- clientRegistration.getGrantTypes().forEach(grantType ->
- authorizationGrantTypes.add(new AuthorizationGrantType(grantType))));
- } else {
- builder.authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE);
- }
- if (CollectionUtils.isEmpty(clientRegistration.getResponseTypes()) ||
- clientRegistration.getResponseTypes().contains(OAuth2AuthorizationResponseType.CODE.getValue())) {
- builder.authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE);
- }
-
- if (!CollectionUtils.isEmpty(clientRegistration.getScopes())) {
- builder.scopes(scopes ->
- scopes.addAll(clientRegistration.getScopes()));
- }
-
- ClientSettings.Builder clientSettingsBuilder = ClientSettings.builder()
- .requireProofKey(true)
- .requireAuthorizationConsent(true);
-
- if (ClientAuthenticationMethod.CLIENT_SECRET_JWT.getValue().equals(clientRegistration.getTokenEndpointAuthenticationMethod())) {
- MacAlgorithm macAlgorithm = MacAlgorithm.from(clientRegistration.getTokenEndpointAuthenticationSigningAlgorithm());
- if (macAlgorithm == null) {
- macAlgorithm = MacAlgorithm.HS256;
- }
- clientSettingsBuilder.tokenEndpointAuthenticationSigningAlgorithm(macAlgorithm);
- } else if (ClientAuthenticationMethod.PRIVATE_KEY_JWT.getValue().equals(clientRegistration.getTokenEndpointAuthenticationMethod())) {
- SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.from(clientRegistration.getTokenEndpointAuthenticationSigningAlgorithm());
- if (signatureAlgorithm == null) {
- signatureAlgorithm = SignatureAlgorithm.RS256;
- }
- clientSettingsBuilder.tokenEndpointAuthenticationSigningAlgorithm(signatureAlgorithm);
- clientSettingsBuilder.jwkSetUrl(clientRegistration.getJwkSetUrl().toString());
- }
-
- // Add custom metadata claims
- clientRegistration.getClaims().forEach((claim, value) -> {
- if (claim.startsWith("custom-metadata")) {
- clientSettingsBuilder.setting(claim, value);
- }
- });
-
- builder
- .clientSettings(clientSettingsBuilder.build())
- .tokenSettings(TokenSettings.builder()
- .idTokenSignatureAlgorithm(SignatureAlgorithm.RS256)
- .build());
-
- return builder.build();
- // @formatter:on
- }
-
+ private Consumer> configureRegisteredClientConverters() {
+ // @formatter:off
+ return (authenticationProviders) ->
+ authenticationProviders.forEach(authenticationProvider -> {
+ List customClientMetadata = List.of("custom-metadata-name-1", "custom-metadata-name-2");
+
+ if (authenticationProvider instanceof OidcClientRegistrationAuthenticationProvider provider) {
+ provider.setRegisteredClientConverter(new CustomRegisteredClientConverter(customClientMetadata));
+ provider.setClientRegistrationConverter(new CustomClientRegistrationConverter(customClientMetadata));
+ }
+ });
+ // @formatter:on
}
}
@@ -781,4 +695,54 @@ PasswordEncoder passwordEncoder() {
}
+ static class CustomClientRegistrationConverter implements Converter {
+ private final List customMetadata;
+
+ private final RegisteredClientOidcClientRegistrationConverter delegate;
+
+ CustomClientRegistrationConverter(List customMetadata) {
+ this.customMetadata = customMetadata;
+ this.delegate = new RegisteredClientOidcClientRegistrationConverter();
+ }
+
+ public OidcClientRegistration convert(RegisteredClient registeredClient) {
+ var clientRegistration = delegate.convert(registeredClient);
+ Map claims = new HashMap<>(clientRegistration.getClaims());
+ if (!CollectionUtils.isEmpty(customMetadata)) {
+ ClientSettings clientSettings = registeredClient.getClientSettings();
+
+ claims.putAll(customMetadata.stream()
+ .filter(metadatum -> clientSettings.getSetting(metadatum) != null)
+ .collect(Collectors.toMap(Function.identity(), clientSettings::getSetting)));
+ }
+ return OidcClientRegistration.withClaims(claims).build();
+ }
+ }
+
+ static class CustomRegisteredClientConverter implements Converter {
+ private final List customMetadata;
+
+ private final OidcClientRegistrationRegisteredClientConverter delegate;
+
+ CustomRegisteredClientConverter(List customMetadata) {
+ this.customMetadata = customMetadata;
+ this.delegate = new OidcClientRegistrationRegisteredClientConverter();
+ }
+
+ public RegisteredClient convert(OidcClientRegistration clientRegistration) {
+ RegisteredClient convertedClient = delegate.convert(clientRegistration);
+ ClientSettings.Builder clientSettingsBuilder = ClientSettings
+ .withSettings(convertedClient.getClientSettings().getSettings());
+
+ if (!CollectionUtils.isEmpty(this.customMetadata)) {
+ clientRegistration.getClaims().forEach((claim, value) -> {
+ if (this.customMetadata.contains(claim)) {
+ clientSettingsBuilder.setting(claim, value);
+ }
+ });
+ }
+
+ return RegisteredClient.from(convertedClient).clientSettings(clientSettingsBuilder.build()).build();
+ }
+ }
}
diff --git a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/oidc/authentication/OidcClientConfigurationAuthenticationProviderTests.java b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/oidc/authentication/OidcClientConfigurationAuthenticationProviderTests.java
index 36f86250f..02f08fc86 100644
--- a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/oidc/authentication/OidcClientConfigurationAuthenticationProviderTests.java
+++ b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/oidc/authentication/OidcClientConfigurationAuthenticationProviderTests.java
@@ -378,6 +378,13 @@ public void authenticateWhenValidAccessTokenThenReturnClientRegistration() {
assertThat(clientRegistrationResult.getRegistrationAccessToken()).isNull();
}
+ @Test
+ public void setClientRegistrationConverterWhenNullThenThrowIllegalArgumentException() {
+ assertThatIllegalArgumentException()
+ .isThrownBy(() -> this.authenticationProvider.setClientRegistrationConverter(null))
+ .withMessage("clientRegistrationConverter cannot be null");
+ }
+
private static Jwt createJwtClientConfiguration() {
return createJwt(Collections.singleton("client.read"));
}
diff --git a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/oidc/authentication/OidcClientRegistrationAuthenticationProviderTests.java b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/oidc/authentication/OidcClientRegistrationAuthenticationProviderTests.java
index c7b04d746..a7357585d 100644
--- a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/oidc/authentication/OidcClientRegistrationAuthenticationProviderTests.java
+++ b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/oidc/authentication/OidcClientRegistrationAuthenticationProviderTests.java
@@ -158,6 +158,13 @@ public void setRegisteredClientConverterWhenNullThenThrowIllegalArgumentExceptio
.withMessage("registeredClientConverter cannot be null");
}
+ @Test
+ public void setClientRegistrationConverterWhenNullThenThrowIllegalArgumentException() {
+ assertThatIllegalArgumentException()
+ .isThrownBy(() -> this.authenticationProvider.setClientRegistrationConverter(null))
+ .withMessage("clientRegistrationConverter cannot be null");
+ }
+
@Test
public void setPasswordEncoderWhenNullThenThrowIllegalArgumentException() {
assertThatThrownBy(() -> this.authenticationProvider.setPasswordEncoder(null))