Browse Source

Add issuerUri to ClientRegistration.providerDetails

- Add "issuerUri" attribute to ClientRegistration.providerDetails for OpenID Connect Discovery 1.0 or OAuth 2.0 Authorization Server Metadata.
- Validate OidcIdToken "iss" claim against the OpenID Provider "issuerUri" value.
- Update documentation for client registration: it includes issuer-uri property now.

Fixes gh-8326
pull/8540/head
Thomas Vitale 6 years ago committed by Joe Grandja
parent
commit
78fa859798
  1. 1
      config/src/main/java/org/springframework/security/config/oauth2/client/CommonOAuth2Provider.java
  2. 2
      config/src/test/java/org/springframework/security/config/oauth2/client/ClientRegistrationsBeanDefinitionParserTests.java
  3. 4
      docs/manual/src/docs/asciidoc/_includes/servlet/oauth2/oauth2-login.adoc
  4. 3
      oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/oidc/authentication/OidcIdTokenValidator.java
  5. 25
      oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/registration/ClientRegistration.java
  6. 1
      oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/registration/ClientRegistrations.java
  7. 4
      oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/jackson2/OAuth2AuthorizedClientMixinTests.java
  8. 8
      oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/oidc/authentication/OidcIdTokenValidatorTests.java
  9. 1
      oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/registration/ClientRegistrationsTest.java

1
config/src/main/java/org/springframework/security/config/oauth2/client/CommonOAuth2Provider.java

