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 7ceaee3a70..f51b7557ac 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 @@ -33,6 +33,7 @@ import java.time.Instant; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Objects; /** * An {@link OAuth2TokenValidator} responsible for @@ -68,7 +69,12 @@ public final class OidcIdTokenValidator implements OAuth2TokenValidator { // 2. The Issuer Identifier for the OpenID Provider (which is typically obtained during Discovery) // MUST exactly match the value of the iss (issuer) Claim. - // TODO Depends on gh-4413 + String metadataIssuer = (String) this.clientRegistration.getProviderDetails().getConfigurationMetadata() + .get("issuer"); + + if (metadataIssuer != null && !Objects.equals(metadataIssuer, idToken.getIssuer().toExternalForm())) { + invalidClaims.put(IdTokenClaimNames.ISS, idToken.getIssuer()); + } // 3. The Client MUST validate that the aud (audience) Claim contains its client_id value // registered at the Issuer identified by the iss (issuer) Claim as an audience. 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 f357cc38c8..b2cc0e77b6 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 @@ -92,6 +92,35 @@ public class OidcIdTokenValidatorTests { .allMatch(msg -> msg.contains(IdTokenClaimNames.ISS)); } + @Test + 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); + + assertThat(this.validateIdToken()) + .hasSize(1) + .extracting(OAuth2Error::getDescription) + .allMatch(msg -> msg.contains(IdTokenClaimNames.ISS)); + } + + @Test + 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); + + assertThat(this.validateIdToken()).isEmpty(); + } + @Test public void validateWhenSubNullThenHasErrors() { this.claims.remove(IdTokenClaimNames.SUB);