|
70 | 70 | import org.springframework.security.oauth2.core.user.OAuth2User;
|
71 | 71 | import org.springframework.security.oauth2.core.user.TestOAuth2Users;
|
72 | 72 | import org.springframework.security.oauth2.jwt.Jwt;
|
| 73 | +import org.springframework.security.oauth2.jwt.JwtValidationException; |
73 | 74 | import org.springframework.security.oauth2.jwt.ReactiveJwtDecoder;
|
74 | 75 | import org.springframework.security.oauth2.jwt.ReactiveJwtDecoderFactory;
|
75 | 76 | import org.springframework.security.test.web.reactive.server.WebTestClientBuilder;
|
@@ -518,6 +519,85 @@ public void oauth2LoginWhenCustomBeansThenUsed() {
|
518 | 519 | verify(securityContextRepository).save(any(), any());
|
519 | 520 | }
|
520 | 521 |
|
| 522 | + // gh-5562 |
| 523 | + @Test |
| 524 | + public void oauth2LoginWhenAccessTokenRequestFailsThenDefaultRedirectToLogin() { |
| 525 | + this.spring.register(OAuth2LoginWithMultipleClientRegistrations.class, |
| 526 | + OAuth2LoginWithCustomBeansConfig.class).autowire(); |
| 527 | + |
| 528 | + WebTestClient webTestClient = WebTestClientBuilder |
| 529 | + .bindToWebFilters(this.springSecurity) |
| 530 | + .build(); |
| 531 | + |
| 532 | + OAuth2AuthorizationRequest request = TestOAuth2AuthorizationRequests.request().scope("openid").build(); |
| 533 | + OAuth2AuthorizationResponse response = TestOAuth2AuthorizationResponses.success().build(); |
| 534 | + OAuth2AuthorizationExchange exchange = new OAuth2AuthorizationExchange(request, response); |
| 535 | + OAuth2AccessToken accessToken = TestOAuth2AccessTokens.scopes("openid"); |
| 536 | + OAuth2AuthorizationCodeAuthenticationToken authenticationToken = new OAuth2AuthorizationCodeAuthenticationToken(google, exchange, accessToken); |
| 537 | + |
| 538 | + OAuth2LoginWithCustomBeansConfig config = this.spring.getContext().getBean(OAuth2LoginWithCustomBeansConfig.class); |
| 539 | + |
| 540 | + ServerAuthenticationConverter converter = config.authenticationConverter; |
| 541 | + when(converter.convert(any())).thenReturn(Mono.just(authenticationToken)); |
| 542 | + |
| 543 | + ReactiveOAuth2AccessTokenResponseClient<OAuth2AuthorizationCodeGrantRequest> tokenResponseClient = config.tokenResponseClient; |
| 544 | + OAuth2Error oauth2Error = new OAuth2Error("invalid_request", "Invalid request", null); |
| 545 | + when(tokenResponseClient.getTokenResponse(any())).thenThrow(new OAuth2AuthenticationException(oauth2Error)); |
| 546 | + |
| 547 | + webTestClient.get() |
| 548 | + .uri("/login/oauth2/code/google") |
| 549 | + .exchange() |
| 550 | + .expectStatus() |
| 551 | + .is3xxRedirection() |
| 552 | + .expectHeader() |
| 553 | + .valueEquals("Location", "/login?error"); |
| 554 | + } |
| 555 | + |
| 556 | + // gh-6484 |
| 557 | + @Test |
| 558 | + public void oauth2LoginWhenIdTokenValidationFailsThenDefaultRedirectToLogin() { |
| 559 | + this.spring.register(OAuth2LoginWithMultipleClientRegistrations.class, |
| 560 | + OAuth2LoginWithCustomBeansConfig.class).autowire(); |
| 561 | + |
| 562 | + WebTestClient webTestClient = WebTestClientBuilder |
| 563 | + .bindToWebFilters(this.springSecurity) |
| 564 | + .build(); |
| 565 | + |
| 566 | + OAuth2LoginWithCustomBeansConfig config = this.spring.getContext().getBean(OAuth2LoginWithCustomBeansConfig.class); |
| 567 | + |
| 568 | + OAuth2AuthorizationRequest request = TestOAuth2AuthorizationRequests.request().scope("openid").build(); |
| 569 | + OAuth2AuthorizationResponse response = TestOAuth2AuthorizationResponses.success().build(); |
| 570 | + OAuth2AuthorizationExchange exchange = new OAuth2AuthorizationExchange(request, response); |
| 571 | + OAuth2AccessToken accessToken = TestOAuth2AccessTokens.scopes("openid"); |
| 572 | + OAuth2AuthorizationCodeAuthenticationToken authenticationToken = new OAuth2AuthorizationCodeAuthenticationToken(google, exchange, accessToken); |
| 573 | + |
| 574 | + ServerAuthenticationConverter converter = config.authenticationConverter; |
| 575 | + when(converter.convert(any())).thenReturn(Mono.just(authenticationToken)); |
| 576 | + |
| 577 | + Map<String, Object> additionalParameters = new HashMap<>(); |
| 578 | + additionalParameters.put(OidcParameterNames.ID_TOKEN, "id-token"); |
| 579 | + OAuth2AccessTokenResponse accessTokenResponse = OAuth2AccessTokenResponse.withToken(accessToken.getTokenValue()) |
| 580 | + .tokenType(accessToken.getTokenType()) |
| 581 | + .scopes(accessToken.getScopes()) |
| 582 | + .additionalParameters(additionalParameters) |
| 583 | + .build(); |
| 584 | + ReactiveOAuth2AccessTokenResponseClient<OAuth2AuthorizationCodeGrantRequest> tokenResponseClient = config.tokenResponseClient; |
| 585 | + when(tokenResponseClient.getTokenResponse(any())).thenReturn(Mono.just(accessTokenResponse)); |
| 586 | + |
| 587 | + ReactiveJwtDecoderFactory<ClientRegistration> jwtDecoderFactory = config.jwtDecoderFactory; |
| 588 | + OAuth2Error oauth2Error = new OAuth2Error("invalid_id_token", "Invalid ID Token", null); |
| 589 | + when(jwtDecoderFactory.createDecoder(any())).thenReturn(token -> |
| 590 | + Mono.error(new JwtValidationException("ID Token validation failed", Collections.singleton(oauth2Error)))); |
| 591 | + |
| 592 | + webTestClient.get() |
| 593 | + .uri("/login/oauth2/code/google") |
| 594 | + .exchange() |
| 595 | + .expectStatus() |
| 596 | + .is3xxRedirection() |
| 597 | + .expectHeader() |
| 598 | + .valueEquals("Location", "/login?error"); |
| 599 | + } |
| 600 | + |
521 | 601 | @Configuration
|
522 | 602 | static class OAuth2LoginWithCustomBeansConfig {
|
523 | 603 |
|
|
0 commit comments