Skip to content

Commit 68b820e

Browse files
committed
Check Issuer with Issuer Provided
Signed-off-by: Josh Cummings <3627351+jzheaux@users.noreply.github.com>
1 parent 44d3281 commit 68b820e

4 files changed

Lines changed: 62 additions & 6 deletions

File tree

oauth2/oauth2-jose/src/main/java/org/springframework/security/oauth2/jwt/NimbusJwtDecoder.java

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -230,7 +230,8 @@ public static JwkSetUriJwtDecoderBuilder withIssuerLocation(String issuer) {
230230
.getConfigurationForIssuerLocation(issuer, rest);
231231
JwtDecoderProviderConfigurationUtils.validateIssuer(configuration, issuer);
232232
return configuration.get("jwks_uri").toString();
233-
}, JwtDecoderProviderConfigurationUtils::getJWSAlgorithms);
233+
}, JwtDecoderProviderConfigurationUtils::getJWSAlgorithms)
234+
.validator(JwtValidators.createDefaultWithIssuer(issuer));
234235
}
235236

236237
/**
@@ -289,6 +290,8 @@ public static final class JwkSetUriJwtDecoderBuilder {
289290

290291
private Consumer<ConfigurableJWTProcessor<SecurityContext>> jwtProcessorCustomizer;
291292

293+
private OAuth2TokenValidator<Jwt> validator = JwtValidators.createDefault();
294+
292295
private JwkSetUriJwtDecoderBuilder(String jwkSetUri) {
293296
Assert.hasText(jwkSetUri, "jwkSetUri cannot be empty");
294297
this.jwkSetUri = (rest) -> jwkSetUri;
@@ -423,6 +426,12 @@ public JwkSetUriJwtDecoderBuilder jwtProcessorCustomizer(
423426
return this;
424427
}
425428

429+
JwkSetUriJwtDecoderBuilder validator(OAuth2TokenValidator<Jwt> validator) {
430+
Assert.notNull(validator, "validator cannot be null");
431+
this.validator = validator;
432+
return this;
433+
}
434+
426435
JWSKeySelector<SecurityContext> jwsKeySelector(JWKSource<SecurityContext> jwkSource) {
427436
if (this.signatureAlgorithms.isEmpty()) {
428437
return new JWSVerificationKeySelector<>(this.defaultAlgorithms.apply(jwkSource), jwkSource);
@@ -461,7 +470,9 @@ JWTProcessor<SecurityContext> processor() {
461470
* @return the configured {@link NimbusJwtDecoder}
462471
*/
463472
public NimbusJwtDecoder build() {
464-
return new NimbusJwtDecoder(processor());
473+
NimbusJwtDecoder decoder = new NimbusJwtDecoder(processor());
474+
decoder.setJwtValidator(this.validator);
475+
return decoder;
465476
}
466477

467478
private static final class SpringJWKSource<C extends SecurityContext> implements JWKSetSource<C> {

oauth2/oauth2-jose/src/main/java/org/springframework/security/oauth2/jwt/NimbusReactiveJwtDecoder.java

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -241,7 +241,8 @@ public static JwkSetUriReactiveJwtDecoderBuilder withIssuerLocation(String issue
241241
}
242242
return Mono.just(configuration.get("jwks_uri").toString());
243243
}),
244-
ReactiveJwtDecoderProviderConfigurationUtils::getJWSAlgorithms);
244+
ReactiveJwtDecoderProviderConfigurationUtils::getJWSAlgorithms)
245+
.validator(JwtValidators.createDefaultWithIssuer(issuer));
245246
}
246247

247248
/**
@@ -332,6 +333,8 @@ public static final class JwkSetUriReactiveJwtDecoderBuilder {
332333

333334
private BiFunction<ReactiveRemoteJWKSource, ConfigurableJWTProcessor<JWKSecurityContext>, Mono<ConfigurableJWTProcessor<JWKSecurityContext>>> jwtProcessorCustomizer;
334335

336+
private OAuth2TokenValidator<Jwt> validator = JwtValidators.createDefault();
337+
335338
private JwkSetUriReactiveJwtDecoderBuilder(String jwkSetUri) {
336339
Assert.hasText(jwkSetUri, "jwkSetUri cannot be empty");
337340
this.jwkSetUri = (web) -> Mono.just(jwkSetUri);
@@ -456,6 +459,11 @@ public JwkSetUriReactiveJwtDecoderBuilder jwtProcessorCustomizer(
456459
return this;
457460
}
458461

462+
JwkSetUriReactiveJwtDecoderBuilder validator(OAuth2TokenValidator<Jwt> validator) {
463+
this.validator = validator;
464+
return this;
465+
}
466+
459467
JwkSetUriReactiveJwtDecoderBuilder jwtProcessorCustomizer(
460468
BiFunction<ReactiveRemoteJWKSource, ConfigurableJWTProcessor<JWKSecurityContext>, Mono<ConfigurableJWTProcessor<JWKSecurityContext>>> jwtProcessorCustomizer) {
461469
Assert.notNull(jwtProcessorCustomizer, "jwtProcessorCustomizer cannot be null");
@@ -468,7 +476,9 @@ JwkSetUriReactiveJwtDecoderBuilder jwtProcessorCustomizer(
468476
* @return the configured {@link NimbusReactiveJwtDecoder}
469477
*/
470478
public NimbusReactiveJwtDecoder build() {
471-
return new NimbusReactiveJwtDecoder(processor());
479+
NimbusReactiveJwtDecoder decoder = new NimbusReactiveJwtDecoder(processor());
480+
decoder.setJwtValidator(this.validator);
481+
return decoder;
472482
}
473483

