Skip to content

Commit f15e84b

Browse files
committed
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
1 parent 25fb1f4 commit f15e84b

File tree

2 files changed

+36
-1
lines changed

2 files changed

+36
-1
lines changed

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

+7-1
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
import java.util.HashMap;
3434
import java.util.List;
3535
import java.util.Map;
36+
import java.util.Objects;
3637

3738
/**
3839
* An {@link OAuth2TokenValidator} responsible for
@@ -68,7 +69,12 @@ public OAuth2TokenValidatorResult validate(Jwt idToken) {
6869

6970
// 2. The Issuer Identifier for the OpenID Provider (which is typically obtained during Discovery)
7071
// MUST exactly match the value of the iss (issuer) Claim.
71-
// TODO Depends on gh-4413
72+
String metadataIssuer = (String) this.clientRegistration.getProviderDetails().getConfigurationMetadata()
73+
.get("issuer");
74+
75+
if (metadataIssuer != null && !Objects.equals(metadataIssuer, idToken.getIssuer().toExternalForm())) {
76+
invalidClaims.put(IdTokenClaimNames.ISS, idToken.getIssuer());
77+
}
7278

7379
// 3. The Client MUST validate that the aud (audience) Claim contains its client_id value
7480
// registered at the Issuer identified by the iss (issuer) Claim as an audience.

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

+29
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,35 @@ public void validateWhenIssuerNullThenHasErrors() {
9292
.allMatch(msg -> msg.contains(IdTokenClaimNames.ISS));
9393
}
9494

95+
@Test
96+
public void validateWhenMetadataIssuerMismatchThenHasErrors() {
97+
/*
98+
* When the issuer is set in the provider metadata, and it does not match the issuer in the ID Token,
99+
* the validation must fail
100+
*/
101+
Map<String, Object> configurationMetadata = new HashMap<>();
102+
configurationMetadata.put("issuer", "https://issuer.somethingelse.com");
103+
this.registration = this.registration.providerConfigurationMetadata(configurationMetadata);
104+
105+
assertThat(this.validateIdToken())
106+
.hasSize(1)
107+
.extracting(OAuth2Error::getDescription)
108+
.allMatch(msg -> msg.contains(IdTokenClaimNames.ISS));
109+
}
110+
111+
@Test
112+
public void validateWhenMetadataIssuerMatchThenNoErrors() {
113+
/*
114+
* When the issuer is set in the provider metadata, and it does match the issuer in the ID Token,
115+
* the validation must succeed
116+
*/
117+
Map<String, Object> configurationMetadata = new HashMap<>();
118+
configurationMetadata.put("issuer", "https://issuer.example.com");
119+
this.registration = this.registration.providerConfigurationMetadata(configurationMetadata);
120+
121+
assertThat(this.validateIdToken()).isEmpty();
122+
}
123+
95124
@Test
96125
public void validateWhenSubNullThenHasErrors() {
97126
this.claims.remove(IdTokenClaimNames.SUB);

0 commit comments

Comments
 (0)