Skip to content

Commit 9ad7981

Browse files
committed
Merge branch '6.5.x' into 7.0.x
2 parents 53bcf0d + 68b820e commit 9ad7981

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
@@ -232,7 +232,8 @@ public static JwkSetUriJwtDecoderBuilder withIssuerLocation(String issuer) {
232232
.getConfigurationForIssuerLocation(issuer, rest);
233233
JwtDecoderProviderConfigurationUtils.validateIssuer(configuration, issuer);
234234
return configuration.get("jwks_uri").toString();
235-
}, JwtDecoderProviderConfigurationUtils::getJWSAlgorithms);
235+
}, JwtDecoderProviderConfigurationUtils::getJWSAlgorithms)
236+
.validator(JwtValidators.createDefaultWithIssuer(issuer));
236237
}
237238

238239
/**
@@ -301,6 +302,8 @@ public static final class JwkSetUriJwtDecoderBuilder {
301302

302303
private Consumer<ConfigurableJWTProcessor<SecurityContext>> jwtProcessorCustomizer;
303304

305+
private OAuth2TokenValidator<Jwt> validator = JwtValidators.createDefault();
306+
304307
private JwkSetUriJwtDecoderBuilder(String jwkSetUri) {
305308
Assert.hasText(jwkSetUri, "jwkSetUri cannot be empty");
306309
this.jwkSetUri = (rest) -> jwkSetUri;
@@ -441,6 +444,12 @@ public JwkSetUriJwtDecoderBuilder jwtProcessorCustomizer(
441444
return this;
442445
}
443446

447+
JwkSetUriJwtDecoderBuilder validator(OAuth2TokenValidator<Jwt> validator) {
448+
Assert.notNull(validator, "validator cannot be null");
449+
this.validator = validator;
450+
return this;
451+
}
452+
444453
JWSKeySelector<SecurityContext> jwsKeySelector(JWKSource<SecurityContext> jwkSource) {
445454
if (this.signatureAlgorithms.isEmpty()) {
446455
return new JWSVerificationKeySelector<>(this.defaultAlgorithms.apply(jwkSource), jwkSource);
@@ -479,7 +488,9 @@ JWTProcessor<SecurityContext> processor() {
479488
* @return the configured {@link NimbusJwtDecoder}
480489
*/
481490
public NimbusJwtDecoder build() {
482-
return new NimbusJwtDecoder(processor());
491+
NimbusJwtDecoder decoder = new NimbusJwtDecoder(processor());
492+
decoder.setJwtValidator(this.validator);
493+
return decoder;
483494
}
484495

485496
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
@@ -332,7 +332,10 @@ public void decodeWhenIssuerLocationThenOk() {
332332
.willReturn(new ResponseEntity<>(Map.of("issuer", issuer, "jwks_uri", issuer + "/jwks"), HttpStatus.OK));
333333
given(restOperations.exchange(any(RequestEntity.class), eq(String.class)))
334334
.willReturn(new ResponseEntity<>(JWK_SET, HttpStatus.OK));
335-
JwtDecoder jwtDecoder = NimbusJwtDecoder.withIssuerLocation(issuer).restOperations(restOperations).build();
335+
NimbusJwtDecoder jwtDecoder = NimbusJwtDecoder.withIssuerLocation(issuer)
336+
.restOperations(restOperations)
337+
.build();
338+
jwtDecoder.setJwtValidator(JwtValidators.createDefault());
336339
Jwt jwt = jwtDecoder.decode(SIGNED_JWT);
337340
assertThat(jwt.hasClaim(JwtClaimNames.EXP)).isNotNull();
338341
}
@@ -350,6 +353,18 @@ public void decodeWhenDiscoverJwsAlgorithmsThenOk() {
350353
assertThat(jwt.hasClaim(JwtClaimNames.EXP)).isNotNull();
351354
}
352355

356+
@Test
357+
public void decodeWhenIssuerLocationThenRejectsMismatchingIssuers() {
358+
String issuer = "https://example.org/wrong-issuer";
359+
RestOperations restOperations = mock(RestOperations.class);
360+
given(restOperations.exchange(any(RequestEntity.class), any(ParameterizedTypeReference.class)))
361+
.willReturn(new ResponseEntity<>(Map.of("issuer", issuer, "jwks_uri", issuer + "/jwks"), HttpStatus.OK));
362+
given(restOperations.exchange(any(RequestEntity.class), eq(String.class)))
363+
.willReturn(new ResponseEntity<>(JWK_SET, HttpStatus.OK));
364+
JwtDecoder jwtDecoder = NimbusJwtDecoder.withIssuerLocation(issuer).restOperations(restOperations).build();
365+
assertThatExceptionOfType(JwtValidationException.class).isThrownBy(() -> jwtDecoder.decode(SIGNED_JWT));
366+
}
367+
353368
@Test
354369
public void withJwkSetUriWhenNullOrEmptyThenThrowsException() {
355370
// @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)