@ -42,6 +42,7 @@ public enum CommonOAuth2Provider { @@ -42,6 +42,7 @@ public enum CommonOAuth2Provider {
builder.tokenUri("https://www.googleapis.com/oauth2/v4/token");
builder.jwkSetUri("https://www.googleapis.com/oauth2/v3/certs");
builder.userInfoUri("https://www.googleapis.com/oauth2/v3/userinfo");
builder.issuerUri("https://accounts.google.com");
builder.userNameAttributeName(IdTokenClaimNames.SUB);
builder.clientName("Google");
return builder;

2
config/src/test/java/org/springframework/security/config/oauth2/client/ClientRegistrationsBeanDefinitionParserTests.java

@ -165,6 +165,7 @@ public class ClientRegistrationsBeanDefinitionParserTests { @@ -165,6 +165,7 @@ public class ClientRegistrationsBeanDefinitionParserTests {
.isEqualTo(AuthenticationMethod.HEADER);
assertThat(googleProviderDetails.getUserInfoEndpoint().getUserNameAttributeName()).isEqualTo("sub");
assertThat(googleProviderDetails.getJwkSetUri()).isEqualTo("https://example.com/oauth2/v3/certs");
assertThat(googleProviderDetails.getIssuerUri()).isEqualTo(serverUrl);
}
@Test
@ -195,6 +196,7 @@ public class ClientRegistrationsBeanDefinitionParserTests { @@ -195,6 +196,7 @@ public class ClientRegistrationsBeanDefinitionParserTests {
.isEqualTo(AuthenticationMethod.HEADER);
assertThat(googleProviderDetails.getUserInfoEndpoint().getUserNameAttributeName()).isEqualTo("sub");
assertThat(googleProviderDetails.getJwkSetUri()).isEqualTo("https://www.googleapis.com/oauth2/v3/certs");
assertThat(googleProviderDetails.getIssuerUri()).isEqualTo("https://accounts.google.com");
ClientRegistration githubRegistration = clientRegistrationRepository.findByRegistrationId("github-login");
assertThat(githubRegistration).isNotNull();

4
docs/manual/src/docs/asciidoc/_includes/servlet/oauth2/oauth2-login.adoc

@ -137,9 +137,11 @@ The following table outlines the mapping of the Spring Boot 2.x OAuth Client pro @@ -137,9 +137,11 @@ The following table outlines the mapping of the Spring Boot 2.x OAuth Client pro
|`spring.security.oauth2.client.provider._[providerId]_.user-info-authentication-method`
|`providerDetails.userInfoEndpoint.authenticationMethod`
|`spring.security.oauth2.client.provider._[providerId]_.user-name-attribute`
|`providerDetails.userInfoEndpoint.userNameAttributeName`
|`spring.security.oauth2.client.provider._[providerId]_.issuer-uri`
|`providerDetails.issuerUri`
|===
[TIP]

3
oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/oidc/authentication/OidcIdTokenValidator.java

@ -69,8 +69,7 @@ public final class OidcIdTokenValidator implements OAuth2TokenValidator<Jwt> { @@ -69,8 +69,7 @@ public final class OidcIdTokenValidator implements OAuth2TokenValidator<Jwt> {
// 2. The Issuer Identifier for the OpenID Provider (which is typically obtained during Discovery)
// MUST exactly match the value of the iss (issuer) Claim.
String metadataIssuer = (String) this.clientRegistration.getProviderDetails().getConfigurationMetadata()
.get("issuer");
String metadataIssuer = this.clientRegistration.getProviderDetails().getIssuerUri();
if (metadataIssuer != null && !Objects.equals(metadataIssuer, idToken.getIssuer().toExternalForm())) {
invalidClaims.put(IdTokenClaimNames.ISS, idToken.getIssuer());

25
oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/registration/ClientRegistration.java

@ -163,6 +163,7 @@ public final class ClientRegistration implements Serializable { @@ -163,6 +163,7 @@ public final class ClientRegistration implements Serializable {
private String tokenUri;
private UserInfoEndpoint userInfoEndpoint = new UserInfoEndpoint();
private String jwkSetUri;
private String issuerUri;
private Map<String, Object> configurationMetadata = Collections.emptyMap();
private ProviderDetails() {
@ -204,6 +205,16 @@ public final class ClientRegistration implements Serializable { @@ -204,6 +205,16 @@ public final class ClientRegistration implements Serializable {
return this.jwkSetUri;
}
/**
* Returns the uri for the OpenID Provider Issuer.
*
* @since 5.4
* @return the uri for the OpenID Provider Issuer
*/
public String getIssuerUri() {
return this.issuerUri;
}
/**
* Returns a {@code Map} of the metadata describing the provider's configuration.
*
@ -296,6 +307,7 @@ public final class ClientRegistration implements Serializable { @@ -296,6 +307,7 @@ public final class ClientRegistration implements Serializable {
private AuthenticationMethod userInfoAuthenticationMethod = AuthenticationMethod.HEADER;
private String userNameAttributeName;
private String jwkSetUri;
private String issuerUri;
private Map<String, Object> configurationMetadata = Collections.emptyMap();
private String clientName;
@ -317,6 +329,7 @@ public final class ClientRegistration implements Serializable { @@ -317,6 +329,7 @@ public final class ClientRegistration implements Serializable {
this.userInfoAuthenticationMethod = clientRegistration.providerDetails.userInfoEndpoint.authenticationMethod;
this.userNameAttributeName = clientRegistration.providerDetails.userInfoEndpoint.userNameAttributeName;
this.jwkSetUri = clientRegistration.providerDetails.jwkSetUri;
this.issuerUri = clientRegistration.providerDetails.issuerUri;
Map<String, Object> configurationMetadata = clientRegistration.providerDetails.configurationMetadata;
if (configurationMetadata != EMPTY_MAP) {
this.configurationMetadata = new HashMap<>(configurationMetadata);
@ -486,6 +499,17 @@ public final class ClientRegistration implements Serializable { @@ -486,6 +499,17 @@ public final class ClientRegistration implements Serializable {
return this;
}
/**
* Sets the uri for the OpenID Provider Issuer.
*
* @param issuerUri the uri for the OpenID Provider Issuer
* @return the {@link Builder}
*/
public Builder issuerUri(String issuerUri) {
this.issuerUri = issuerUri;
return this;
}
/**
* Sets the metadata describing the provider's configuration.
*
@ -554,6 +578,7 @@ public final class ClientRegistration implements Serializable { @@ -554,6 +578,7 @@ public final class ClientRegistration implements Serializable {
providerDetails.userInfoEndpoint.authenticationMethod = this.userInfoAuthenticationMethod;
providerDetails.userInfoEndpoint.userNameAttributeName = this.userNameAttributeName;
providerDetails.jwkSetUri = this.jwkSetUri;
providerDetails.issuerUri = this.issuerUri;
providerDetails.configurationMetadata = Collections.unmodifiableMap(this.configurationMetadata);
clientRegistration.providerDetails = providerDetails;

1
oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/registration/ClientRegistrations.java

@ -248,6 +248,7 @@ public final class ClientRegistrations { @@ -248,6 +248,7 @@ public final class ClientRegistrations {
.authorizationUri(metadata.getAuthorizationEndpointURI().toASCIIString())
.providerConfigurationMetadata(configurationMetadata)
.tokenUri(metadata.getTokenEndpointURI().toASCIIString())
.issuerUri(issuer)
.clientName(issuer);
}

4
oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/jackson2/OAuth2AuthorizedClientMixinTests.java

@ -139,6 +139,8 @@ public class OAuth2AuthorizedClientMixinTests { @@ -139,6 +139,8 @@ public class OAuth2AuthorizedClientMixinTests {
.isEqualTo(expectedClientRegistration.getProviderDetails().getUserInfoEndpoint().getUserNameAttributeName());
assertThat(clientRegistration.getProviderDetails().getJwkSetUri())
.isEqualTo(expectedClientRegistration.getProviderDetails().getJwkSetUri());
assertThat(clientRegistration.getProviderDetails().getIssuerUri())
.isEqualTo(expectedClientRegistration.getProviderDetails().getIssuerUri());
assertThat(clientRegistration.getProviderDetails().getConfigurationMetadata())
.containsExactlyEntriesOf(clientRegistration.getProviderDetails().getConfigurationMetadata());
assertThat(clientRegistration.getClientName())
@ -203,6 +205,7 @@ public class OAuth2AuthorizedClientMixinTests { @@ -203,6 +205,7 @@ public class OAuth2AuthorizedClientMixinTests {
.isEqualTo(expectedClientRegistration.getProviderDetails().getUserInfoEndpoint().getAuthenticationMethod());
assertThat(clientRegistration.getProviderDetails().getUserInfoEndpoint().getUserNameAttributeName()).isNull();
assertThat(clientRegistration.getProviderDetails().getJwkSetUri()).isNull();
assertThat(clientRegistration.getProviderDetails().getIssuerUri()).isNull();
assertThat(clientRegistration.getProviderDetails().getConfigurationMetadata()).isEmpty();
assertThat(clientRegistration.getClientName())
.isEqualTo(clientRegistration.getRegistrationId());
@ -276,6 +279,7 @@ public class OAuth2AuthorizedClientMixinTests { @@ -276,6 +279,7 @@ public class OAuth2AuthorizedClientMixinTests {
" \"userNameAttributeName\": " + (userInfoEndpoint.getUserNameAttributeName() != null ? "\"" + userInfoEndpoint.getUserNameAttributeName() + "\"" : null) + "\n" +
" },\n" +
" \"jwkSetUri\": " + (providerDetails.getJwkSetUri() != null ? "\"" + providerDetails.getJwkSetUri() + "\"" : null) + ",\n" +
" \"issuerUri\": " + (providerDetails.getIssuerUri() != null ? "\"" + providerDetails.getIssuerUri() + "\"" : null) + ",\n" +
" \"configurationMetadata\": {\n" +
" " + configurationMetadata + "\n" +
" }\n" +

8
oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/oidc/authentication/OidcIdTokenValidatorTests.java

@ -98,9 +98,7 @@ public class OidcIdTokenValidatorTests { @@ -98,9 +98,7 @@ public class OidcIdTokenValidatorTests {
* When the issuer is set in the provider metadata, and it does not match the issuer in the ID Token,
* the validation must fail
*/
Map<String, Object> configurationMetadata = new HashMap<>();
configurationMetadata.put("issuer", "https://issuer.somethingelse.com");
this.registration = this.registration.providerConfigurationMetadata(configurationMetadata);
this.registration = this.registration.issuerUri("https://issuer.somethingelse.com");
assertThat(this.validateIdToken())
.hasSize(1)
@ -114,9 +112,7 @@ public class OidcIdTokenValidatorTests { @@ -114,9 +112,7 @@ public class OidcIdTokenValidatorTests {
* When the issuer is set in the provider metadata, and it does match the issuer in the ID Token,
* the validation must succeed
*/
Map<String, Object> configurationMetadata = new HashMap<>();
configurationMetadata.put("issuer", "https://issuer.example.com");
this.registration = this.registration.providerConfigurationMetadata(configurationMetadata);
this.registration = this.registration.issuerUri("https://issuer.example.com");
assertThat(this.validateIdToken()).isEmpty();
}

1
oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/registration/ClientRegistrationsTest.java

@ -162,6 +162,7 @@ public class ClientRegistrationsTest { @@ -162,6 +162,7 @@ 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.getIssuerUri()).isEqualTo(this.issuer);
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",

Loading…
Cancel
Save