ClientRegistration contains Provider Configuration Metadata

Fixes gh-5540
This commit is contained in:
Joe Grandja 2018-08-23 16:33:05 -04:00
parent c60fcf263e
commit 057587ef29
4 changed files with 103 additions and 12 deletions

View File

@ -24,7 +24,9 @@ import org.springframework.util.StringUtils;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;
/**
@ -153,6 +155,7 @@ public final class ClientRegistration {
private String tokenUri;
private UserInfoEndpoint userInfoEndpoint = new UserInfoEndpoint();
private String jwkSetUri;
private Map<String, Object> configurationMetadata = Collections.emptyMap();
private ProviderDetails() {
}
@ -193,6 +196,16 @@ public final class ClientRegistration {
return this.jwkSetUri;
}
/**
* Returns a {@code Map} of the metadata describing the provider's configuration.
*
* @since 5.1
* @return a {@code Map} of the metadata describing the provider's configuration
*/
public Map<String, Object> getConfigurationMetadata() {
return this.configurationMetadata;
}
/**
* Details of the UserInfo Endpoint.
*/
@ -262,6 +275,7 @@ public final class ClientRegistration {
private AuthenticationMethod userInfoAuthenticationMethod = AuthenticationMethod.HEADER;
private String userNameAttributeName;
private String jwkSetUri;
private Map<String, Object> configurationMetadata = Collections.emptyMap();
private String clientName;
private Builder(String registrationId) {
@ -430,6 +444,20 @@ public final class ClientRegistration {
return this;
}
/**
* Sets the metadata describing the provider's configuration.
*
* @since 5.1
* @param configurationMetadata the metadata describing the provider's configuration
* @return the {@link Builder}
*/
public Builder providerConfigurationMetadata(Map<String, Object> configurationMetadata) {
if (configurationMetadata != null) {
this.configurationMetadata = new LinkedHashMap<>(configurationMetadata);
}
return this;
}
/**
* Sets the logical name of the client or registration.
*
@ -476,6 +504,7 @@ public final class ClientRegistration {
providerDetails.userInfoEndpoint.authenticationMethod = this.userInfoAuthenticationMethod;
providerDetails.userInfoEndpoint.userNameAttributeName = this.userNameAttributeName;
providerDetails.jwkSetUri = this.jwkSetUri;
providerDetails.configurationMetadata = Collections.unmodifiableMap(this.configurationMetadata);
clientRegistration.providerDetails = providerDetails;
clientRegistration.clientName = StringUtils.hasText(this.clientName) ?

View File

@ -16,21 +16,22 @@
package org.springframework.security.oauth2.client.registration;
import java.net.URI;
import java.util.Collections;
import java.util.List;
import com.nimbusds.oauth2.sdk.GrantType;
import com.nimbusds.oauth2.sdk.ParseException;
import com.nimbusds.oauth2.sdk.Scope;
import com.nimbusds.openid.connect.sdk.op.OIDCProviderMetadata;
import org.springframework.security.oauth2.core.AuthorizationGrantType;
import org.springframework.security.oauth2.core.ClientAuthenticationMethod;
import org.springframework.security.oauth2.core.oidc.IdTokenClaimNames;
import org.springframework.security.oauth2.core.oidc.OidcScopes;
import org.springframework.web.client.RestTemplate;
import java.net.URI;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
/**
* Allows creating a {@link ClientRegistration.Builder} from an
* <a href="https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderConfig">OpenID Provider Configuration</a>.
@ -83,6 +84,8 @@ public final class ClientRegistrations {
throw new IllegalArgumentException("Only AuthorizationGrantType.AUTHORIZATION_CODE is supported. The issuer \"" + issuer + "\" returned a configuration of " + grantTypes);
}
List<String> scopes = getScopes(metadata);
Map<String, Object> configurationMetadata = new LinkedHashMap<>(metadata.toJSONObject());
return ClientRegistration.withRegistrationId(name)
.userNameAttributeName(IdTokenClaimNames.SUB)
.scope(scopes)
@ -91,6 +94,7 @@ public final class ClientRegistrations {
.redirectUriTemplate("{baseUrl}/{action}/oauth2/code/{registrationId}")
.authorizationUri(metadata.getAuthorizationEndpointURI().toASCIIString())
.jwkSetUri(metadata.getJWKSetURI().toASCIIString())
.providerConfigurationMetadata(configurationMetadata)
.userInfoUri(metadata.getUserInfoEndpointURI().toASCIIString())
.tokenUri(metadata.getTokenEndpointURI().toASCIIString())
.clientName(issuer);

View File

@ -20,9 +20,12 @@ import org.springframework.security.oauth2.core.AuthenticationMethod;
import org.springframework.security.oauth2.core.AuthorizationGrantType;
import org.springframework.security.oauth2.core.ClientAuthenticationMethod;
import java.util.Arrays;
import java.util.LinkedHashSet;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
@ -37,11 +40,21 @@ public class ClientRegistrationTests {
private static final String CLIENT_ID = "client-1";
private static final String CLIENT_SECRET = "secret";
private static final String REDIRECT_URI = "https://example.com";
private static final Set<String> SCOPES = new LinkedHashSet<>(Arrays.asList("openid", "profile", "email"));
private static final Set<String> SCOPES = Collections.unmodifiableSet(
Stream.of("openid", "profile", "email").collect(Collectors.toSet()));
private static final String AUTHORIZATION_URI = "https://provider.com/oauth2/authorization";
private static final String TOKEN_URI = "https://provider.com/oauth2/token";
private static final String JWK_SET_URI = "https://provider.com/oauth2/keys";
private static final String CLIENT_NAME = "Client 1";
private static final Map<String, Object> PROVIDER_CONFIGURATION_METADATA =
Collections.unmodifiableMap(createProviderConfigurationMetadata());
private static Map<String, Object> createProviderConfigurationMetadata() {
Map<String, Object> configurationMetadata = new LinkedHashMap<>();
configurationMetadata.put("config-1", "value-1");
configurationMetadata.put("config-2", "value-2");
return configurationMetadata;
}
@Test(expected = IllegalArgumentException.class)
public void buildWhenAuthorizationGrantTypeIsNullThenThrowIllegalArgumentException() {
@ -73,6 +86,7 @@ public class ClientRegistrationTests {
.tokenUri(TOKEN_URI)
.userInfoAuthenticationMethod(AuthenticationMethod.FORM)
.jwkSetUri(JWK_SET_URI)
.providerConfigurationMetadata(PROVIDER_CONFIGURATION_METADATA)
.clientName(CLIENT_NAME)
.build();
@ -87,6 +101,7 @@ public class ClientRegistrationTests {
assertThat(registration.getProviderDetails().getTokenUri()).isEqualTo(TOKEN_URI);
assertThat(registration.getProviderDetails().getUserInfoEndpoint().getAuthenticationMethod()).isEqualTo(AuthenticationMethod.FORM);
assertThat(registration.getProviderDetails().getJwkSetUri()).isEqualTo(JWK_SET_URI);
assertThat(registration.getProviderDetails().getConfigurationMetadata()).isEqualTo(PROVIDER_CONFIGURATION_METADATA);
assertThat(registration.getClientName()).isEqualTo(CLIENT_NAME);
}
@ -276,6 +291,46 @@ public class ClientRegistrationTests {
.build();
}
@Test
public void buildWhenAuthorizationCodeGrantProviderConfigurationMetadataIsNullThenDefaultToEmpty() {
ClientRegistration clientRegistration = ClientRegistration.withRegistrationId(REGISTRATION_ID)
.clientId(CLIENT_ID)
.clientSecret(CLIENT_SECRET)
.clientAuthenticationMethod(ClientAuthenticationMethod.BASIC)
.authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)
.redirectUriTemplate(REDIRECT_URI)
.scope(SCOPES.toArray(new String[0]))
.authorizationUri(AUTHORIZATION_URI)
.tokenUri(TOKEN_URI)
.userInfoAuthenticationMethod(AuthenticationMethod.HEADER)
.providerConfigurationMetadata(null)
.jwkSetUri(JWK_SET_URI)
.clientName(CLIENT_NAME)
.build();
assertThat(clientRegistration.getProviderDetails().getConfigurationMetadata()).isNotNull();
assertThat(clientRegistration.getProviderDetails().getConfigurationMetadata()).isEmpty();
}
@Test
public void buildWhenAuthorizationCodeGrantProviderConfigurationMetadataEmptyThenIsEmpty() {
ClientRegistration clientRegistration = ClientRegistration.withRegistrationId(REGISTRATION_ID)
.clientId(CLIENT_ID)
.clientSecret(CLIENT_SECRET)
.clientAuthenticationMethod(ClientAuthenticationMethod.BASIC)
.authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)
.redirectUriTemplate(REDIRECT_URI)
.scope(SCOPES.toArray(new String[0]))
.authorizationUri(AUTHORIZATION_URI)
.tokenUri(TOKEN_URI)
.userInfoAuthenticationMethod(AuthenticationMethod.HEADER)
.providerConfigurationMetadata(Collections.emptyMap())
.jwkSetUri(JWK_SET_URI)
.clientName(CLIENT_NAME)
.build();
assertThat(clientRegistration.getProviderDetails().getConfigurationMetadata()).isNotNull();
assertThat(clientRegistration.getProviderDetails().getConfigurationMetadata()).isEmpty();
}
@Test
public void buildWhenImplicitGrantAllAttributesProvidedThenAllAttributesAreSet() {
ClientRegistration registration = ClientRegistration.withRegistrationId(REGISTRATION_ID)

View File

@ -16,9 +16,6 @@
package org.springframework.security.oauth2.client.registration;
import java.util.Arrays;
import java.util.Map;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import okhttp3.mockwebserver.MockResponse;
@ -26,12 +23,14 @@ import okhttp3.mockwebserver.MockWebServer;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.security.oauth2.core.AuthorizationGrantType;
import org.springframework.security.oauth2.core.ClientAuthenticationMethod;
import java.util.Arrays;
import java.util.Map;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
@ -131,6 +130,10 @@ public class ClientRegistrationsTest {
assertThat(provider.getAuthorizationUri()).isEqualTo("https://example.com/o/oauth2/v2/auth");
assertThat(provider.getTokenUri()).isEqualTo("https://example.com/oauth2/v4/token");
assertThat(provider.getJwkSetUri()).isEqualTo("https://example.com/oauth2/v3/certs");
assertThat(provider.getConfigurationMetadata()).containsKeys("authorization_endpoint", "claims_supported",
"code_challenge_methods_supported", "id_token_signing_alg_values_supported", "issuer", "jwks_uri",
"response_types_supported", "revocation_endpoint", "scopes_supported", "subject_types_supported",
"grant_types_supported", "token_endpoint", "token_endpoint_auth_methods_supported", "userinfo_endpoint");
assertThat(provider.getUserInfoEndpoint().getUri()).isEqualTo("https://example.com/oauth2/v3/userinfo");
}