|
52 | 52 | import org.springframework.security.oauth2.client.OAuth2AuthorizedClientManager;
|
53 | 53 | import org.springframework.security.oauth2.client.PasswordOAuth2AuthorizedClientProvider;
|
54 | 54 | import org.springframework.security.oauth2.client.RefreshTokenOAuth2AuthorizedClientProvider;
|
| 55 | +import org.springframework.security.oauth2.client.TokenExchangeOAuth2AuthorizedClientProvider; |
55 | 56 | import org.springframework.security.oauth2.client.endpoint.AbstractOAuth2AuthorizationGrantRequest;
|
56 | 57 | import org.springframework.security.oauth2.client.endpoint.JwtBearerGrantRequest;
|
57 | 58 | import org.springframework.security.oauth2.client.endpoint.OAuth2AccessTokenResponseClient;
|
58 | 59 | import org.springframework.security.oauth2.client.endpoint.OAuth2AuthorizationCodeGrantRequest;
|
59 | 60 | import org.springframework.security.oauth2.client.endpoint.OAuth2ClientCredentialsGrantRequest;
|
60 | 61 | import org.springframework.security.oauth2.client.endpoint.OAuth2PasswordGrantRequest;
|
61 | 62 | import org.springframework.security.oauth2.client.endpoint.OAuth2RefreshTokenGrantRequest;
|
| 63 | +import org.springframework.security.oauth2.client.endpoint.TokenExchangeGrantRequest; |
62 | 64 | import org.springframework.security.oauth2.client.oidc.userinfo.OidcUserRequest;
|
63 | 65 | import org.springframework.security.oauth2.client.oidc.userinfo.OidcUserService;
|
64 | 66 | import org.springframework.security.oauth2.client.registration.ClientRegistration;
|
|
70 | 72 | import org.springframework.security.oauth2.client.web.DefaultOAuth2AuthorizedClientManager;
|
71 | 73 | import org.springframework.security.oauth2.client.web.OAuth2AuthorizedClientRepository;
|
72 | 74 | import org.springframework.security.oauth2.core.AuthorizationGrantType;
|
| 75 | +import org.springframework.security.oauth2.core.ClientAuthenticationMethod; |
73 | 76 | import org.springframework.security.oauth2.core.OAuth2AccessToken;
|
74 | 77 | import org.springframework.security.oauth2.core.OAuth2AuthorizationException;
|
75 | 78 | import org.springframework.security.oauth2.core.OAuth2Error;
|
@@ -327,6 +330,47 @@ private void testJwtBearerGrant() {
|
327 | 330 | assertThat(grantRequest.getJwt().getSubject()).isEqualTo("user");
|
328 | 331 | }
|
329 | 332 |
|
| 333 | + @Test |
| 334 | + public void authorizeWhenTokenExchangeAccessTokenResponseClientBeanThenUsed() { |
| 335 | + this.spring.register(CustomAccessTokenResponseClientsConfig.class).autowire(); |
| 336 | + testTokenExchangeGrant(); |
| 337 | + } |
| 338 | + |
| 339 | + @Test |
| 340 | + public void authorizeWhenTokenExchangeAuthorizedClientProviderBeanThenUsed() { |
| 341 | + this.spring.register(CustomAuthorizedClientProvidersConfig.class).autowire(); |
| 342 | + testTokenExchangeGrant(); |
| 343 | + } |
| 344 | + |
| 345 | + private void testTokenExchangeGrant() { |
| 346 | + OAuth2AccessTokenResponse accessTokenResponse = TestOAuth2AccessTokenResponses.accessTokenResponse().build(); |
| 347 | + given(MOCK_RESPONSE_CLIENT.getTokenResponse(any(TokenExchangeGrantRequest.class))) |
| 348 | + .willReturn(accessTokenResponse); |
| 349 | + |
| 350 | + JwtAuthenticationToken authentication = new JwtAuthenticationToken(getJwt()); |
| 351 | + ClientRegistration clientRegistration = this.clientRegistrationRepository.findByRegistrationId("auth0"); |
| 352 | + // @formatter:off |
| 353 | + OAuth2AuthorizeRequest authorizeRequest = OAuth2AuthorizeRequest |
| 354 | + .withClientRegistrationId(clientRegistration.getRegistrationId()) |
| 355 | + .principal(authentication) |
| 356 | + .attribute(HttpServletRequest.class.getName(), this.request) |
| 357 | + .attribute(HttpServletResponse.class.getName(), this.response) |
| 358 | + .build(); |
| 359 | + // @formatter:on |
| 360 | + OAuth2AuthorizedClient authorizedClient = this.authorizedClientManager.authorize(authorizeRequest); |
| 361 | + assertThat(authorizedClient).isNotNull(); |
| 362 | + |
| 363 | + ArgumentCaptor<TokenExchangeGrantRequest> grantRequestCaptor = ArgumentCaptor |
| 364 | + .forClass(TokenExchangeGrantRequest.class); |
| 365 | + verify(MOCK_RESPONSE_CLIENT).getTokenResponse(grantRequestCaptor.capture()); |
| 366 | + |
| 367 | + TokenExchangeGrantRequest grantRequest = grantRequestCaptor.getValue(); |
| 368 | + assertThat(grantRequest.getClientRegistration().getRegistrationId()) |
| 369 | + .isEqualTo(clientRegistration.getRegistrationId()); |
| 370 | + assertThat(grantRequest.getGrantType()).isEqualTo(AuthorizationGrantType.TOKEN_EXCHANGE); |
| 371 | + assertThat(grantRequest.getSubjectToken()).isEqualTo(authentication.getToken()); |
| 372 | + } |
| 373 | + |
330 | 374 | private static OAuth2AccessToken getExpiredAccessToken() {
|
331 | 375 | Instant expiresAt = Instant.now().minusSeconds(60);
|
332 | 376 | Instant issuedAt = expiresAt.minus(Duration.ofDays(1));
|
@@ -376,6 +420,11 @@ OAuth2AccessTokenResponseClient<JwtBearerGrantRequest> jwtBearerTokenResponseCli
|
376 | 420 | return new MockJwtBearerClient();
|
377 | 421 | }
|
378 | 422 |
|
| 423 | + @Bean |
| 424 | + OAuth2AccessTokenResponseClient<TokenExchangeGrantRequest> tokenExchangeTokenResponseClient() { |
| 425 | + return new MockTokenExchangeClient(); |
| 426 | + } |
| 427 | + |
379 | 428 | @Bean
|
380 | 429 | OAuth2UserService<OAuth2UserRequest, OAuth2User> oauth2UserService() {
|
381 | 430 | return mock(DefaultOAuth2UserService.class);
|
@@ -425,6 +474,13 @@ JwtBearerOAuth2AuthorizedClientProvider jwtBearerAuthorizedClientProvider() {
|
425 | 474 | return authorizedClientProvider;
|
426 | 475 | }
|
427 | 476 |
|
| 477 | + @Bean |
| 478 | + TokenExchangeOAuth2AuthorizedClientProvider tokenExchangeAuthorizedClientProvider() { |
| 479 | + TokenExchangeOAuth2AuthorizedClientProvider authorizedClientProvider = new TokenExchangeOAuth2AuthorizedClientProvider(); |
| 480 | + authorizedClientProvider.setAccessTokenResponseClient(new MockTokenExchangeClient()); |
| 481 | + return authorizedClientProvider; |
| 482 | + } |
| 483 | + |
428 | 484 | }
|
429 | 485 |
|
430 | 486 | abstract static class OAuth2ClientBaseConfig {
|
@@ -463,6 +519,14 @@ ClientRegistrationRepository clientRegistrationRepository() {
|
463 | 519 | .clientId("okta-client-id")
|
464 | 520 | .clientSecret("okta-client-secret")
|
465 | 521 | .authorizationGrantType(AuthorizationGrantType.JWT_BEARER)
|
| 522 | + .build(), |
| 523 | + ClientRegistration.withRegistrationId("auth0") |
| 524 | + .clientName("Auth0") |
| 525 | + .clientId("auth0-client-id") |
| 526 | + .clientSecret("auth0-client-secret") |
| 527 | + .clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC) |
| 528 | + .authorizationGrantType(AuthorizationGrantType.TOKEN_EXCHANGE) |
| 529 | + .scope("user.read", "user.write") |
466 | 530 | .build()));
|
467 | 531 | // @formatter:on
|
468 | 532 | }
|
@@ -544,4 +608,13 @@ public OAuth2AccessTokenResponse getTokenResponse(JwtBearerGrantRequest authoriz
|
544 | 608 |
|
545 | 609 | }
|
546 | 610 |
|
| 611 | + private static class MockTokenExchangeClient implements OAuth2AccessTokenResponseClient<TokenExchangeGrantRequest> { |
| 612 | + |
| 613 | + @Override |
| 614 | + public OAuth2AccessTokenResponse getTokenResponse(TokenExchangeGrantRequest authorizationGrantRequest) { |
| 615 | + return MOCK_RESPONSE_CLIENT.getTokenResponse(authorizationGrantRequest); |
| 616 | + } |
| 617 | + |
| 618 | + } |
| 619 | + |
547 | 620 | }
|
0 commit comments