474484
Mono<JWSKeySelector<JWKSecurityContext>> jwsKeySelector(ReactiveRemoteJWKSource source) {

oauth2/oauth2-jose/src/test/java/org/springframework/security/oauth2/jwt/NimbusJwtDecoderTests.java

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -328,11 +328,26 @@ public void decodeWhenIssuerLocationThenOk() {
328328
.willReturn(new ResponseEntity<>(Map.of("issuer", issuer, "jwks_uri", issuer + "/jwks"), HttpStatus.OK));
329329
given(restOperations.exchange(any(RequestEntity.class), eq(String.class)))
330330
.willReturn(new ResponseEntity<>(JWK_SET, HttpStatus.OK));
331-
JwtDecoder jwtDecoder = NimbusJwtDecoder.withIssuerLocation(issuer).restOperations(restOperations).build();
331+
NimbusJwtDecoder jwtDecoder = NimbusJwtDecoder.withIssuerLocation(issuer)
332+
.restOperations(restOperations)
333+
.build();
334+
jwtDecoder.setJwtValidator(JwtValidators.createDefault());
332335
Jwt jwt = jwtDecoder.decode(SIGNED_JWT);
333336
assertThat(jwt.hasClaim(JwtClaimNames.EXP)).isNotNull();
334337
}
335338

339+
@Test
340+
public void decodeWhenIssuerLocationThenRejectsMismatchingIssuers() {
341+
String issuer = "https://example.org/wrong-issuer";
342+
RestOperations restOperations = mock(RestOperations.class);
343+
given(restOperations.exchange(any(RequestEntity.class), any(ParameterizedTypeReference.class)))
344+
.willReturn(new ResponseEntity<>(Map.of("issuer", issuer, "jwks_uri", issuer + "/jwks"), HttpStatus.OK));
345+
given(restOperations.exchange(any(RequestEntity.class), eq(String.class)))
346+
.willReturn(new ResponseEntity<>(JWK_SET, HttpStatus.OK));
347+
JwtDecoder jwtDecoder = NimbusJwtDecoder.withIssuerLocation(issuer).restOperations(restOperations).build();
348+
assertThatExceptionOfType(JwtValidationException.class).isThrownBy(() -> jwtDecoder.decode(SIGNED_JWT));
349+
}
350+
336351
@Test
337352
public void withJwkSetUriWhenNullOrEmptyThenThrowsException() {
338353
// @formatter:off

oauth2/oauth2-jose/src/test/java/org/springframework/security/oauth2/jwt/NimbusReactiveJwtDecoderTests.java

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -617,13 +617,33 @@ public void decodeWhenIssuerLocationThenOk() {
617617
given(responseSpec.bodyToMono(any(ParameterizedTypeReference.class)))
618618
.willReturn(Mono.just(Map.of("issuer", issuer, "jwks_uri", issuer + "/jwks")));
619619
given(spec.retrieve()).willReturn(responseSpec);
620-
ReactiveJwtDecoder jwtDecoder = NimbusReactiveJwtDecoder.withIssuerLocation(issuer)
620+
NimbusReactiveJwtDecoder jwtDecoder = NimbusReactiveJwtDecoder.withIssuerLocation(issuer)
621621
.webClient(webClient)
622622
.build();
623+
jwtDecoder.setJwtValidator(JwtValidators.createDefault());
623624
Jwt jwt = jwtDecoder.decode(this.messageReadToken).block();
624625
assertThat(jwt.hasClaim(JwtClaimNames.EXP)).isNotNull();
625626
}
626627

628+
@Test
629+
public void decodeWhenIssuerLocationThenRejectsMismatchingIssuers() {
630+
String issuer = "https://example.org/wrong-issuer";
631+
WebClient real = WebClient.builder().build();
632+
WebClient.RequestHeadersUriSpec spec = spy(real.get());
633+
WebClient webClient = spy(WebClient.class);
634+
given(webClient.get()).willReturn(spec);
635+
WebClient.ResponseSpec responseSpec = mock(WebClient.ResponseSpec.class);
636+
given(responseSpec.bodyToMono(String.class)).willReturn(Mono.just(this.jwkSet));
637+
given(responseSpec.bodyToMono(any(ParameterizedTypeReference.class)))
638+
.willReturn(Mono.just(Map.of("issuer", issuer, "jwks_uri", issuer + "/jwks")));
639+
given(spec.retrieve()).willReturn(responseSpec);
640+
ReactiveJwtDecoder jwtDecoder = NimbusReactiveJwtDecoder.withIssuerLocation(issuer)
641+
.webClient(webClient)
642+
.build();
643+
assertThatExceptionOfType(JwtValidationException.class)
644+
.isThrownBy(() -> jwtDecoder.decode(this.messageReadToken).block());
645+
}
646+
627647
@Test
628648
public void jwsKeySelectorWhenNoAlgorithmThenReturnsRS256Selector() {
629649
ReactiveRemoteJWKSource jwkSource = mock(ReactiveRemoteJWKSource.class);

0 commit comments

Comments
 (0)