From 32ce94d2ddb4fb99c1480f7264cd17e4c4042cf9 Mon Sep 17 00:00:00 2001 From: Daniel Furtlehner Date: Wed, 8 Apr 2020 16:16:22 +0200 Subject: [PATCH] Validate ID Token Issuer When the issuer is set in the provider metadata, we validate the iss field of the ID Token against it. The OpenID Connect Specification says this must always be validated. But this would be a breaking change for applications configured other than with ClientRegistrations.fromOidcIssuerLocation(issuer). This will be done later with #8326 Fixes gh-8321 --- .../authentication/OidcIdTokenValidator.java | 8 ++++- .../OidcIdTokenValidatorTests.java | 29 +++++++++++++++++++ 2 files changed, 36 insertions(+), 1 deletion(-) 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);