diff --git a/config/src/main/java/org/springframework/security/config/oauth2/client/CommonOAuth2Provider.java b/config/src/main/java/org/springframework/security/config/oauth2/client/CommonOAuth2Provider.java index 07950784125..94de0257c20 100644 --- a/config/src/main/java/org/springframework/security/config/oauth2/client/CommonOAuth2Provider.java +++ b/config/src/main/java/org/springframework/security/config/oauth2/client/CommonOAuth2Provider.java @@ -42,6 +42,7 @@ public Builder getBuilder(String registrationId) { 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; diff --git a/config/src/test/java/org/springframework/security/config/oauth2/client/ClientRegistrationsBeanDefinitionParserTests.java b/config/src/test/java/org/springframework/security/config/oauth2/client/ClientRegistrationsBeanDefinitionParserTests.java index 606af24ae41..0a2eac541fa 100644 --- a/config/src/test/java/org/springframework/security/config/oauth2/client/ClientRegistrationsBeanDefinitionParserTests.java +++ b/config/src/test/java/org/springframework/security/config/oauth2/client/ClientRegistrationsBeanDefinitionParserTests.java @@ -165,6 +165,7 @@ public void parseWhenIssuerUriConfiguredThenRequestConfigFromIssuer() throws Exc .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 void parseWhenMultipleClientsConfiguredThenAvailableInRepository() { .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(); diff --git a/docs/manual/src/docs/asciidoc/_includes/servlet/oauth2/oauth2-login.adoc b/docs/manual/src/docs/asciidoc/_includes/servlet/oauth2/oauth2-login.adoc index 31c19185dd3..66648646084 100644 --- a/docs/manual/src/docs/asciidoc/_includes/servlet/oauth2/oauth2-login.adoc +++ b/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 |`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] diff --git a/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/oidc/authentication/OidcIdTokenValidator.java b/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/oidc/authentication/OidcIdTokenValidator.java index f51b7557ac7..03ae739a7b8 100644 --- a/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/oidc/authentication/OidcIdTokenValidator.java +++ b/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/oidc/authentication/OidcIdTokenValidator.java @@ -69,8 +69,7 @@ public OAuth2TokenValidatorResult validate(Jwt idToken) { // 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()); diff --git a/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/registration/ClientRegistration.java b/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/registration/ClientRegistration.java index e2185714562..1b165609071 100644 --- a/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/registration/ClientRegistration.java +++ b/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/registration/ClientRegistration.java @@ -163,6 +163,7 @@ public class ProviderDetails implements Serializable { private String tokenUri; private UserInfoEndpoint userInfoEndpoint = new UserInfoEndpoint(); private String jwkSetUri; + private String issuerUri; private Map configurationMetadata = Collections.emptyMap(); private ProviderDetails() { @@ -204,6 +205,16 @@ public String getJwkSetUri() { 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 static class Builder implements Serializable { private AuthenticationMethod userInfoAuthenticationMethod = AuthenticationMethod.HEADER; private String userNameAttributeName; private String jwkSetUri; + private String issuerUri; private Map configurationMetadata = Collections.emptyMap(); private String clientName; @@ -317,6 +329,7 @@ private Builder(ClientRegistration clientRegistration) { this.userInfoAuthenticationMethod = clientRegistration.providerDetails.userInfoEndpoint.authenticationMethod; this.userNameAttributeName = clientRegistration.providerDetails.userInfoEndpoint.userNameAttributeName; this.jwkSetUri = clientRegistration.providerDetails.jwkSetUri; + this.issuerUri = clientRegistration.providerDetails.issuerUri; Map configurationMetadata = clientRegistration.providerDetails.configurationMetadata; if (configurationMetadata != EMPTY_MAP) { this.configurationMetadata = new HashMap<>(configurationMetadata); @@ -486,6 +499,17 @@ public Builder jwkSetUri(String jwkSetUri) { 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 @@ private ClientRegistration create() { 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; diff --git a/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/registration/ClientRegistrations.java b/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/registration/ClientRegistrations.java index c6cbaebf550..7c807679c0b 100644 --- a/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/registration/ClientRegistrations.java +++ b/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/registration/ClientRegistrations.java @@ -248,6 +248,7 @@ private static ClientRegistration.Builder withProviderConfiguration(Authorizatio .authorizationUri(metadata.getAuthorizationEndpointURI().toASCIIString()) .providerConfigurationMetadata(configurationMetadata) .tokenUri(metadata.getTokenEndpointURI().toASCIIString()) + .issuerUri(issuer) .clientName(issuer); } diff --git a/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/jackson2/OAuth2AuthorizedClientMixinTests.java b/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/jackson2/OAuth2AuthorizedClientMixinTests.java index 93dfd2dd083..5f32a1797f6 100644 --- a/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/jackson2/OAuth2AuthorizedClientMixinTests.java +++ b/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/jackson2/OAuth2AuthorizedClientMixinTests.java @@ -139,6 +139,8 @@ public void deserializeWhenMixinRegisteredThenDeserializes() throws Exception { .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 void deserializeWhenRequiredAttributesOnlyThenDeserializes() throws Excep .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 @@ private static String asJson(ClientRegistration clientRegistration) { " \"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" + diff --git a/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/oidc/authentication/OidcIdTokenValidatorTests.java b/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/oidc/authentication/OidcIdTokenValidatorTests.java index b2cc0e77b6d..690bcecdeae 100644 --- a/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/oidc/authentication/OidcIdTokenValidatorTests.java +++ b/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/oidc/authentication/OidcIdTokenValidatorTests.java @@ -98,9 +98,7 @@ public void validateWhenMetadataIssuerMismatchThenHasErrors() { * 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 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 void validateWhenMetadataIssuerMatchThenNoErrors() { * When the issuer is set in the provider metadata, and it does match the issuer in the ID Token, * the validation must succeed */ - Map 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(); } diff --git a/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/registration/ClientRegistrationsTest.java b/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/registration/ClientRegistrationsTest.java index f0bc7737737..9e2067f678a 100644 --- a/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/registration/ClientRegistrationsTest.java +++ b/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/registration/ClientRegistrationsTest.java @@ -162,6 +162,7 @@ private void assertIssuerMetadata(ClientRegistration registration, 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",