浏览代码

Polish gh-427

Joe Grandja 3 年之前
父节点
当前提交
c7f01f0795
共有 13 个文件被更改,包括 378 次插入584 次删除
  1. 3 3
      oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/core/oidc/OidcClientMetadataClaimAccessor.java
  2. 2 2
      oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/core/oidc/OidcClientMetadataClaimNames.java
  3. 8 6
      oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/core/oidc/OidcClientRegistration.java
  4. 6 2
      oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/oidc/authentication/JwtUtils.java
  5. 121 90
      oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/oidc/authentication/OidcClientRegistrationAuthenticationProvider.java
  6. 12 7
      oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/oidc/authentication/OidcClientRegistrationAuthenticationToken.java
  7. 23 44
      oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/oidc/web/OidcClientRegistrationEndpointFilter.java
  8. 44 255
      oauth2-authorization-server/src/test/java/org/springframework/security/config/annotation/web/configurers/oauth2/server/authorization/OidcClientRegistrationTests.java
  9. 7 7
      oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/core/oidc/OidcClientRegistrationTests.java
  10. 3 3
      oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/core/oidc/http/converter/OidcClientRegistrationHttpMessageConverterTests.java
  11. 105 109
      oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/oidc/authentication/OidcClientRegistrationAuthenticationProviderTests.java
  12. 2 2
      oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/oidc/authentication/OidcClientRegistrationAuthenticationTokenTests.java
  13. 42 54
      oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/oidc/web/OidcClientRegistrationEndpointFilterTests.java

+ 3 - 3
oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/core/oidc/OidcClientMetadataClaimAccessor.java

@@ -146,12 +146,12 @@ public interface OidcClientMetadataClaimAccessor extends ClaimAccessor {
 	}
 
 	/**
-	 * Returns the {@code URL} of the OAuth 2.0 Client Configuration Endpoint.
+	 * Returns the {@code URL} of the Client Configuration Endpoint where the Registration Access Token can be used.
 	 *
-	 * @return the {@code URL} of the OAuth 2.0 Client Configuration Endpoint
+	 * @return the {@code URL} of the Client Configuration Endpoint where the Registration Access Token can be used
 	 * @since 0.2.1
 	 */
-	default URL getRegistrationClientUri() {
+	default URL getRegistrationClientUrl() {
 		return getClaimAsURL(OidcClientMetadataClaimNames.REGISTRATION_CLIENT_URI);
 	}
 

+ 2 - 2
oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/core/oidc/OidcClientMetadataClaimNames.java

@@ -84,13 +84,13 @@ public interface OidcClientMetadataClaimNames {
 	String ID_TOKEN_SIGNED_RESPONSE_ALG = "id_token_signed_response_alg";
 
 	/**
-	 * {@code registration_access_token} - Registration Access Token that can be used at the Client Configuration Endpoint to perform subsequent operations upon the Client registration
+	 * {@code registration_access_token} - the Registration Access Token that can be used at the Client Configuration Endpoint
 	 * @since 0.2.1
 	 */
 	String REGISTRATION_ACCESS_TOKEN = "registration_access_token";
 
 	/**
-	 * {@code registration_client_uri} - the {@code URL} of the OAuth 2.0 Client Configuration Endpoint
+	 * {@code registration_client_uri} - the {@code URL} of the Client Configuration Endpoint where the Registration Access Token can be used
 	 * @since 0.2.1
 	 */
 	String REGISTRATION_CLIENT_URI = "registration_client_uri";

+ 8 - 6
oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/core/oidc/OidcClientRegistration.java

@@ -252,23 +252,25 @@ public final class OidcClientRegistration implements OidcClientMetadataClaimAcce
 		}
 
 		/**
-		 * Sets the Registration Access Token that can be used at the Client Configuration Endpoint to perform subsequent operations upon the Client registration, OPTIONAL.
+		 * Sets the Registration Access Token that can be used at the Client Configuration Endpoint, OPTIONAL.
 		 *
-		 * @param registrationAccessToken the Registration Access Token that can be used at the Client Configuration Endpoint to perform subsequent operations upon the Client registration
+		 * @param registrationAccessToken the Registration Access Token that can be used at the Client Configuration Endpoint
 		 * @return the {@link Builder} for further configuration
+		 * @since 0.2.1
 		 */
 		public Builder registrationAccessToken(String registrationAccessToken) {
 			return claim(OidcClientMetadataClaimNames.REGISTRATION_ACCESS_TOKEN, registrationAccessToken);
 		}
 
 		/**
-		 * Sets the {@code URL} of the OAuth 2.0 Client Configuration Endpoint, OPTIONAL.
+		 * Sets the {@code URL} of the Client Configuration Endpoint where the Registration Access Token can be used, OPTIONAL.
 		 *
-		 * @param registrationClientUri the {@code URL} of the OAuth 2.0 Client Configuration Endpoint
+		 * @param registrationClientUrl the {@code URL} of the Client Configuration Endpoint where the Registration Access Token can be used
 		 * @return the {@link Builder} for further configuration
+		 * @since 0.2.1
 		 */
-		public Builder registrationClientUri(String registrationClientUri) {
-			return claim(OidcClientMetadataClaimNames.REGISTRATION_CLIENT_URI, registrationClientUri);
+		public Builder registrationClientUrl(String registrationClientUrl) {
+			return claim(OidcClientMetadataClaimNames.REGISTRATION_CLIENT_URI, registrationClientUrl);
 		}
 
 		/**

+ 6 - 2
oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/oidc/authentication/JwtUtils.java

@@ -19,6 +19,7 @@ import java.time.Instant;
 import java.util.Collections;
 import java.util.Set;
 
+import org.springframework.security.authentication.AuthenticationProvider;
 import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;
 import org.springframework.security.oauth2.jose.jws.SignatureAlgorithm;
 import org.springframework.security.oauth2.jwt.JoseHeader;
@@ -29,14 +30,17 @@ import org.springframework.util.CollectionUtils;
 import org.springframework.util.StringUtils;
 
 /**
+ * TODO
+ * This class is mostly a straight copy from {@code org.springframework.security.oauth2.server.authorization.authentication.JwtUtils}.
+ * It should be consolidated when we introduce a token generator abstraction.
+ *
+ * Utility methods used by the {@link AuthenticationProvider}'s when issuing {@link Jwt}'s.
  *
- * Utility methods used by the {@link OidcClientRegistrationAuthenticationProvider} when issuing {@link Jwt}'s.
  * @author Ovidiu Popa
  * @since 0.2.1
  */
 final class JwtUtils {
 
-	//TODO Duplicate of {@code org.springframework.security.oauth2.server.authorization.authentication.JwtUtils}. To be refactored
 	private JwtUtils() {
 	}
 

+ 121 - 90
oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/oidc/authentication/OidcClientRegistrationAuthenticationProvider.java

@@ -20,13 +20,12 @@ import java.net.URISyntaxException;
 import java.time.Instant;
 import java.util.Base64;
 import java.util.Collection;
-import java.util.HashSet;
+import java.util.Collections;
 import java.util.List;
 import java.util.Set;
 import java.util.UUID;
 
 import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.lang.Nullable;
 import org.springframework.security.authentication.AuthenticationProvider;
 import org.springframework.security.core.Authentication;
 import org.springframework.security.core.AuthenticationException;
@@ -56,18 +55,17 @@ import org.springframework.security.oauth2.server.authorization.config.TokenSett
 import org.springframework.security.oauth2.server.resource.authentication.AbstractOAuth2TokenAuthenticationToken;
 import org.springframework.util.Assert;
 import org.springframework.util.CollectionUtils;
-import org.springframework.util.StringUtils;
 import org.springframework.web.util.UriComponentsBuilder;
 
 /**
- * An {@link AuthenticationProvider} implementation for OpenID Connect Dynamic Client Registration 1.0 and
- * OpenID Connect Client Configuration 1.0.
+ * An {@link AuthenticationProvider} implementation for OpenID Connect 1.0 Dynamic Client Registration (and Configuration) Endpoint.
  *
  * @author Ovidiu Popa
  * @author Joe Grandja
  * @since 0.1.1
  * @see RegisteredClientRepository
  * @see OAuth2AuthorizationService
+ * @see JwtEncoder
  * @see <a href="https://openid.net/specs/openid-connect-registration-1_0.html#ClientRegistration">3. Client Registration Endpoint</a>
  * @see <a href="https://openid.net/specs/openid-connect-registration-1_0.html#ClientConfigurationEndpoint">4. Client Configuration Endpoint</a>
  */
@@ -80,9 +78,25 @@ public final class OidcClientRegistrationAuthenticationProvider implements Authe
 	private static final String DEFAULT_CLIENT_CONFIGURATION_AUTHORIZED_SCOPE = "client.read";
 	private final RegisteredClientRepository registeredClientRepository;
 	private final OAuth2AuthorizationService authorizationService;
-	private final JwtEncoder jwtEncoder;
+	private JwtEncoder jwtEncoder;
 	private ProviderSettings providerSettings;
 
+	/**
+	 * Constructs an {@code OidcClientRegistrationAuthenticationProvider} using the provided parameters.
+	 *
+	 * @param registeredClientRepository the repository of registered clients
+	 * @param authorizationService the authorization service
+	 * @deprecated Use {@link #OidcClientRegistrationAuthenticationProvider(RegisteredClientRepository, OAuth2AuthorizationService, JwtEncoder)} instead
+	 */
+	@Deprecated
+	public OidcClientRegistrationAuthenticationProvider(RegisteredClientRepository registeredClientRepository,
+			OAuth2AuthorizationService authorizationService) {
+		Assert.notNull(registeredClientRepository, "registeredClientRepository cannot be null");
+		Assert.notNull(authorizationService, "authorizationService cannot be null");
+		this.registeredClientRepository = registeredClientRepository;
+		this.authorizationService = authorizationService;
+	}
+
 	/**
 	 * Constructs an {@code OidcClientRegistrationAuthenticationProvider} using the provided parameters.
 	 *
@@ -100,6 +114,12 @@ public final class OidcClientRegistrationAuthenticationProvider implements Authe
 		this.jwtEncoder = jwtEncoder;
 	}
 
+	@Deprecated
+	@Autowired(required = false)
+	protected void setJwtEncoder(JwtEncoder jwtEncoder) {
+		this.jwtEncoder = jwtEncoder;
+	}
+
 	@Autowired(required = false)
 	protected void setProviderSettings(ProviderSettings providerSettings) {
 		this.providerSettings = providerSettings;
@@ -110,7 +130,7 @@ public final class OidcClientRegistrationAuthenticationProvider implements Authe
 		OidcClientRegistrationAuthenticationToken clientRegistrationAuthentication =
 				(OidcClientRegistrationAuthenticationToken) authentication;
 
-		// Validate the "initial" and the registration access token
+		// Validate the "initial" or "registration" access token
 		AbstractOAuth2TokenAuthenticationToken<?> accessTokenAuthentication = null;
 		if (AbstractOAuth2TokenAuthenticationToken.class.isAssignableFrom(clientRegistrationAuthentication.getPrincipal().getClass())) {
 			accessTokenAuthentication = (AbstractOAuth2TokenAuthenticationToken<?>) clientRegistrationAuthentication.getPrincipal();
@@ -132,78 +152,77 @@ public final class OidcClientRegistrationAuthenticationProvider implements Authe
 			throw new OAuth2AuthenticationException(OAuth2ErrorCodes.INVALID_TOKEN);
 		}
 
-		if (clientRegistrationAuthentication.getClientRegistration() != null) {
+		return clientRegistrationAuthentication.getClientRegistration() != null ?
+				registerClient(clientRegistrationAuthentication, authorization) :
+				findRegistration(clientRegistrationAuthentication, authorization);
+	}
 
-			return registerClient(clientRegistrationAuthentication, authorization);
-		}
+	@Override
+	public boolean supports(Class<?> authentication) {
+		return OidcClientRegistrationAuthenticationToken.class.isAssignableFrom(authentication);
+	}
 
-		if (isNotAuthorized(authorizedAccessToken, DEFAULT_CLIENT_CONFIGURATION_AUTHORIZED_SCOPE)) {
-			throw new OAuth2AuthenticationException(OAuth2ErrorCodes.INSUFFICIENT_SCOPE);
-		}
+	private OidcClientRegistrationAuthenticationToken findRegistration(OidcClientRegistrationAuthenticationToken clientRegistrationAuthentication,
+			OAuth2Authorization authorization) {
 
-		RegisteredClient registeredClient = this.registeredClientRepository
-				.findByClientId(clientRegistrationAuthentication.getClientId());
+		OAuth2Authorization.Token<OAuth2AccessToken> authorizedAccessToken = authorization.getAccessToken();
+		checkScopeForConfiguration(authorizedAccessToken);
 
+		RegisteredClient registeredClient = this.registeredClientRepository.findByClientId(
+				clientRegistrationAuthentication.getClientId());
 		if (registeredClient == null) {
 			throw new OAuth2AuthenticationException(OAuth2ErrorCodes.INVALID_CLIENT);
 		}
+
 		if (!registeredClient.getId().equals(authorization.getRegisteredClientId())) {
 			throw new OAuth2AuthenticationException(OAuth2ErrorCodes.INVALID_CLIENT);
 		}
 
-		String registrationClientUri = registrationClientUri(getIssuer(), this.providerSettings.getOidcClientRegistrationEndpoint(), registeredClient.getClientId());
-		return new OidcClientRegistrationAuthenticationToken(accessTokenAuthentication,
-				convert(registeredClient, registrationClientUri, null));
+		OidcClientRegistration clientRegistration = buildRegistration(registeredClient).build();
 
+		return new OidcClientRegistrationAuthenticationToken(
+				(Authentication) clientRegistrationAuthentication.getPrincipal(), clientRegistration);
 	}
 
 	private OidcClientRegistrationAuthenticationToken registerClient(OidcClientRegistrationAuthenticationToken clientRegistrationAuthentication,
 			OAuth2Authorization authorization) {
 
 		OAuth2Authorization.Token<OAuth2AccessToken> authorizedAccessToken = authorization.getAccessToken();
-		if (isNotAuthorized(authorizedAccessToken, DEFAULT_CLIENT_REGISTRATION_AUTHORIZED_SCOPE)) {
-			throw new OAuth2AuthenticationException(OAuth2ErrorCodes.INSUFFICIENT_SCOPE);
-		}
+		checkScopeForRegistration(authorizedAccessToken);
 
 		if (!isValidRedirectUris(clientRegistrationAuthentication.getClientRegistration().getRedirectUris())) {
 			// TODO Add OAuth2ErrorCodes.INVALID_REDIRECT_URI
 			throw new OAuth2AuthenticationException("invalid_redirect_uri");
 		}
 
-		RegisteredClient registeredClient = create(clientRegistrationAuthentication.getClientRegistration());
+		RegisteredClient registeredClient = createClient(clientRegistrationAuthentication.getClientRegistration());
 		this.registeredClientRepository.save(registeredClient);
 
+		OAuth2Authorization registeredClientAuthorization = registerAccessToken(registeredClient);
+
 		// Invalidate the "initial" access token as it can only be used once
 		authorization = OidcAuthenticationProviderUtils.invalidate(authorization, authorizedAccessToken.getToken());
 		if (authorization.getRefreshToken() != null) {
 			authorization = OidcAuthenticationProviderUtils.invalidate(authorization, authorization.getRefreshToken().getToken());
 		}
 		this.authorizationService.save(authorization);
-		String registrationClientUri = registrationClientUri(getIssuer(), this.providerSettings.getOidcClientRegistrationEndpoint(), registeredClient.getClientId());
-		String registrationAccessToken = registerAccessToken(registeredClient)
-				.getAccessToken().getToken().getTokenValue();
-
-		return new OidcClientRegistrationAuthenticationToken((AbstractOAuth2TokenAuthenticationToken<?>) clientRegistrationAuthentication.getPrincipal(),
-				convert(registeredClient, registrationClientUri, registrationAccessToken));
-	}
 
-	@Override
-	public boolean supports(Class<?> authentication) {
-		return OidcClientRegistrationAuthenticationToken.class.isAssignableFrom(authentication);
-	}
+		OidcClientRegistration clientRegistration = buildRegistration(registeredClient)
+				.registrationAccessToken(registeredClientAuthorization.getAccessToken().getToken().getTokenValue())
+				.build();
 
-	private String getIssuer() {
-		return this.providerSettings != null ? this.providerSettings.getIssuer() : null;
+		return new OidcClientRegistrationAuthenticationToken(
+				(Authentication) clientRegistrationAuthentication.getPrincipal(), clientRegistration);
 	}
 
 	private OAuth2Authorization registerAccessToken(RegisteredClient registeredClient) {
-
-		String issuer = getIssuer();
-		Set<String> authorizedScopes = new HashSet<>();
-		authorizedScopes.add(DEFAULT_CLIENT_CONFIGURATION_AUTHORIZED_SCOPE);
 		JoseHeader headers = JwtUtils.headers().build();
+
+		Set<String> authorizedScopes = Collections.singleton(DEFAULT_CLIENT_CONFIGURATION_AUTHORIZED_SCOPE);
+
 		JwtClaimsSet claims = JwtUtils.accessTokenClaims(
-				registeredClient, issuer, registeredClient.getClientId(), authorizedScopes).build();
+				registeredClient, this.providerSettings.getIssuer(), registeredClient.getClientId(), authorizedScopes)
+				.build();
 
 		Jwt registrationAccessToken = this.jwtEncoder.encode(headers, claims);
 
@@ -212,7 +231,7 @@ public final class OidcClientRegistrationAuthenticationProvider implements Authe
 				registrationAccessToken.getExpiresAt(), authorizedScopes);
 
 		// @formatter:off
-		OAuth2Authorization accessTokenAuthorization = OAuth2Authorization.withRegisteredClient(registeredClient)
+		OAuth2Authorization registeredClientAuthorization = OAuth2Authorization.withRegisteredClient(registeredClient)
 				.principalName(registeredClient.getClientId())
 				.authorizationGrantType(AuthorizationGrantType.CLIENT_CREDENTIALS)
 				.token(accessToken,
@@ -222,14 +241,69 @@ public final class OidcClientRegistrationAuthenticationProvider implements Authe
 				.build();
 		// @formatter:on
 
-		this.authorizationService.save(accessTokenAuthorization);
-		return accessTokenAuthorization;
+		this.authorizationService.save(registeredClientAuthorization);
+
+		return registeredClientAuthorization;
+	}
+
+	private OidcClientRegistration.Builder buildRegistration(RegisteredClient registeredClient) {
+		// @formatter:off
+		OidcClientRegistration.Builder builder = OidcClientRegistration.builder()
+				.clientId(registeredClient.getClientId())
+				.clientIdIssuedAt(registeredClient.getClientIdIssuedAt())
+				.clientSecret(registeredClient.getClientSecret())
+				.clientName(registeredClient.getClientName());
+
+		builder.redirectUris(redirectUris ->
+				redirectUris.addAll(registeredClient.getRedirectUris()));
+
+		builder.grantTypes(grantTypes ->
+				registeredClient.getAuthorizationGrantTypes().forEach(authorizationGrantType ->
+						grantTypes.add(authorizationGrantType.getValue())));
+
+		if (registeredClient.getAuthorizationGrantTypes().contains(AuthorizationGrantType.AUTHORIZATION_CODE)) {
+			builder.responseType(OAuth2AuthorizationResponseType.CODE.getValue());
+		}
+
+		if (!CollectionUtils.isEmpty(registeredClient.getScopes())) {
+			builder.scopes(scopes ->
+					scopes.addAll(registeredClient.getScopes()));
+		}
+
+		String registrationClientUri = UriComponentsBuilder.fromUriString(this.providerSettings.getIssuer())
+				.path(this.providerSettings.getOidcClientRegistrationEndpoint())
+				.queryParam(OAuth2ParameterNames.CLIENT_ID, registeredClient.getClientId())
+				.toUriString();
+
+		builder
+				.tokenEndpointAuthenticationMethod(registeredClient.getClientAuthenticationMethods().iterator().next().getValue())
+				.idTokenSignedResponseAlgorithm(registeredClient.getTokenSettings().getIdTokenSignatureAlgorithm().getName())
+				.registrationClientUrl(registrationClientUri);
+
+		return builder;
+		// @formatter:on
+	}
+
+	private static void checkScopeForRegistration(OAuth2Authorization.Token<OAuth2AccessToken> authorizedAccessToken) {
+		checkScope(authorizedAccessToken, Collections.singleton(DEFAULT_CLIENT_REGISTRATION_AUTHORIZED_SCOPE));
+	}
+
+	private static void checkScopeForConfiguration(OAuth2Authorization.Token<OAuth2AccessToken> authorizedAccessToken) {
+		checkScope(authorizedAccessToken, Collections.singleton(DEFAULT_CLIENT_CONFIGURATION_AUTHORIZED_SCOPE));
 	}
 
 	@SuppressWarnings("unchecked")
-	private static boolean isNotAuthorized(OAuth2Authorization.Token<OAuth2AccessToken> authorizedAccessToken, String requiredScope) {
-		Object scope = authorizedAccessToken.getClaims().get(OAuth2ParameterNames.SCOPE);
-		return scope == null || !((Collection<String>) scope).contains(requiredScope);
+	private static void checkScope(OAuth2Authorization.Token<OAuth2AccessToken> authorizedAccessToken, Set<String> requiredScope) {
+		Collection<String> authorizedScope = Collections.emptySet();
+		if (authorizedAccessToken.getClaims().containsKey(OAuth2ParameterNames.SCOPE)) {
+			authorizedScope = (Collection<String>) authorizedAccessToken.getClaims().get(OAuth2ParameterNames.SCOPE);
+		}
+		if (!authorizedScope.containsAll(requiredScope)) {
+			throw new OAuth2AuthenticationException(OAuth2ErrorCodes.INSUFFICIENT_SCOPE);
+		} else if (authorizedScope.size() != requiredScope.size()) {
+			// Restrict the access token to only contain the required scope
+			throw new OAuth2AuthenticationException(OAuth2ErrorCodes.INVALID_TOKEN);
+		}
 	}
 
 	private static boolean isValidRedirectUris(List<String> redirectUris) {
@@ -251,7 +325,7 @@ public final class OidcClientRegistrationAuthenticationProvider implements Authe
 		return true;
 	}
 
-	private static RegisteredClient create(OidcClientRegistration clientRegistration) {
+	private static RegisteredClient createClient(OidcClientRegistration clientRegistration) {
 		// @formatter:off
 		RegisteredClient.Builder builder = RegisteredClient.withId(UUID.randomUUID().toString())
 				.clientId(CLIENT_ID_GENERATOR.generateKey())
@@ -298,47 +372,4 @@ public final class OidcClientRegistrationAuthenticationProvider implements Authe
 		// @formatter:on
 	}
 
-
-	private static String registrationClientUri(String issuer, String oidcClientRegistrationEndpoint, String clientId){
-		return UriComponentsBuilder.fromUriString(issuer)
-				.path(oidcClientRegistrationEndpoint)
-				.queryParam(OAuth2ParameterNames.CLIENT_ID, clientId).toUriString();
-	}
-
-	private static OidcClientRegistration convert(RegisteredClient registeredClient, String registrationClientUri,
-			@Nullable String registrationAccessToken) {
-		// @formatter:off
-		OidcClientRegistration.Builder builder = OidcClientRegistration.builder()
-				.clientId(registeredClient.getClientId())
-				.clientIdIssuedAt(registeredClient.getClientIdIssuedAt())
-				.clientSecret(registeredClient.getClientSecret())
-				.clientName(registeredClient.getClientName());
-
-		builder.redirectUris(redirectUris ->
-				redirectUris.addAll(registeredClient.getRedirectUris()));
-
-		builder.grantTypes(grantTypes ->
-				registeredClient.getAuthorizationGrantTypes().forEach(authorizationGrantType ->
-						grantTypes.add(authorizationGrantType.getValue())));
-
-		if (registeredClient.getAuthorizationGrantTypes().contains(AuthorizationGrantType.AUTHORIZATION_CODE)) {
-			builder.responseType(OAuth2AuthorizationResponseType.CODE.getValue());
-		}
-
-		if (!CollectionUtils.isEmpty(registeredClient.getScopes())) {
-			builder.scopes(scopes ->
-					scopes.addAll(registeredClient.getScopes()));
-		}
-
-		builder
-				.tokenEndpointAuthenticationMethod(registeredClient.getClientAuthenticationMethods().iterator().next().getValue())
-				.idTokenSignedResponseAlgorithm(registeredClient.getTokenSettings().getIdTokenSignatureAlgorithm().getName())
-				.registrationClientUri(registrationClientUri);
-		if (StringUtils.hasText(registrationAccessToken)) {
-			builder.registrationAccessToken(registrationAccessToken);
-		}
-		return builder.build();
-		// @formatter:on
-	}
-
 }

+ 12 - 7
oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/oidc/authentication/OidcClientRegistrationAuthenticationToken.java

@@ -17,6 +17,7 @@ package org.springframework.security.oauth2.server.authorization.oidc.authentica
 
 import java.util.Collections;
 
+import org.springframework.lang.Nullable;
 import org.springframework.security.authentication.AbstractAuthenticationToken;
 import org.springframework.security.core.Authentication;
 import org.springframework.security.oauth2.core.Version;
@@ -24,7 +25,7 @@ import org.springframework.security.oauth2.core.oidc.OidcClientRegistration;
 import org.springframework.util.Assert;
 
 /**
- * An {@link Authentication} implementation used for OpenID Connect Dynamic Client Registration 1.0.
+ * An {@link Authentication} implementation used for OpenID Connect 1.0 Dynamic Client Registration (and Configuration) Endpoint.
  *
  * @author Joe Grandja
  * @author Ovidiu Popa
@@ -36,8 +37,9 @@ import org.springframework.util.Assert;
 public class OidcClientRegistrationAuthenticationToken extends AbstractAuthenticationToken {
 	private static final long serialVersionUID = Version.SERIAL_VERSION_UID;
 	private final Authentication principal;
-	private OidcClientRegistration clientRegistration;
-	private String clientId;
+	private final OidcClientRegistration clientRegistration;
+	private final String clientId;
+
 	/**
 	 * Constructs an {@code OidcClientRegistrationAuthenticationToken} using the provided parameters.
 	 *
@@ -50,6 +52,7 @@ public class OidcClientRegistrationAuthenticationToken extends AbstractAuthentic
 		Assert.notNull(clientRegistration, "clientRegistration cannot be null");
 		this.principal = principal;
 		this.clientRegistration = clientRegistration;
+		this.clientId = null;
 		setAuthenticated(principal.isAuthenticated());
 	}
 
@@ -57,13 +60,14 @@ public class OidcClientRegistrationAuthenticationToken extends AbstractAuthentic
 	 * Constructs an {@code OidcClientRegistrationAuthenticationToken} using the provided parameters.
 	 *
 	 * @param principal the authenticated principal
-	 * @param clientId the registered client_id
+	 * @param clientId the client identifier
 	 */
 	public OidcClientRegistrationAuthenticationToken(Authentication principal, String clientId) {
 		super(Collections.emptyList());
 		Assert.notNull(principal, "principal cannot be null");
-		Assert.hasText(clientId, "clientId cannot be null or empty");
+		Assert.hasText(clientId, "clientId cannot be empty");
 		this.principal = principal;
+		this.clientRegistration = null;
 		this.clientId = clientId;
 		setAuthenticated(principal.isAuthenticated());
 	}
@@ -88,11 +92,12 @@ public class OidcClientRegistrationAuthenticationToken extends AbstractAuthentic
 	}
 
 	/**
-	 * Returns the registered client_id.
+	 * Returns the client identifier.
 	 *
-	 * @return the registered client_id
+	 * @return the client identifier
 	 * @since 0.2.1
 	 */
+	@Nullable
 	public String getClientId() {
 		return this.clientId;
 	}

+ 23 - 44
oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/oidc/web/OidcClientRegistrationEndpointFilter.java

@@ -47,7 +47,7 @@ import org.springframework.util.StringUtils;
 import org.springframework.web.filter.OncePerRequestFilter;
 
 /**
- * A {@code Filter} that processes OpenID Connect Dynamic Client Registration 1.0 Requests and  OpenID Connect Client Configuration 1.0 Requests.
+ * A {@code Filter} that processes OpenID Connect Dynamic Client Registration (and Configuration) 1.0 Requests.
  *
  * @author Ovidiu Popa
  * @author Joe Grandja
@@ -64,8 +64,6 @@ public final class OidcClientRegistrationEndpointFilter extends OncePerRequestFi
 
 	private final AuthenticationManager authenticationManager;
 	private final RequestMatcher clientRegistrationEndpointMatcher;
-	private final RequestMatcher registerClientEndpointMatcher;
-	private final RequestMatcher clientConfigurationEndpointMatcher;
 	private final HttpMessageConverter<OidcClientRegistration> clientRegistrationHttpMessageConverter =
 			new OidcClientRegistrationHttpMessageConverter();
 	private final HttpMessageConverter<OAuth2Error> errorHttpResponseConverter =
@@ -91,15 +89,14 @@ public final class OidcClientRegistrationEndpointFilter extends OncePerRequestFi
 		Assert.notNull(authenticationManager, "authenticationManager cannot be null");
 		Assert.hasText(clientRegistrationEndpointUri, "clientRegistrationEndpointUri cannot be empty");
 		this.authenticationManager = authenticationManager;
-		this.registerClientEndpointMatcher = new AntPathRequestMatcher(
-				clientRegistrationEndpointUri, HttpMethod.POST.name());
-		this.clientConfigurationEndpointMatcher = createClientConfigurationEndpointMatcher(clientRegistrationEndpointUri);
-		this.clientRegistrationEndpointMatcher = new OrRequestMatcher(this.registerClientEndpointMatcher, this.clientConfigurationEndpointMatcher);
+		this.clientRegistrationEndpointMatcher = new OrRequestMatcher(
+				new AntPathRequestMatcher(
+						clientRegistrationEndpointUri, HttpMethod.POST.name()),
+				createConfigureClientMatcher(clientRegistrationEndpointUri));
 	}
 
-	private static RequestMatcher createClientConfigurationEndpointMatcher(String clientRegistrationEndpointUri) {
-
-		RequestMatcher clientConfigurationRequestGetMatcher = new AntPathRequestMatcher(
+	private static RequestMatcher createConfigureClientMatcher(String clientRegistrationEndpointUri) {
+		RequestMatcher configureClientGetMatcher = new AntPathRequestMatcher(
 				clientRegistrationEndpointUri, HttpMethod.GET.name());
 
 		RequestMatcher clientIdMatcher = request -> {
@@ -107,7 +104,7 @@ public final class OidcClientRegistrationEndpointFilter extends OncePerRequestFi
 			return StringUtils.hasText(clientId);
 		};
 
-		return new AndRequestMatcher(clientConfigurationRequestGetMatcher, clientIdMatcher);
+		return new AndRequestMatcher(configureClientGetMatcher, clientIdMatcher);
 	}
 
 	@Override
@@ -120,17 +117,17 @@ public final class OidcClientRegistrationEndpointFilter extends OncePerRequestFi
 		}
 
 		try {
-			OidcClientRegistrationAuthenticationToken clientRegistrationAuthenticationToken = convert(request);
+			OidcClientRegistrationAuthenticationToken clientRegistrationAuthentication = convert(request);
 
 			OidcClientRegistrationAuthenticationToken clientRegistrationAuthenticationResult =
-					(OidcClientRegistrationAuthenticationToken) this.authenticationManager.authenticate(clientRegistrationAuthenticationToken);
+					(OidcClientRegistrationAuthenticationToken) this.authenticationManager.authenticate(clientRegistrationAuthentication);
 
-			if (clientRegistrationAuthenticationToken.getClientRegistration() != null) {
-				sendClientRegistrationResponse(response, HttpStatus.CREATED, clientRegistrationAuthenticationResult.getClientRegistration());
-				return;
+			HttpStatus httpStatus = HttpStatus.OK;
+			if (clientRegistrationAuthentication.getClientRegistration() != null) {
+				httpStatus = HttpStatus.CREATED;
 			}
 
-			sendClientRegistrationResponse(response, HttpStatus.OK, clientRegistrationAuthenticationResult.getClientRegistration());
+			sendClientRegistrationResponse(response, httpStatus, clientRegistrationAuthenticationResult.getClientRegistration());
 
 		} catch (OAuth2AuthenticationException ex) {
 			sendErrorResponse(response, ex.getError());
@@ -145,41 +142,23 @@ public final class OidcClientRegistrationEndpointFilter extends OncePerRequestFi
 		}
 	}
 
-	private OidcClientRegistrationAuthenticationToken convert(HttpServletRequest request) {
-		if (this.registerClientEndpointMatcher.matches(request)) {
-			return convertOidcClientRegistrationRequest(request);
-		}
+	private OidcClientRegistrationAuthenticationToken convert(HttpServletRequest request) throws Exception {
+		Authentication principal = SecurityContextHolder.getContext().getAuthentication();
 
-		if (this.clientConfigurationEndpointMatcher.matches(request)) {
-			return convertOidcClientConfigurationRequest(request);
+		if ("POST".equals(request.getMethod())) {
+			OidcClientRegistration clientRegistration = this.clientRegistrationHttpMessageConverter.read(
+					OidcClientRegistration.class, new ServletServerHttpRequest(request));
+			return new OidcClientRegistrationAuthenticationToken(principal, clientRegistration);
 		}
 
-		throw new OAuth2AuthenticationException(new OAuth2Error(OAuth2ErrorCodes.INVALID_REQUEST));
-	}
-
-	private OidcClientRegistrationAuthenticationToken convertOidcClientConfigurationRequest(HttpServletRequest request) {
-		Authentication principal = SecurityContextHolder.getContext().getAuthentication();
+		// client_id (REQUIRED)
 		String clientId = request.getParameter(OAuth2ParameterNames.CLIENT_ID);
 		String[] clientIdParameters = request.getParameterValues(OAuth2ParameterNames.CLIENT_ID);
 		if (!StringUtils.hasText(clientId) || clientIdParameters.length != 1) {
-			throw new OAuth2AuthenticationException(OAuth2ErrorCodes.INVALID_CLIENT);
+			throw new OAuth2AuthenticationException(OAuth2ErrorCodes.INVALID_REQUEST);
 		}
-		return new OidcClientRegistrationAuthenticationToken(principal, clientId);
-	}
 
-	private OidcClientRegistrationAuthenticationToken convertOidcClientRegistrationRequest(HttpServletRequest request) {
-		try {
-			Authentication principal = SecurityContextHolder.getContext().getAuthentication();
-			OidcClientRegistration clientRegistration = this.clientRegistrationHttpMessageConverter.read(
-					OidcClientRegistration.class, new ServletServerHttpRequest(request));
-			return new OidcClientRegistrationAuthenticationToken(principal, clientRegistration);
-		} catch (IOException ex) {
-			OAuth2Error error = new OAuth2Error(
-					OAuth2ErrorCodes.INVALID_REQUEST,
-					"OpenID Client Registration Error: " + ex.getMessage(),
-					"https://openid.net/specs/openid-connect-registration-1_0.html#RegistrationError");
-			throw new OAuth2AuthenticationException(error);
-		}
+		return new OidcClientRegistrationAuthenticationToken(principal, clientId);
 	}
 
 	private void sendClientRegistrationResponse(HttpServletResponse response, HttpStatus httpStatus, OidcClientRegistration clientRegistration) throws IOException {

+ 44 - 255
oauth2-authorization-server/src/test/java/org/springframework/security/config/annotation/web/configurers/oauth2/server/authorization/OidcClientRegistrationTests.java

@@ -18,17 +18,16 @@ package org.springframework.security.config.annotation.web.configurers.oauth2.se
 import java.net.URLEncoder;
 import java.nio.charset.StandardCharsets;
 import java.util.Base64;
-import java.util.stream.Collectors;
 
 import com.nimbusds.jose.jwk.JWKSet;
 import com.nimbusds.jose.jwk.source.JWKSource;
 import com.nimbusds.jose.proc.SecurityContext;
-
 import org.junit.After;
 import org.junit.AfterClass;
 import org.junit.BeforeClass;
 import org.junit.Rule;
 import org.junit.Test;
+
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.context.annotation.Bean;
 import org.springframework.http.HttpHeaders;
@@ -138,28 +137,6 @@ public class OidcClientRegistrationTests {
 	public void requestWhenClientRegistrationRequestAuthorizedThenClientRegistrationResponse() throws Exception {
 		this.spring.register(AuthorizationServerConfiguration.class).autowire();
 
-		// ***** (1) Obtain the "initial" access token used for registering the client
-
-		String clientRegistrationScope = "client.create";
-		RegisteredClient registeredClient = TestRegisteredClients.registeredClient2()
-				.scope(clientRegistrationScope)
-				.build();
-		this.registeredClientRepository.save(registeredClient);
-
-		MvcResult mvcResult = this.mvc.perform(post(DEFAULT_TOKEN_ENDPOINT_URI)
-				.param(OAuth2ParameterNames.GRANT_TYPE, AuthorizationGrantType.CLIENT_CREDENTIALS.getValue())
-				.param(OAuth2ParameterNames.SCOPE, clientRegistrationScope)
-				.header(HttpHeaders.AUTHORIZATION, "Basic " + encodeBasicAuth(
-						registeredClient.getClientId(), registeredClient.getClientSecret())))
-				.andExpect(status().isOk())
-				.andExpect(jsonPath("$.access_token").isNotEmpty())
-				.andExpect(jsonPath("$.scope").value(clientRegistrationScope))
-				.andReturn();
-
-		OAuth2AccessToken accessToken = readAccessTokenResponse(mvcResult.getResponse()).getAccessToken();
-
-		// ***** (2) Register the client
-
 		// @formatter:off
 		OidcClientRegistration clientRegistration = OidcClientRegistration.builder()
 				.clientName("client-name")
@@ -171,20 +148,8 @@ public class OidcClientRegistrationTests {
 				.build();
 		// @formatter:on
 
-		HttpHeaders httpHeaders = new HttpHeaders();
-		httpHeaders.setBearerAuth(accessToken.getTokenValue());
-
-		// Register the client
-		mvcResult = this.mvc.perform(post(DEFAULT_OIDC_CLIENT_REGISTRATION_ENDPOINT_URI)
-				.headers(httpHeaders)
-				.contentType(MediaType.APPLICATION_JSON)
-				.content(getClientRegistrationRequestContent(clientRegistration)))
-				.andExpect(status().isCreated())
-				.andExpect(header().string(HttpHeaders.CACHE_CONTROL, containsString("no-store")))
-				.andExpect(header().string(HttpHeaders.PRAGMA, containsString("no-cache")))
-				.andReturn();
+		OidcClientRegistration clientRegistrationResponse = registerClient(clientRegistration);
 
-		OidcClientRegistration clientRegistrationResponse = readClientRegistrationResponse(mvcResult.getResponse());
 		assertThat(clientRegistrationResponse.getClientId()).isNotNull();
 		assertThat(clientRegistrationResponse.getClientIdIssuedAt()).isNotNull();
 		assertThat(clientRegistrationResponse.getClientSecret()).isNotNull();
@@ -202,181 +167,75 @@ public class OidcClientRegistrationTests {
 				.isEqualTo(ClientAuthenticationMethod.CLIENT_SECRET_BASIC.getValue());
 		assertThat(clientRegistrationResponse.getIdTokenSignedResponseAlgorithm())
 				.isEqualTo(SignatureAlgorithm.RS256.getName());
-		assertThat(clientRegistrationResponse.getRegistrationClientUri())
-				.isNotNull();
+		assertThat(clientRegistrationResponse.getRegistrationClientUrl()).isNotNull();
 		assertThat(clientRegistrationResponse.getRegistrationAccessToken()).isNotEmpty();
 	}
 
-	@Test
-	public void requestWhenClientConfigurationRequestAndRegisteredClientNotEqualToAuthorizationRegisteredClientThenUnauthorized() throws Exception {
-		this.spring.register(AuthorizationServerConfiguration.class).autowire();
-
-		// ***** (1) Obtain the registration access token used for fetching the registered client configuration
-
-		String clientConfigurationRequestScope = "client.read";
-		RegisteredClient registeredClient = TestRegisteredClients.registeredClient()
-				.scope(clientConfigurationRequestScope)
-				.authorizationGrantType(AuthorizationGrantType.CLIENT_CREDENTIALS)
-				.build();
-		this.registeredClientRepository.save(registeredClient);
-
-		RegisteredClient unauthorizedRegisteredClient = TestRegisteredClients.registeredClient()
-				.id("registration-2")
-				.clientId("client-2")
-				.scope(clientConfigurationRequestScope)
-				.authorizationGrantType(AuthorizationGrantType.CLIENT_CREDENTIALS)
-				.build();
-		this.registeredClientRepository.save(unauthorizedRegisteredClient);
-
-		MvcResult mvcResult = this.mvc.perform(post(DEFAULT_TOKEN_ENDPOINT_URI)
-						.param(OAuth2ParameterNames.GRANT_TYPE, AuthorizationGrantType.CLIENT_CREDENTIALS.getValue())
-						.param(OAuth2ParameterNames.SCOPE, clientConfigurationRequestScope)
-						.header(HttpHeaders.AUTHORIZATION, "Basic " + encodeBasicAuth(
-								registeredClient.getClientId(), registeredClient.getClientSecret())))
-				.andExpect(status().isOk())
-				.andExpect(jsonPath("$.access_token").isNotEmpty())
-				.andExpect(jsonPath("$.scope").value(clientConfigurationRequestScope))
-				.andReturn();
-
-		OAuth2AccessToken accessToken = readAccessTokenResponse(mvcResult.getResponse()).getAccessToken();
-
-		HttpHeaders httpHeaders = new HttpHeaders();
-		httpHeaders.setBearerAuth(accessToken.getTokenValue());
-
-		// ***** (2) Get RegisteredClient Configuration
-		this.mvc.perform(get(DEFAULT_OIDC_CLIENT_REGISTRATION_ENDPOINT_URI)
-						.headers(httpHeaders)
-						.queryParam(OAuth2ParameterNames.CLIENT_ID, unauthorizedRegisteredClient.getClientId()))
-				.andExpect(status().isUnauthorized());
-	}
-
 	@Test
 	public void requestWhenClientConfigurationRequestAuthorizedThenClientRegistrationResponse() throws Exception {
 		this.spring.register(AuthorizationServerConfiguration.class).autowire();
 
-		// ***** (1) Obtain the registration access token used for fetching the registered client configuration
-
-		String clientConfigurationRequestScope = "client.read";
-		RegisteredClient registeredClient = TestRegisteredClients.registeredClient()
-				.scope(clientConfigurationRequestScope)
-				.authorizationGrantType(AuthorizationGrantType.CLIENT_CREDENTIALS)
+		// @formatter:off
+		OidcClientRegistration clientRegistration = OidcClientRegistration.builder()
+				.clientName("client-name")
+				.redirectUri("https://client.example.com")
+				.grantType(AuthorizationGrantType.AUTHORIZATION_CODE.getValue())
+				.grantType(AuthorizationGrantType.CLIENT_CREDENTIALS.getValue())
+				.scope("scope1")
+				.scope("scope2")
 				.build();
-		this.registeredClientRepository.save(registeredClient);
-
-		MvcResult mvcResult = this.mvc.perform(post(DEFAULT_TOKEN_ENDPOINT_URI)
-						.param(OAuth2ParameterNames.GRANT_TYPE, AuthorizationGrantType.CLIENT_CREDENTIALS.getValue())
-						.param(OAuth2ParameterNames.SCOPE, clientConfigurationRequestScope)
-						.header(HttpHeaders.AUTHORIZATION, "Basic " + encodeBasicAuth(
-								registeredClient.getClientId(), registeredClient.getClientSecret())))
-				.andExpect(status().isOk())
-				.andExpect(jsonPath("$.access_token").isNotEmpty())
-				.andExpect(jsonPath("$.scope").value(clientConfigurationRequestScope))
-				.andReturn();
+		// @formatter:on
 
-		OAuth2AccessToken accessToken = readAccessTokenResponse(mvcResult.getResponse()).getAccessToken();
+		OidcClientRegistration clientRegistrationResponse = registerClient(clientRegistration);
 
 		HttpHeaders httpHeaders = new HttpHeaders();
-		httpHeaders.setBearerAuth(accessToken.getTokenValue());
+		httpHeaders.setBearerAuth(clientRegistrationResponse.getRegistrationAccessToken());
 
-		// ***** (2) Get RegisteredClient Configuration
-		mvcResult = this.mvc.perform(get(DEFAULT_OIDC_CLIENT_REGISTRATION_ENDPOINT_URI)
-						.headers(httpHeaders)
-						.queryParam(OAuth2ParameterNames.CLIENT_ID, registeredClient.getClientId()))
+		MvcResult mvcResult = this.mvc.perform(get(clientRegistrationResponse.getRegistrationClientUrl().toURI())
+				.headers(httpHeaders))
 				.andExpect(status().isOk())
 				.andExpect(header().string(HttpHeaders.CACHE_CONTROL, containsString("no-store")))
 				.andExpect(header().string(HttpHeaders.PRAGMA, containsString("no-cache")))
 				.andReturn();
 
 		OidcClientRegistration clientConfigurationResponse = readClientRegistrationResponse(mvcResult.getResponse());
-		assertThat(clientConfigurationResponse.getClientId()).isNotNull().isEqualTo(registeredClient.getClientId());
-		assertThat(clientConfigurationResponse.getClientIdIssuedAt()).isNotNull();
-		assertThat(clientConfigurationResponse.getClientSecret()).isNotNull().isEqualTo(registeredClient.getClientSecret());
-		assertThat(clientConfigurationResponse.getClientSecretExpiresAt()).isNull();
-		assertThat(clientConfigurationResponse.getClientName()).isEqualTo(registeredClient.getClientName());
+
+		assertThat(clientConfigurationResponse.getClientId()).isEqualTo(clientRegistrationResponse.getClientId());
+		assertThat(clientConfigurationResponse.getClientIdIssuedAt()).isEqualTo(clientRegistrationResponse.getClientIdIssuedAt());
+		assertThat(clientConfigurationResponse.getClientSecret()).isEqualTo(clientRegistrationResponse.getClientSecret());
+		assertThat(clientConfigurationResponse.getClientSecretExpiresAt()).isEqualTo(clientRegistrationResponse.getClientSecretExpiresAt());
+		assertThat(clientConfigurationResponse.getClientName()).isEqualTo(clientRegistrationResponse.getClientName());
 		assertThat(clientConfigurationResponse.getRedirectUris())
-				.containsExactlyInAnyOrderElementsOf(registeredClient.getRedirectUris());
+				.containsExactlyInAnyOrderElementsOf(clientRegistrationResponse.getRedirectUris());
 		assertThat(clientConfigurationResponse.getGrantTypes())
-				.containsExactlyInAnyOrderElementsOf(registeredClient.getAuthorizationGrantTypes().stream().map(AuthorizationGrantType::getValue).collect(Collectors.toList()));
+				.containsExactlyInAnyOrderElementsOf(clientRegistrationResponse.getGrantTypes());
 		assertThat(clientConfigurationResponse.getResponseTypes())
-				.containsExactly(OAuth2AuthorizationResponseType.CODE.getValue());
+				.containsExactlyInAnyOrderElementsOf(clientRegistrationResponse.getResponseTypes());
 		assertThat(clientConfigurationResponse.getScopes())
-				.containsExactlyInAnyOrderElementsOf(registeredClient.getScopes());
+				.containsExactlyInAnyOrderElementsOf(clientRegistrationResponse.getScopes());
 		assertThat(clientConfigurationResponse.getTokenEndpointAuthenticationMethod())
-				.isEqualTo(ClientAuthenticationMethod.CLIENT_SECRET_BASIC.getValue());
+				.isEqualTo(clientRegistrationResponse.getTokenEndpointAuthenticationMethod());
 		assertThat(clientConfigurationResponse.getIdTokenSignedResponseAlgorithm())
-				.isEqualTo(SignatureAlgorithm.RS256.getName());
-		assertThat(clientConfigurationResponse.getRegistrationClientUri())
-				.isNotNull();
+				.isEqualTo(clientRegistrationResponse.getIdTokenSignedResponseAlgorithm());
+		assertThat(clientConfigurationResponse.getRegistrationClientUrl())
+				.isEqualTo(clientRegistrationResponse.getRegistrationClientUrl());
 		assertThat(clientConfigurationResponse.getRegistrationAccessToken()).isNull();
 	}
 
-	@Test
-	public void requestWhenClientConfigurationRequestTwiceSameAccessTokenAuthorizedThenClientRegistrationResponse() throws Exception {
-		this.spring.register(AuthorizationServerConfiguration.class).autowire();
-
-		// ***** (1) Obtain the registration access token used for fetching the registered client configuration
-
-		String clientConfigurationRequestScope = "client.read";
-		RegisteredClient registeredClient = TestRegisteredClients.registeredClient()
-				.scope(clientConfigurationRequestScope)
-				.authorizationGrantType(AuthorizationGrantType.CLIENT_CREDENTIALS)
-				.build();
-		this.registeredClientRepository.save(registeredClient);
-
-		MvcResult mvcResult = this.mvc.perform(post(DEFAULT_TOKEN_ENDPOINT_URI)
-						.param(OAuth2ParameterNames.GRANT_TYPE, AuthorizationGrantType.CLIENT_CREDENTIALS.getValue())
-						.param(OAuth2ParameterNames.SCOPE, clientConfigurationRequestScope)
-						.header(HttpHeaders.AUTHORIZATION, "Basic " + encodeBasicAuth(
-								registeredClient.getClientId(), registeredClient.getClientSecret())))
-				.andExpect(status().isOk())
-				.andExpect(jsonPath("$.access_token").isNotEmpty())
-				.andExpect(jsonPath("$.scope").value(clientConfigurationRequestScope))
-				.andReturn();
-
-		OAuth2AccessToken accessToken = readAccessTokenResponse(mvcResult.getResponse()).getAccessToken();
-
-		HttpHeaders httpHeaders = new HttpHeaders();
-		httpHeaders.setBearerAuth(accessToken.getTokenValue());
-
-		// ***** (2) Get RegisteredClient Configuration
-		mvcResult = this.mvc.perform(get(DEFAULT_OIDC_CLIENT_REGISTRATION_ENDPOINT_URI)
-						.headers(httpHeaders)
-						.queryParam(OAuth2ParameterNames.CLIENT_ID, registeredClient.getClientId()))
-				.andExpect(status().isOk())
-				.andExpect(header().string(HttpHeaders.CACHE_CONTROL, containsString("no-store")))
-				.andExpect(header().string(HttpHeaders.PRAGMA, containsString("no-cache")))
-				.andReturn();
-
-		assertClientConfigurationResponse(registeredClient, mvcResult);
-
-		// ***** (3) Get RegisteredClient Configuration with the same access token
-		mvcResult = this.mvc.perform(get(DEFAULT_OIDC_CLIENT_REGISTRATION_ENDPOINT_URI)
-						.headers(httpHeaders)
-						.queryParam(OAuth2ParameterNames.CLIENT_ID, registeredClient.getClientId()))
-				.andExpect(status().isOk())
-				.andExpect(header().string(HttpHeaders.CACHE_CONTROL, containsString("no-store")))
-				.andExpect(header().string(HttpHeaders.PRAGMA, containsString("no-cache")))
-				.andReturn();
-		assertClientConfigurationResponse(registeredClient, mvcResult);
-	}
-
-	@Test
-	public void requestWhenClientRegistrationRequestAndClientConfigurationRequestAuthorizedThenClientRegistrationResponse() throws Exception {
-		this.spring.register(AuthorizationServerConfiguration.class).autowire();
-
+	private OidcClientRegistration registerClient(OidcClientRegistration clientRegistration) throws Exception {
 		// ***** (1) Obtain the "initial" access token used for registering the client
 
 		String clientRegistrationScope = "client.create";
-		RegisteredClient registeredClient = TestRegisteredClients.registeredClient2()
+		RegisteredClient clientRegistrar = TestRegisteredClients.registeredClient2()
 				.scope(clientRegistrationScope)
 				.build();
-		this.registeredClientRepository.save(registeredClient);
+		this.registeredClientRepository.save(clientRegistrar);
 
 		MvcResult mvcResult = this.mvc.perform(post(DEFAULT_TOKEN_ENDPOINT_URI)
-						.param(OAuth2ParameterNames.GRANT_TYPE, AuthorizationGrantType.CLIENT_CREDENTIALS.getValue())
-						.param(OAuth2ParameterNames.SCOPE, clientRegistrationScope)
-						.header(HttpHeaders.AUTHORIZATION, "Basic " + encodeBasicAuth(
-								registeredClient.getClientId(), registeredClient.getClientSecret())))
+				.param(OAuth2ParameterNames.GRANT_TYPE, AuthorizationGrantType.CLIENT_CREDENTIALS.getValue())
+				.param(OAuth2ParameterNames.SCOPE, clientRegistrationScope)
+				.header(HttpHeaders.AUTHORIZATION, "Basic " + encodeBasicAuth(
+						clientRegistrar.getClientId(), clientRegistrar.getClientSecret())))
 				.andExpect(status().isOk())
 				.andExpect(jsonPath("$.access_token").isNotEmpty())
 				.andExpect(jsonPath("$.scope").value(clientRegistrationScope))
@@ -386,93 +245,22 @@ public class OidcClientRegistrationTests {
 
 		// ***** (2) Register the client
 
-		// @formatter:off
-		OidcClientRegistration clientRegistration = OidcClientRegistration.builder()
-				.clientName("client-name")
-				.redirectUri("https://client.example.com")
-				.grantType(AuthorizationGrantType.AUTHORIZATION_CODE.getValue())
-				.grantType(AuthorizationGrantType.CLIENT_CREDENTIALS.getValue())
-				.scope("scope1")
-				.scope("scope2")
-				.build();
-		// @formatter:on
-
 		HttpHeaders httpHeaders = new HttpHeaders();
 		httpHeaders.setBearerAuth(accessToken.getTokenValue());
 
 		// Register the client
 		mvcResult = this.mvc.perform(post(DEFAULT_OIDC_CLIENT_REGISTRATION_ENDPOINT_URI)
-						.headers(httpHeaders)
-						.contentType(MediaType.APPLICATION_JSON)
-						.content(getClientRegistrationRequestContent(clientRegistration)))
+				.headers(httpHeaders)
+				.contentType(MediaType.APPLICATION_JSON)
+				.content(getClientRegistrationRequestContent(clientRegistration)))
 				.andExpect(status().isCreated())
 				.andExpect(header().string(HttpHeaders.CACHE_CONTROL, containsString("no-store")))
 				.andExpect(header().string(HttpHeaders.PRAGMA, containsString("no-cache")))
 				.andReturn();
 
-		OidcClientRegistration clientRegistrationResponse = readClientRegistrationResponse(mvcResult.getResponse());
-
-
-		httpHeaders = new HttpHeaders();
-		httpHeaders.setBearerAuth(clientRegistrationResponse.getRegistrationAccessToken());
-
-		// ***** (3) Get RegisteredClient Configuration
-		mvcResult = this.mvc.perform(get(clientRegistrationResponse.getRegistrationClientUri().toString())
-						.headers(httpHeaders))
-				.andExpect(status().isOk())
-				.andExpect(header().string(HttpHeaders.CACHE_CONTROL, containsString("no-store")))
-				.andExpect(header().string(HttpHeaders.PRAGMA, containsString("no-cache")))
-				.andReturn();
-
-		OidcClientRegistration clientConfigurationResponse = readClientRegistrationResponse(mvcResult.getResponse());
-		assertThat(clientConfigurationResponse.getClientId()).isNotNull().isEqualTo(clientRegistrationResponse.getClientId());
-		assertThat(clientConfigurationResponse.getClientIdIssuedAt()).isNotNull();
-		assertThat(clientConfigurationResponse.getClientSecret()).isNotNull().isEqualTo(clientRegistrationResponse.getClientSecret());
-		assertThat(clientConfigurationResponse.getClientSecretExpiresAt()).isNull();
-		assertThat(clientConfigurationResponse.getClientName()).isEqualTo(clientRegistrationResponse.getClientName());
-		assertThat(clientConfigurationResponse.getRedirectUris())
-				.containsExactlyInAnyOrderElementsOf(clientRegistrationResponse.getRedirectUris());
-		assertThat(clientConfigurationResponse.getGrantTypes())
-				.containsExactlyInAnyOrderElementsOf(clientRegistrationResponse.getGrantTypes());
-		assertThat(clientConfigurationResponse.getResponseTypes())
-				.containsExactly(OAuth2AuthorizationResponseType.CODE.getValue());
-		assertThat(clientConfigurationResponse.getScopes())
-				.containsExactlyInAnyOrderElementsOf(clientRegistrationResponse.getScopes());
-		assertThat(clientConfigurationResponse.getTokenEndpointAuthenticationMethod())
-				.isEqualTo(ClientAuthenticationMethod.CLIENT_SECRET_BASIC.getValue());
-		assertThat(clientConfigurationResponse.getIdTokenSignedResponseAlgorithm())
-				.isEqualTo(SignatureAlgorithm.RS256.getName());
-		assertThat(clientConfigurationResponse.getRegistrationClientUri())
-				.isNotNull();
-		assertThat(clientConfigurationResponse.getRegistrationAccessToken()).isNull();
-	}
-
-	private static void assertClientConfigurationResponse(RegisteredClient registeredClient, MvcResult mvcResult) throws Exception {
-		OidcClientRegistration clientConfigurationResponse;
-		clientConfigurationResponse = readClientRegistrationResponse(mvcResult.getResponse());
-		assertThat(clientConfigurationResponse.getClientId()).isNotNull().isEqualTo(registeredClient.getClientId());
-		assertThat(clientConfigurationResponse.getClientIdIssuedAt()).isNotNull();
-		assertThat(clientConfigurationResponse.getClientSecret()).isNotNull().isEqualTo(registeredClient.getClientSecret());
-		assertThat(clientConfigurationResponse.getClientSecretExpiresAt()).isNull();
-		assertThat(clientConfigurationResponse.getClientName()).isEqualTo(registeredClient.getClientName());
-		assertThat(clientConfigurationResponse.getRedirectUris())
-				.containsExactlyInAnyOrderElementsOf(registeredClient.getRedirectUris());
-		assertThat(clientConfigurationResponse.getGrantTypes())
-				.containsExactlyInAnyOrderElementsOf(registeredClient.getAuthorizationGrantTypes().stream().map(AuthorizationGrantType::getValue).collect(Collectors.toList()));
-		assertThat(clientConfigurationResponse.getResponseTypes())
-				.containsExactly(OAuth2AuthorizationResponseType.CODE.getValue());
-		assertThat(clientConfigurationResponse.getScopes())
-				.containsExactlyInAnyOrderElementsOf(registeredClient.getScopes());
-		assertThat(clientConfigurationResponse.getTokenEndpointAuthenticationMethod())
-				.isEqualTo(ClientAuthenticationMethod.CLIENT_SECRET_BASIC.getValue());
-		assertThat(clientConfigurationResponse.getIdTokenSignedResponseAlgorithm())
-				.isEqualTo(SignatureAlgorithm.RS256.getName());
-		assertThat(clientConfigurationResponse.getRegistrationClientUri())
-				.isNotNull();
-		assertThat(clientConfigurationResponse.getRegistrationAccessToken()).isNull();
+		return readClientRegistrationResponse(mvcResult.getResponse());
 	}
 
-
 	private static String encodeBasicAuth(String clientId, String secret) throws Exception {
 		clientId = URLEncoder.encode(clientId, StandardCharsets.UTF_8.name());
 		secret = URLEncoder.encode(secret, StandardCharsets.UTF_8.name());
@@ -552,8 +340,9 @@ public class OidcClientRegistrationTests {
 
 		@Bean
 		ProviderSettings providerSettings() {
-			return ProviderSettings.builder().issuer("http://auth-server:9000")
-					.oidcClientRegistrationEndpoint("/connect/register").build();
+			return ProviderSettings.builder()
+					.issuer("https://auth-server:9000")
+					.build();
 		}
 
 		@Bean

+ 7 - 7
oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/core/oidc/OidcClientRegistrationTests.java

@@ -63,9 +63,9 @@ public class OidcClientRegistrationTests {
 				.scope("scope1")
 				.scope("scope2")
 				.idTokenSignedResponseAlgorithm(SignatureAlgorithm.RS256.getName())
-				.claim("a-claim", "a-value")
 				.registrationAccessToken("registration-access-token")
-				.registrationClientUri("https://auth-server.com/connect/register?client_id=1")
+				.registrationClientUrl("https://auth-server.com/connect/register?client_id=1")
+				.claim("a-claim", "a-value")
 				.build();
 		// @formatter:on
 
@@ -80,9 +80,9 @@ public class OidcClientRegistrationTests {
 		assertThat(clientRegistration.getResponseTypes()).containsOnly("code");
 		assertThat(clientRegistration.getScopes()).containsExactlyInAnyOrder("scope1", "scope2");
 		assertThat(clientRegistration.getIdTokenSignedResponseAlgorithm()).isEqualTo("RS256");
-		assertThat(clientRegistration.getClaimAsString("a-claim")).isEqualTo("a-value");
 		assertThat(clientRegistration.getRegistrationAccessToken()).isEqualTo("registration-access-token");
-		assertThat(clientRegistration.getRegistrationClientUri().toString()).isEqualTo("https://auth-server.com/connect/register?client_id=1");
+		assertThat(clientRegistration.getRegistrationClientUrl().toString()).isEqualTo("https://auth-server.com/connect/register?client_id=1");
+		assertThat(clientRegistration.getClaimAsString("a-claim")).isEqualTo("a-value");
 	}
 
 	@Test
@@ -108,9 +108,9 @@ public class OidcClientRegistrationTests {
 		claims.put(OidcClientMetadataClaimNames.RESPONSE_TYPES, Collections.singletonList("code"));
 		claims.put(OidcClientMetadataClaimNames.SCOPE, Arrays.asList("scope1", "scope2"));
 		claims.put(OidcClientMetadataClaimNames.ID_TOKEN_SIGNED_RESPONSE_ALG, SignatureAlgorithm.RS256.getName());
-		claims.put("a-claim", "a-value");
 		claims.put(OidcClientMetadataClaimNames.REGISTRATION_ACCESS_TOKEN, "registration-access-token");
 		claims.put(OidcClientMetadataClaimNames.REGISTRATION_CLIENT_URI, "https://auth-server.com/connect/register?client_id=1");
+		claims.put("a-claim", "a-value");
 
 		OidcClientRegistration clientRegistration = OidcClientRegistration.withClaims(claims).build();
 
@@ -125,9 +125,9 @@ public class OidcClientRegistrationTests {
 		assertThat(clientRegistration.getResponseTypes()).containsOnly("code");
 		assertThat(clientRegistration.getScopes()).containsExactlyInAnyOrder("scope1", "scope2");
 		assertThat(clientRegistration.getIdTokenSignedResponseAlgorithm()).isEqualTo("RS256");
-		assertThat(clientRegistration.getClaimAsString("a-claim")).isEqualTo("a-value");
 		assertThat(clientRegistration.getRegistrationAccessToken()).isEqualTo("registration-access-token");
-		assertThat(clientRegistration.getRegistrationClientUri().toString()).isEqualTo("https://auth-server.com/connect/register?client_id=1");
+		assertThat(clientRegistration.getRegistrationClientUrl().toString()).isEqualTo("https://auth-server.com/connect/register?client_id=1");
+		assertThat(clientRegistration.getClaimAsString("a-claim")).isEqualTo("a-value");
 	}
 
 	@Test

+ 3 - 3
oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/core/oidc/http/converter/OidcClientRegistrationHttpMessageConverterTests.java

@@ -184,9 +184,9 @@ public class OidcClientRegistrationHttpMessageConverterTests {
 				.scope("scope1")
 				.scope("scope2")
 				.idTokenSignedResponseAlgorithm(SignatureAlgorithm.RS256.getName())
-				.claim("a-claim", "a-value")
-				.registrationClientUri("https://auth-server.com/connect/register?client_id=1")
 				.registrationAccessToken("registration-access-token")
+				.registrationClientUrl("https://auth-server.com/connect/register?client_id=1")
+				.claim("a-claim", "a-value")
 				.build();
 		// @formatter:on
 
@@ -205,9 +205,9 @@ public class OidcClientRegistrationHttpMessageConverterTests {
 		assertThat(clientRegistrationResponse).contains("\"response_types\":[\"code\"]");
 		assertThat(clientRegistrationResponse).contains("\"scope\":\"scope1 scope2\"");
 		assertThat(clientRegistrationResponse).contains("\"id_token_signed_response_alg\":\"RS256\"");
-		assertThat(clientRegistrationResponse).contains("\"a-claim\":\"a-value\"");
 		assertThat(clientRegistrationResponse).contains("\"registration_access_token\":\"registration-access-token\"");
 		assertThat(clientRegistrationResponse).contains("\"registration_client_uri\":\"https://auth-server.com/connect/register?client_id=1\"");
+		assertThat(clientRegistrationResponse).contains("\"a-claim\":\"a-value\"");
 	}
 
 	@Test

+ 105 - 109
oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/oidc/authentication/OidcClientRegistrationAuthenticationProviderTests.java

@@ -16,13 +16,16 @@
 package org.springframework.security.oauth2.server.authorization.oidc.authentication;
 
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collections;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Set;
 
 import org.junit.Before;
 import org.junit.Test;
 import org.mockito.ArgumentCaptor;
+
 import org.springframework.security.authentication.TestingAuthenticationToken;
 import org.springframework.security.core.authority.AuthorityUtils;
 import org.springframework.security.oauth2.core.AuthorizationGrantType;
@@ -57,6 +60,7 @@ import static org.assertj.core.api.Assertions.assertThatThrownBy;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
@@ -70,34 +74,32 @@ import static org.mockito.Mockito.when;
 public class OidcClientRegistrationAuthenticationProviderTests {
 	private RegisteredClientRepository registeredClientRepository;
 	private OAuth2AuthorizationService authorizationService;
-	private OidcClientRegistrationAuthenticationProvider authenticationProvider;
 	private JwtEncoder jwtEncoder;
 	private ProviderSettings providerSettings;
+	private OidcClientRegistrationAuthenticationProvider authenticationProvider;
 
 	@Before
 	public void setUp() {
-
 		this.registeredClientRepository = mock(RegisteredClientRepository.class);
 		this.authorizationService = mock(OAuth2AuthorizationService.class);
 		this.jwtEncoder = mock(JwtEncoder.class);
-		this.providerSettings = ProviderSettings.builder().issuer("http://auth-server:9000").build();
+		this.providerSettings = ProviderSettings.builder().issuer("https://auth-server:9000").build();
 		this.authenticationProvider = new OidcClientRegistrationAuthenticationProvider(
-				this.registeredClientRepository, this.authorizationService,
-				this.jwtEncoder);
-		this.authenticationProvider.setProviderSettings(providerSettings);
+				this.registeredClientRepository, this.authorizationService, this.jwtEncoder);
+		this.authenticationProvider.setProviderSettings(this.providerSettings);
 	}
 
 	@Test
 	public void constructorWhenRegisteredClientRepositoryNullThenThrowIllegalArgumentException() {
 		assertThatIllegalArgumentException()
-				.isThrownBy(() -> new OidcClientRegistrationAuthenticationProvider(null, this.authorizationService, jwtEncoder))
+				.isThrownBy(() -> new OidcClientRegistrationAuthenticationProvider(null, this.authorizationService, this.jwtEncoder))
 				.withMessage("registeredClientRepository cannot be null");
 	}
 
 	@Test
 	public void constructorWhenAuthorizationServiceNullThenThrowIllegalArgumentException() {
 		assertThatIllegalArgumentException()
-				.isThrownBy(() -> new OidcClientRegistrationAuthenticationProvider(this.registeredClientRepository, null, jwtEncoder))
+				.isThrownBy(() -> new OidcClientRegistrationAuthenticationProvider(this.registeredClientRepository, null, this.jwtEncoder))
 				.withMessage("authorizationService cannot be null");
 	}
 
@@ -114,13 +116,14 @@ public class OidcClientRegistrationAuthenticationProviderTests {
 	}
 
 	@Test
-	public void authenticateWhenClientRegistrationRequestAndPrincipalNotOAuth2TokenAuthenticationTokenThenThrowOAuth2AuthenticationException() {
+	public void authenticateWhenPrincipalNotOAuth2TokenAuthenticationTokenThenThrowOAuth2AuthenticationException() {
 		TestingAuthenticationToken principal = new TestingAuthenticationToken("principal", "credentials");
 		OidcClientRegistration clientRegistration = OidcClientRegistration.builder()
 				.redirectUri("https://client.example.com")
 				.build();
 
-		OidcClientRegistrationAuthenticationToken authentication = new OidcClientRegistrationAuthenticationToken(principal, clientRegistration);
+		OidcClientRegistrationAuthenticationToken authentication = new OidcClientRegistrationAuthenticationToken(
+				principal, clientRegistration);
 
 		assertThatThrownBy(() -> this.authenticationProvider.authenticate(authentication))
 				.isInstanceOf(OAuth2AuthenticationException.class)
@@ -129,13 +132,14 @@ public class OidcClientRegistrationAuthenticationProviderTests {
 	}
 
 	@Test
-	public void authenticateWhenClientRegistrationRequestAndPrincipalNotAuthenticatedThenThrowOAuth2AuthenticationException() {
+	public void authenticateWhenPrincipalNotAuthenticatedThenThrowOAuth2AuthenticationException() {
 		JwtAuthenticationToken principal = new JwtAuthenticationToken(createJwtClientRegistration());
 		OidcClientRegistration clientRegistration = OidcClientRegistration.builder()
 				.redirectUri("https://client.example.com")
 				.build();
 
-		OidcClientRegistrationAuthenticationToken authentication = new OidcClientRegistrationAuthenticationToken(principal, clientRegistration);
+		OidcClientRegistrationAuthenticationToken authentication = new OidcClientRegistrationAuthenticationToken(
+				principal, clientRegistration);
 
 		assertThatThrownBy(() -> this.authenticationProvider.authenticate(authentication))
 				.isInstanceOf(OAuth2AuthenticationException.class)
@@ -144,7 +148,7 @@ public class OidcClientRegistrationAuthenticationProviderTests {
 	}
 
 	@Test
-	public void authenticateWhenClientRegistrationRequestAndAccessTokenNotFoundThenThrowOAuth2AuthenticationException() {
+	public void authenticateWhenAccessTokenNotFoundThenThrowOAuth2AuthenticationException() {
 		Jwt jwt = createJwtClientRegistration();
 		JwtAuthenticationToken principal = new JwtAuthenticationToken(
 				jwt, AuthorityUtils.createAuthorityList("SCOPE_client.create"));
@@ -152,7 +156,8 @@ public class OidcClientRegistrationAuthenticationProviderTests {
 				.redirectUri("https://client.example.com")
 				.build();
 
-		OidcClientRegistrationAuthenticationToken authentication = new OidcClientRegistrationAuthenticationToken(principal, clientRegistration);
+		OidcClientRegistrationAuthenticationToken authentication = new OidcClientRegistrationAuthenticationToken(
+				principal, clientRegistration);
 
 		assertThatThrownBy(() -> this.authenticationProvider.authenticate(authentication))
 				.isInstanceOf(OAuth2AuthenticationException.class)
@@ -163,7 +168,7 @@ public class OidcClientRegistrationAuthenticationProviderTests {
 	}
 
 	@Test
-	public void authenticateWhenClientRegistrationRequestAndAccessTokenNotActiveThenThrowOAuth2AuthenticationException() {
+	public void authenticateWhenAccessTokenNotActiveThenThrowOAuth2AuthenticationException() {
 		Jwt jwt = createJwtClientRegistration();
 		OAuth2AccessToken jwtAccessToken = new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER,
 				jwt.getTokenValue(), jwt.getIssuedAt(),
@@ -182,7 +187,8 @@ public class OidcClientRegistrationAuthenticationProviderTests {
 				.redirectUri("https://client.example.com")
 				.build();
 
-		OidcClientRegistrationAuthenticationToken authentication = new OidcClientRegistrationAuthenticationToken(principal, clientRegistration);
+		OidcClientRegistrationAuthenticationToken authentication = new OidcClientRegistrationAuthenticationToken(
+				principal, clientRegistration);
 
 		assertThatThrownBy(() -> this.authenticationProvider.authenticate(authentication))
 				.isInstanceOf(OAuth2AuthenticationException.class)
@@ -211,7 +217,8 @@ public class OidcClientRegistrationAuthenticationProviderTests {
 				.redirectUri("https://client.example.com")
 				.build();
 
-		OidcClientRegistrationAuthenticationToken authentication = new OidcClientRegistrationAuthenticationToken(principal, clientRegistration);
+		OidcClientRegistrationAuthenticationToken authentication = new OidcClientRegistrationAuthenticationToken(
+				principal, clientRegistration);
 
 		assertThatThrownBy(() -> this.authenticationProvider.authenticate(authentication))
 				.isInstanceOf(OAuth2AuthenticationException.class)
@@ -221,6 +228,36 @@ public class OidcClientRegistrationAuthenticationProviderTests {
 				eq(jwtAccessToken.getTokenValue()), eq(OAuth2TokenType.ACCESS_TOKEN));
 	}
 
+	@Test
+	public void authenticateWhenClientRegistrationRequestAndAccessTokenContainsRequiredScopeAndAdditionalScopeThenThrowOAuth2AuthenticationException() {
+		Jwt jwt = createJwt(new HashSet<>(Arrays.asList("client.create", "scope1")));
+		OAuth2AccessToken jwtAccessToken = new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER,
+				jwt.getTokenValue(), jwt.getIssuedAt(),
+				jwt.getExpiresAt(), jwt.getClaim(OAuth2ParameterNames.SCOPE));
+		RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();
+		OAuth2Authorization authorization = TestOAuth2Authorizations.authorization(
+				registeredClient, jwtAccessToken, jwt.getClaims()).build();
+		when(this.authorizationService.findByToken(
+				eq(jwtAccessToken.getTokenValue()), eq(OAuth2TokenType.ACCESS_TOKEN)))
+				.thenReturn(authorization);
+
+		JwtAuthenticationToken principal = new JwtAuthenticationToken(
+				jwt, AuthorityUtils.createAuthorityList("SCOPE_client.create", "SCOPE_scope1"));
+		OidcClientRegistration clientRegistration = OidcClientRegistration.builder()
+				.redirectUri("https://client.example.com")
+				.build();
+
+		OidcClientRegistrationAuthenticationToken authentication = new OidcClientRegistrationAuthenticationToken(
+				principal, clientRegistration);
+
+		assertThatThrownBy(() -> this.authenticationProvider.authenticate(authentication))
+				.isInstanceOf(OAuth2AuthenticationException.class)
+				.extracting(ex -> ((OAuth2AuthenticationException) ex).getError()).extracting("errorCode")
+				.isEqualTo(OAuth2ErrorCodes.INVALID_TOKEN);
+		verify(this.authorizationService).findByToken(
+				eq(jwtAccessToken.getTokenValue()), eq(OAuth2TokenType.ACCESS_TOKEN));
+	}
+
 	@Test
 	public void authenticateWhenClientRegistrationRequestAndInvalidRedirectUriThenThrowOAuth2AuthenticationException() {
 		Jwt jwt = createJwtClientRegistration();
@@ -242,7 +279,8 @@ public class OidcClientRegistrationAuthenticationProviderTests {
 				.build();
 		// @formatter:on
 
-		OidcClientRegistrationAuthenticationToken authentication = new OidcClientRegistrationAuthenticationToken(principal, clientRegistration);
+		OidcClientRegistrationAuthenticationToken authentication = new OidcClientRegistrationAuthenticationToken(
+				principal, clientRegistration);
 
 		assertThatThrownBy(() -> this.authenticationProvider.authenticate(authentication))
 				.isInstanceOf(OAuth2AuthenticationException.class)
@@ -273,7 +311,8 @@ public class OidcClientRegistrationAuthenticationProviderTests {
 				.build();
 		// @formatter:on
 
-		OidcClientRegistrationAuthenticationToken authentication = new OidcClientRegistrationAuthenticationToken(principal, clientRegistration);
+		OidcClientRegistrationAuthenticationToken authentication = new OidcClientRegistrationAuthenticationToken(
+				principal, clientRegistration);
 
 		assertThatThrownBy(() -> this.authenticationProvider.authenticate(authentication))
 				.isInstanceOf(OAuth2AuthenticationException.class)
@@ -295,7 +334,7 @@ public class OidcClientRegistrationAuthenticationProviderTests {
 		when(this.authorizationService.findByToken(
 				eq(jwtAccessToken.getTokenValue()), eq(OAuth2TokenType.ACCESS_TOKEN)))
 				.thenReturn(authorization);
-		when(this.jwtEncoder.encode(any(), any())).thenReturn(createJwt(Collections.singleton("client.read")));
+		when(this.jwtEncoder.encode(any(), any())).thenReturn(createJwtClientConfiguration());
 
 		JwtAuthenticationToken principal = new JwtAuthenticationToken(
 				jwt, AuthorityUtils.createAuthorityList("SCOPE_client.create"));
@@ -310,7 +349,8 @@ public class OidcClientRegistrationAuthenticationProviderTests {
 				.build();
 		// @formatter:on
 
-		OidcClientRegistrationAuthenticationToken authentication = new OidcClientRegistrationAuthenticationToken(principal, clientRegistration);
+		OidcClientRegistrationAuthenticationToken authentication = new OidcClientRegistrationAuthenticationToken(
+				principal, clientRegistration);
 		OidcClientRegistrationAuthenticationToken authenticationResult =
 				(OidcClientRegistrationAuthenticationToken) this.authenticationProvider.authenticate(authentication);
 
@@ -323,20 +363,20 @@ public class OidcClientRegistrationAuthenticationProviderTests {
 		verify(this.authorizationService, times(2)).save(authorizationCaptor.capture());
 		verify(this.jwtEncoder).encode(any(), any());
 
-		// assert access token
+		// assert "registration" access token, which should be used for subsequent calls to client configuration endpoint
 		OAuth2Authorization authorizationResult = authorizationCaptor.getAllValues().get(0);
+		assertThat(authorizationResult.getAccessToken().getToken().getScopes())
+				.containsExactly("client.read");
+		assertThat(authorizationResult.getAccessToken().isActive()).isTrue();
+		assertThat(authorizationResult.getRefreshToken()).isNull();
+
+		// assert "initial" access token is invalidated
+		authorizationResult = authorizationCaptor.getAllValues().get(1);
 		assertThat(authorizationResult.getAccessToken().isInvalidated()).isTrue();
 		if (authorizationResult.getRefreshToken() != null) {
 			assertThat(authorizationResult.getRefreshToken().isInvalidated()).isTrue();
 		}
 
-		// assert registration access token which should be used for subsequent calls to client configuration endpoint
-		authorizationResult = authorizationCaptor.getAllValues().get(1);
-		assertThat(authorizationResult.getAccessToken().isInvalidated()).isFalse();
-		assertThat(authorizationResult.getRefreshToken()).isNull();
-		assertThat(authorizationResult.getAccessToken().getToken().getScopes())
-				.containsExactly("client.read");
-
 		RegisteredClient registeredClientResult = registeredClientCaptor.getValue();
 		assertThat(registeredClientResult.getId()).isNotNull();
 		assertThat(registeredClientResult.getClientId()).isNotNull();
@@ -375,84 +415,44 @@ public class OidcClientRegistrationAuthenticationProviderTests {
 		assertThat(clientRegistrationResult.getIdTokenSignedResponseAlgorithm())
 				.isEqualTo(registeredClientResult.getTokenSettings().getIdTokenSignatureAlgorithm().getName());
 
-		String expectedRegistrationClientUri = UriComponentsBuilder.fromUriString(this.providerSettings.getIssuer())
+		String expectedRegistrationClientUrl = UriComponentsBuilder.fromUriString(this.providerSettings.getIssuer())
 				.path(this.providerSettings.getOidcClientRegistrationEndpoint())
-				.queryParam("client_id", registeredClientResult.getClientId()).toUriString();
-
-		assertThat(clientRegistrationResult.getRegistrationClientUri().toString()).isEqualTo(expectedRegistrationClientUri);
-		assertThat(clientRegistrationResult.getRegistrationAccessToken()).isNotEmpty().isEqualTo(jwt.getTokenValue());
-	}
-
-	@Test
-	public void authenticateWhenClientConfigurationRequestAndPrincipalNotOAuth2TokenAuthenticationTokenThenThrowOAuth2AuthenticationException() {
-		TestingAuthenticationToken principal = new TestingAuthenticationToken("principal", "credentials");
-
-		OidcClientRegistrationAuthenticationToken authentication = new OidcClientRegistrationAuthenticationToken(principal, "client-1");
-
-		assertThatThrownBy(() -> this.authenticationProvider.authenticate(authentication))
-				.isInstanceOf(OAuth2AuthenticationException.class)
-				.extracting(ex -> ((OAuth2AuthenticationException) ex).getError()).extracting("errorCode")
-				.isEqualTo(OAuth2ErrorCodes.INVALID_TOKEN);
-	}
+				.queryParam(OAuth2ParameterNames.CLIENT_ID, registeredClientResult.getClientId()).toUriString();
 
-	@Test
-	public void authenticateWhenClientConfigurationRequestAndPrincipalNotAuthenticatedThenThrowOAuth2AuthenticationException() {
-		JwtAuthenticationToken principal = new JwtAuthenticationToken(createJwtClientConfiguration());
-
-		OidcClientRegistrationAuthenticationToken authentication = new OidcClientRegistrationAuthenticationToken(principal, "client-1");
-
-		assertThatThrownBy(() -> this.authenticationProvider.authenticate(authentication))
-				.isInstanceOf(OAuth2AuthenticationException.class)
-				.extracting(ex -> ((OAuth2AuthenticationException) ex).getError()).extracting("errorCode")
-				.isEqualTo(OAuth2ErrorCodes.INVALID_TOKEN);
-	}
-
-	@Test
-	public void authenticateWhenClientConfigurationRequestAndAccessTokenNotFoundThenThrowOAuth2AuthenticationException() {
-		Jwt jwt = createJwtClientConfiguration();
-		JwtAuthenticationToken principal = new JwtAuthenticationToken(
-				jwt, AuthorityUtils.createAuthorityList("SCOPE_client.read"));
-
-		OidcClientRegistrationAuthenticationToken authentication = new OidcClientRegistrationAuthenticationToken(principal, "client-1");
-
-		assertThatThrownBy(() -> this.authenticationProvider.authenticate(authentication))
-				.isInstanceOf(OAuth2AuthenticationException.class)
-				.extracting(ex -> ((OAuth2AuthenticationException) ex).getError()).extracting("errorCode")
-				.isEqualTo(OAuth2ErrorCodes.INVALID_TOKEN);
-		verify(this.authorizationService).findByToken(
-				eq(jwt.getTokenValue()), eq(OAuth2TokenType.ACCESS_TOKEN));
+		assertThat(clientRegistrationResult.getRegistrationClientUrl().toString()).isEqualTo(expectedRegistrationClientUrl);
+		assertThat(clientRegistrationResult.getRegistrationAccessToken()).isEqualTo(jwt.getTokenValue());
 	}
 
 	@Test
-	public void authenticateWhenClientConfigurationRequestAndAccessTokenNotActiveThenThrowOAuth2AuthenticationException() {
-		Jwt jwt = createJwtClientConfiguration();
+	public void authenticateWhenClientConfigurationRequestAndAccessTokenNotAuthorizedThenThrowOAuth2AuthenticationException() {
+		Jwt jwt = createJwt(Collections.singleton("unauthorized.scope"));
 		OAuth2AccessToken jwtAccessToken = new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER,
 				jwt.getTokenValue(), jwt.getIssuedAt(),
 				jwt.getExpiresAt(), jwt.getClaim(OAuth2ParameterNames.SCOPE));
 		RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();
 		OAuth2Authorization authorization = TestOAuth2Authorizations.authorization(
 				registeredClient, jwtAccessToken, jwt.getClaims()).build();
-		authorization = OidcAuthenticationProviderUtils.invalidate(authorization, jwtAccessToken);
 		when(this.authorizationService.findByToken(
 				eq(jwtAccessToken.getTokenValue()), eq(OAuth2TokenType.ACCESS_TOKEN)))
 				.thenReturn(authorization);
 
 		JwtAuthenticationToken principal = new JwtAuthenticationToken(
-				jwt, AuthorityUtils.createAuthorityList("SCOPE_client.read"));
+				jwt, AuthorityUtils.createAuthorityList("SCOPE_unauthorized.scope"));
 
-		OidcClientRegistrationAuthenticationToken authentication = new OidcClientRegistrationAuthenticationToken(principal, "client-1");
+		OidcClientRegistrationAuthenticationToken authentication = new OidcClientRegistrationAuthenticationToken(
+				principal, registeredClient.getClientId());
 
 		assertThatThrownBy(() -> this.authenticationProvider.authenticate(authentication))
 				.isInstanceOf(OAuth2AuthenticationException.class)
 				.extracting(ex -> ((OAuth2AuthenticationException) ex).getError()).extracting("errorCode")
-				.isEqualTo(OAuth2ErrorCodes.INVALID_TOKEN);
+				.isEqualTo(OAuth2ErrorCodes.INSUFFICIENT_SCOPE);
 		verify(this.authorizationService).findByToken(
 				eq(jwtAccessToken.getTokenValue()), eq(OAuth2TokenType.ACCESS_TOKEN));
 	}
 
 	@Test
-	public void authenticateWhenClientConfigurationRequestAndAccessTokenNotAuthorizedThenThrowOAuth2AuthenticationException() {
-		Jwt jwt = createJwt(Collections.singleton("unauthorized.scope"));
+	public void authenticateWhenClientConfigurationRequestAndAccessTokenContainsRequiredScopeAndAdditionalScopeThenThrowOAuth2AuthenticationException() {
+		Jwt jwt = createJwt(new HashSet<>(Arrays.asList("client.read", "scope1")));
 		OAuth2AccessToken jwtAccessToken = new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER,
 				jwt.getTokenValue(), jwt.getIssuedAt(),
 				jwt.getExpiresAt(), jwt.getClaim(OAuth2ParameterNames.SCOPE));
@@ -464,14 +464,15 @@ public class OidcClientRegistrationAuthenticationProviderTests {
 				.thenReturn(authorization);
 
 		JwtAuthenticationToken principal = new JwtAuthenticationToken(
-				jwt, AuthorityUtils.createAuthorityList("SCOPE_unauthorized.scope"));
+				jwt, AuthorityUtils.createAuthorityList("SCOPE_client.read", "SCOPE_scope1"));
 
-		OidcClientRegistrationAuthenticationToken authentication = new OidcClientRegistrationAuthenticationToken(principal, registeredClient.getClientId());
+		OidcClientRegistrationAuthenticationToken authentication = new OidcClientRegistrationAuthenticationToken(
+				principal, registeredClient.getClientId());
 
 		assertThatThrownBy(() -> this.authenticationProvider.authenticate(authentication))
 				.isInstanceOf(OAuth2AuthenticationException.class)
 				.extracting(ex -> ((OAuth2AuthenticationException) ex).getError()).extracting("errorCode")
-				.isEqualTo(OAuth2ErrorCodes.INSUFFICIENT_SCOPE);
+				.isEqualTo(OAuth2ErrorCodes.INVALID_TOKEN);
 		verify(this.authorizationService).findByToken(
 				eq(jwtAccessToken.getTokenValue()), eq(OAuth2TokenType.ACCESS_TOKEN));
 	}
@@ -482,21 +483,18 @@ public class OidcClientRegistrationAuthenticationProviderTests {
 		OAuth2AccessToken jwtAccessToken = new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER,
 				jwt.getTokenValue(), jwt.getIssuedAt(),
 				jwt.getExpiresAt(), jwt.getClaim(OAuth2ParameterNames.SCOPE));
-		RegisteredClient registeredClient = TestRegisteredClients.registeredClient()
-				.build();
+		RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();
 		OAuth2Authorization authorization = TestOAuth2Authorizations.authorization(
 				registeredClient, jwtAccessToken, jwt.getClaims()).build();
 		when(this.authorizationService.findByToken(
 				eq(jwtAccessToken.getTokenValue()), eq(OAuth2TokenType.ACCESS_TOKEN)))
 				.thenReturn(authorization);
 
-		when(this.registeredClientRepository.findByClientId(eq(registeredClient.getClientId())))
-				.thenReturn(null);
-
 		JwtAuthenticationToken principal = new JwtAuthenticationToken(
 				jwt, AuthorityUtils.createAuthorityList("SCOPE_client.read"));
 
-		OidcClientRegistrationAuthenticationToken authentication = new OidcClientRegistrationAuthenticationToken(principal, registeredClient.getClientId());
+		OidcClientRegistrationAuthenticationToken authentication = new OidcClientRegistrationAuthenticationToken(
+				principal, registeredClient.getClientId());
 
 		assertThatThrownBy(() -> this.authenticationProvider.authenticate(authentication))
 				.isInstanceOf(OAuth2AuthenticationException.class)
@@ -509,30 +507,27 @@ public class OidcClientRegistrationAuthenticationProviderTests {
 	}
 
 	@Test
-	public void authenticateWhenClientConfigurationRequestRegisteredClientNotEqualToAuthorizationRegisteredClientThenThrowOAuth2AuthenticationException() {
+	public void authenticateWhenClientConfigurationRequestClientIdNotEqualToAuthorizedClientThenThrowOAuth2AuthenticationException() {
 		Jwt jwt = createJwtClientConfiguration();
 		OAuth2AccessToken jwtAccessToken = new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER,
 				jwt.getTokenValue(), jwt.getIssuedAt(),
 				jwt.getExpiresAt(), jwt.getClaim(OAuth2ParameterNames.SCOPE));
-
-		RegisteredClient registeredClient = TestRegisteredClients.registeredClient()
-				.id("registration-1").clientId("client-1").build();
-		RegisteredClient authorizationRegisteredClient = TestRegisteredClients.registeredClient()
+		RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();
+		RegisteredClient authorizedRegisteredClient = TestRegisteredClients.registeredClient()
 				.id("registration-2").clientId("client-2").build();
-
 		OAuth2Authorization authorization = TestOAuth2Authorizations.authorization(
-				authorizationRegisteredClient, jwtAccessToken, jwt.getClaims()).build();
+				authorizedRegisteredClient, jwtAccessToken, jwt.getClaims()).build();
 		when(this.authorizationService.findByToken(
 				eq(jwtAccessToken.getTokenValue()), eq(OAuth2TokenType.ACCESS_TOKEN)))
 				.thenReturn(authorization);
-
 		when(this.registeredClientRepository.findByClientId(eq(registeredClient.getClientId())))
 				.thenReturn(registeredClient);
 
 		JwtAuthenticationToken principal = new JwtAuthenticationToken(
 				jwt, AuthorityUtils.createAuthorityList("SCOPE_client.read"));
 
-		OidcClientRegistrationAuthenticationToken authentication = new OidcClientRegistrationAuthenticationToken(principal, registeredClient.getClientId());
+		OidcClientRegistrationAuthenticationToken authentication = new OidcClientRegistrationAuthenticationToken(
+				principal, registeredClient.getClientId());
 
 		assertThatThrownBy(() -> this.authenticationProvider.authenticate(authentication))
 				.isInstanceOf(OAuth2AuthenticationException.class)
@@ -558,15 +553,14 @@ public class OidcClientRegistrationAuthenticationProviderTests {
 		when(this.authorizationService.findByToken(
 				eq(jwtAccessToken.getTokenValue()), eq(OAuth2TokenType.ACCESS_TOKEN)))
 				.thenReturn(authorization);
+		when(this.registeredClientRepository.findByClientId(eq(registeredClient.getClientId())))
+				.thenReturn(registeredClient);
 
 		JwtAuthenticationToken principal = new JwtAuthenticationToken(
 				jwt, AuthorityUtils.createAuthorityList("SCOPE_client.read"));
 
-		when(this.registeredClientRepository.findByClientId(eq(registeredClient.getClientId())))
-				.thenReturn(registeredClient);
-
-		OidcClientRegistrationAuthenticationToken authentication =
-				new OidcClientRegistrationAuthenticationToken(principal, registeredClient.getClientId());
+		OidcClientRegistrationAuthenticationToken authentication = new OidcClientRegistrationAuthenticationToken(
+				principal, registeredClient.getClientId());
 
 		OidcClientRegistrationAuthenticationToken authenticationResult =
 				(OidcClientRegistrationAuthenticationToken) this.authenticationProvider.authenticate(authentication);
@@ -576,8 +570,8 @@ public class OidcClientRegistrationAuthenticationProviderTests {
 		verify(this.registeredClientRepository).findByClientId(
 				eq(registeredClient.getClientId()));
 
-		// verify that the registration access token is not invalidated after its used
-		verify(this.authorizationService, times(0)).save(eq(authorization));
+		// verify that the "registration" access token is not invalidated after it is used
+		verify(this.authorizationService, never()).save(eq(authorization));
 		assertThat(authorization.getAccessToken().isInvalidated()).isFalse();
 
 		OidcClientRegistration clientRegistrationResult = authenticationResult.getClientRegistration();
@@ -602,10 +596,12 @@ public class OidcClientRegistrationAuthenticationProviderTests {
 				.isEqualTo(registeredClient.getClientAuthenticationMethods().iterator().next().getValue());
 		assertThat(clientRegistrationResult.getIdTokenSignedResponseAlgorithm())
 				.isEqualTo(registeredClient.getTokenSettings().getIdTokenSignatureAlgorithm().getName());
-		String expectedRegistrationClientUri = UriComponentsBuilder.fromUriString(this.providerSettings.getIssuer())
+
+		String expectedRegistrationClientUrl = UriComponentsBuilder.fromUriString(this.providerSettings.getIssuer())
 				.path(this.providerSettings.getOidcClientRegistrationEndpoint())
-				.queryParam("client_id", registeredClient.getClientId()).toUriString();
-		assertThat(clientRegistrationResult.getRegistrationClientUri().toString()).isEqualTo(expectedRegistrationClientUri);
+				.queryParam(OAuth2ParameterNames.CLIENT_ID, registeredClient.getClientId()).toUriString();
+
+		assertThat(clientRegistrationResult.getRegistrationClientUrl().toString()).isEqualTo(expectedRegistrationClientUrl);
 		assertThat(clientRegistrationResult.getRegistrationAccessToken()).isNull();
 	}
 

+ 2 - 2
oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/oidc/authentication/OidcClientRegistrationAuthenticationTokenTests.java

@@ -51,14 +51,14 @@ public class OidcClientRegistrationAuthenticationTokenTests {
 	public void constructorWhenClientIdNullThenThrowIllegalArgumentException() {
 		assertThatIllegalArgumentException()
 				.isThrownBy(() -> new OidcClientRegistrationAuthenticationToken(this.principal, (String) null))
-				.withMessage("clientId cannot be null or empty");
+				.withMessage("clientId cannot be empty");
 	}
 
 	@Test
 	public void constructorWhenClientIdEmptyThenThrowIllegalArgumentException() {
 		assertThatIllegalArgumentException()
 				.isThrownBy(() -> new OidcClientRegistrationAuthenticationToken(this.principal, ""))
-				.withMessage("clientId cannot be null or empty");
+				.withMessage("clientId cannot be empty");
 	}
 
 	@Test

+ 42 - 54
oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/oidc/web/OidcClientRegistrationEndpointFilterTests.java

@@ -25,6 +25,7 @@ import javax.servlet.http.HttpServletResponse;
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
+
 import org.springframework.http.HttpStatus;
 import org.springframework.http.converter.HttpMessageConverter;
 import org.springframework.mock.http.client.MockClientHttpRequest;
@@ -219,8 +220,8 @@ public class OidcClientRegistrationEndpointFilterTests {
 				.tokenEndpointAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC.getValue())
 				.responseType(OAuth2AuthorizationResponseType.CODE.getValue())
 				.idTokenSignedResponseAlgorithm(SignatureAlgorithm.RS256.getName())
-				.registrationClientUri("http://auth-server:9000/connect/register?client_id=client-id")
 				.registrationAccessToken("registration-access-token")
+				.registrationClientUrl("https://auth-server:9000/connect/register?client_id=client-id")
 				.build();
 		// @formatter:on
 
@@ -270,23 +271,10 @@ public class OidcClientRegistrationEndpointFilterTests {
 				.isEqualTo(expectedClientRegistrationResponse.getTokenEndpointAuthenticationMethod());
 		assertThat(clientRegistrationResponse.getIdTokenSignedResponseAlgorithm())
 				.isEqualTo(expectedClientRegistrationResponse.getIdTokenSignedResponseAlgorithm());
-		assertThat(clientRegistrationResponse.getRegistrationClientUri())
-				.isEqualTo(expectedClientRegistrationResponse.getRegistrationClientUri());
 		assertThat(clientRegistrationResponse.getRegistrationAccessToken())
 				.isEqualTo(expectedClientRegistrationResponse.getRegistrationAccessToken());
-	}
-
-	@Test
-	public void doFilterWhenNotClientConfigurationRequestThenNotProcessed() throws Exception {
-		String requestUri = "/path";
-		MockHttpServletRequest request = new MockHttpServletRequest("GET", requestUri);
-		request.setServletPath(requestUri);
-		MockHttpServletResponse response = new MockHttpServletResponse();
-		FilterChain filterChain = mock(FilterChain.class);
-
-		this.filter.doFilter(request, response, filterChain);
-
-		verify(filterChain).doFilter(any(HttpServletRequest.class), any(HttpServletResponse.class));
+		assertThat(clientRegistrationResponse.getRegistrationClientUrl())
+				.isEqualTo(expectedClientRegistrationResponse.getRegistrationClientUrl());
 	}
 
 	@Test
@@ -330,7 +318,7 @@ public class OidcClientRegistrationEndpointFilterTests {
 	}
 
 	@Test
-	public void doFilterWhenClientConfigurationRequestMultipleClientIdParametersThenInvalidClientError() throws Exception {
+	public void doFilterWhenClientConfigurationRequestMultipleClientIdThenInvalidRequestError() throws Exception {
 		String requestUri = DEFAULT_OIDC_CLIENT_REGISTRATION_ENDPOINT_URI;
 		MockHttpServletRequest request = new MockHttpServletRequest("GET", requestUri);
 		request.setServletPath(requestUri);
@@ -340,10 +328,11 @@ public class OidcClientRegistrationEndpointFilterTests {
 		FilterChain filterChain = mock(FilterChain.class);
 
 		this.filter.doFilter(request, response, filterChain);
+
 		verifyNoInteractions(filterChain);
-		assertThat(response.getStatus()).isEqualTo(HttpStatus.UNAUTHORIZED.value());
+		assertThat(response.getStatus()).isEqualTo(HttpStatus.BAD_REQUEST.value());
 		OAuth2Error error = readError(response);
-		assertThat(error.getErrorCode()).isEqualTo(OAuth2ErrorCodes.INVALID_CLIENT);
+		assertThat(error.getErrorCode()).isEqualTo(OAuth2ErrorCodes.INVALID_REQUEST);
 	}
 
 	@Test
@@ -353,7 +342,7 @@ public class OidcClientRegistrationEndpointFilterTests {
 	}
 
 	@Test
-	public void doFilterWhenClientConfigurationRequestInsufficientTokenScopeThenForbiddenError() throws Exception {
+	public void doFilterWhenClientConfigurationRequestInsufficientScopeThenForbiddenError() throws Exception {
 		doFilterWhenClientConfigurationRequestInvalidThenError(
 				OAuth2ErrorCodes.INSUFFICIENT_SCOPE, HttpStatus.FORBIDDEN);
 	}
@@ -364,6 +353,35 @@ public class OidcClientRegistrationEndpointFilterTests {
 				OAuth2ErrorCodes.INVALID_CLIENT, HttpStatus.UNAUTHORIZED);
 	}
 
+	private void doFilterWhenClientConfigurationRequestInvalidThenError(
+			String errorCode, HttpStatus status) throws Exception {
+		Jwt jwt = createJwt("client.read");
+		JwtAuthenticationToken principal = new JwtAuthenticationToken(
+				jwt, AuthorityUtils.createAuthorityList("SCOPE_client.read"));
+
+		SecurityContext securityContext = SecurityContextHolder.createEmptyContext();
+		securityContext.setAuthentication(principal);
+		SecurityContextHolder.setContext(securityContext);
+
+		when(this.authenticationManager.authenticate(any()))
+				.thenThrow(new OAuth2AuthenticationException(errorCode));
+
+		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);
+
+		verifyNoInteractions(filterChain);
+
+		assertThat(response.getStatus()).isEqualTo(status.value());
+		OAuth2Error error = readError(response);
+		assertThat(error.getErrorCode()).isEqualTo(errorCode);
+	}
+
 	@Test
 	public void doFilterWhenClientConfigurationRequestValidThenSuccessResponse() throws Exception {
 		// @formatter:off
@@ -380,7 +398,7 @@ public class OidcClientRegistrationEndpointFilterTests {
 				.idTokenSignedResponseAlgorithm(SignatureAlgorithm.RS256.getName())
 				.scope("scope1")
 				.scope("scope2")
-				.registrationClientUri("http://auth-server:9000/connect/register?client_id=client-id")
+				.registrationClientUrl("https://auth-server:9000/connect/register?client_id=client-id")
 				.build();
 		// @formatter:on
 
@@ -400,7 +418,7 @@ public class OidcClientRegistrationEndpointFilterTests {
 		String requestUri = DEFAULT_OIDC_CLIENT_REGISTRATION_ENDPOINT_URI;
 		MockHttpServletRequest request = new MockHttpServletRequest("GET", requestUri);
 		request.setServletPath(requestUri);
-		request.setParameter(OAuth2ParameterNames.CLIENT_ID, "client-id");
+		request.setParameter(OAuth2ParameterNames.CLIENT_ID, expectedClientRegistrationResponse.getClientId());
 
 		MockHttpServletResponse response = new MockHttpServletResponse();
 		FilterChain filterChain = mock(FilterChain.class);
@@ -430,38 +448,8 @@ public class OidcClientRegistrationEndpointFilterTests {
 				.isEqualTo(expectedClientRegistrationResponse.getTokenEndpointAuthenticationMethod());
 		assertThat(clientRegistrationResponse.getIdTokenSignedResponseAlgorithm())
 				.isEqualTo(expectedClientRegistrationResponse.getIdTokenSignedResponseAlgorithm());
-		assertThat(clientRegistrationResponse.getRegistrationClientUri())
-				.isEqualTo(expectedClientRegistrationResponse.getRegistrationClientUri());
-	}
-
-
-	private void doFilterWhenClientConfigurationRequestInvalidThenError(
-			String errorCode, HttpStatus status) throws Exception {
-		Jwt jwt = createJwt("client.read");
-		JwtAuthenticationToken principal = new JwtAuthenticationToken(
-				jwt, AuthorityUtils.createAuthorityList("SCOPE_client.read"));
-
-		SecurityContext securityContext = SecurityContextHolder.createEmptyContext();
-		securityContext.setAuthentication(principal);
-		SecurityContextHolder.setContext(securityContext);
-
-		when(this.authenticationManager.authenticate(any()))
-				.thenThrow(new OAuth2AuthenticationException(errorCode));
-
-		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);
-
-		verifyNoInteractions(filterChain);
-
-		assertThat(response.getStatus()).isEqualTo(status.value());
-		OAuth2Error error = readError(response);
-		assertThat(error.getErrorCode()).isEqualTo(errorCode);
+		assertThat(clientRegistrationResponse.getRegistrationClientUrl())
+				.isEqualTo(expectedClientRegistrationResponse.getRegistrationClientUrl());
 	}
 
 	private OAuth2Error readError(MockHttpServletResponse response) throws Exception {