Support refresh token for Token Exchange

Closes gh-15534
This commit is contained in:
Steve Riesenberg 2024-09-27 15:57:57 -05:00
parent e11c188122
commit 9ba2435cb2
No known key found for this signature in database
GPG Key ID: 3D0169B18AB8F0A9
4 changed files with 42 additions and 12 deletions

View File

@ -90,7 +90,7 @@ public final class TokenExchangeOAuth2AuthorizedClientProvider implements OAuth2
OAuth2AccessTokenResponse tokenResponse = getTokenResponse(clientRegistration, grantRequest);
return new OAuth2AuthorizedClient(clientRegistration, context.getPrincipal().getName(),
tokenResponse.getAccessToken());
tokenResponse.getAccessToken(), tokenResponse.getRefreshToken());
}
private OAuth2Token resolveSubjectToken(OAuth2AuthorizationContext context) {

View File

@ -88,7 +88,7 @@ public final class TokenExchangeReactiveOAuth2AuthorizedClientProvider
.onErrorMap(OAuth2AuthorizationException.class,
(ex) -> new ClientAuthorizationException(ex.getError(), clientRegistration.getRegistrationId(), ex))
.map((tokenResponse) -> new OAuth2AuthorizedClient(clientRegistration, context.getPrincipal().getName(),
tokenResponse.getAccessToken()));
tokenResponse.getAccessToken(), tokenResponse.getRefreshToken()));
}
private Mono<OAuth2Token> resolveSubjectToken(OAuth2AuthorizationContext context) {

View File

@ -213,7 +213,9 @@ public class TokenExchangeOAuth2AuthorizedClientProviderTests {
issuedAt, expiresAt);
OAuth2AuthorizedClient authorizedClient = new OAuth2AuthorizedClient(this.clientRegistration,
this.principal.getName(), accessToken);
OAuth2AccessTokenResponse accessTokenResponse = TestOAuth2AccessTokenResponses.accessTokenResponse().build();
OAuth2AccessTokenResponse accessTokenResponse = TestOAuth2AccessTokenResponses.accessTokenResponse()
.refreshToken("refresh")
.build();
given(this.accessTokenResponseClient.getTokenResponse(any(TokenExchangeGrantRequest.class)))
.willReturn(accessTokenResponse);
// @formatter:off
@ -228,6 +230,7 @@ public class TokenExchangeOAuth2AuthorizedClientProviderTests {
assertThat(reauthorizedClient.getClientRegistration()).isSameAs(this.clientRegistration);
assertThat(reauthorizedClient.getPrincipalName()).isEqualTo(this.principal.getName());
assertThat(reauthorizedClient.getAccessToken()).isEqualTo(accessTokenResponse.getAccessToken());
assertThat(reauthorizedClient.getRefreshToken()).isEqualTo(accessTokenResponse.getRefreshToken());
ArgumentCaptor<TokenExchangeGrantRequest> grantRequestCaptor = ArgumentCaptor
.forClass(TokenExchangeGrantRequest.class);
verify(this.accessTokenResponseClient).getTokenResponse(grantRequestCaptor.capture());
@ -248,7 +251,9 @@ public class TokenExchangeOAuth2AuthorizedClientProviderTests {
// Shorten the lifespan of the access token by 90 seconds, which will ultimately
// force it to expire on the client
this.authorizedClientProvider.setClockSkew(Duration.ofSeconds(90));
OAuth2AccessTokenResponse accessTokenResponse = TestOAuth2AccessTokenResponses.accessTokenResponse().build();
OAuth2AccessTokenResponse accessTokenResponse = TestOAuth2AccessTokenResponses.accessTokenResponse()
.refreshToken("refresh")
.build();
given(this.accessTokenResponseClient.getTokenResponse(any(TokenExchangeGrantRequest.class)))
.willReturn(accessTokenResponse);
// @formatter:off
@ -263,6 +268,7 @@ public class TokenExchangeOAuth2AuthorizedClientProviderTests {
assertThat(reauthorizedClient.getClientRegistration()).isSameAs(this.clientRegistration);
assertThat(reauthorizedClient.getPrincipalName()).isEqualTo(this.principal.getName());
assertThat(reauthorizedClient.getAccessToken()).isEqualTo(accessTokenResponse.getAccessToken());
assertThat(reauthorizedClient.getRefreshToken()).isEqualTo(accessTokenResponse.getRefreshToken());
ArgumentCaptor<TokenExchangeGrantRequest> grantRequestCaptor = ArgumentCaptor
.forClass(TokenExchangeGrantRequest.class);
verify(this.accessTokenResponseClient).getTokenResponse(grantRequestCaptor.capture());
@ -285,7 +291,9 @@ public class TokenExchangeOAuth2AuthorizedClientProviderTests {
@Test
public void authorizeWhenTokenExchangeAndNotAuthorizedAndSubjectTokenResolvesThenAuthorized() {
OAuth2AccessTokenResponse accessTokenResponse = TestOAuth2AccessTokenResponses.accessTokenResponse().build();
OAuth2AccessTokenResponse accessTokenResponse = TestOAuth2AccessTokenResponses.accessTokenResponse()
.refreshToken("refresh")
.build();
given(this.accessTokenResponseClient.getTokenResponse(any(TokenExchangeGrantRequest.class)))
.willReturn(accessTokenResponse);
// @formatter:off
@ -299,6 +307,7 @@ public class TokenExchangeOAuth2AuthorizedClientProviderTests {
assertThat(authorizedClient.getClientRegistration()).isSameAs(this.clientRegistration);
assertThat(authorizedClient.getPrincipalName()).isEqualTo(this.principal.getName());
assertThat(authorizedClient.getAccessToken()).isEqualTo(accessTokenResponse.getAccessToken());
assertThat(authorizedClient.getRefreshToken()).isEqualTo(accessTokenResponse.getRefreshToken());
ArgumentCaptor<TokenExchangeGrantRequest> grantRequestCaptor = ArgumentCaptor
.forClass(TokenExchangeGrantRequest.class);
verify(this.accessTokenResponseClient).getTokenResponse(grantRequestCaptor.capture());
@ -312,7 +321,9 @@ public class TokenExchangeOAuth2AuthorizedClientProviderTests {
Function<OAuth2AuthorizationContext, OAuth2Token> subjectTokenResolver = mock(Function.class);
given(subjectTokenResolver.apply(any(OAuth2AuthorizationContext.class))).willReturn(this.subjectToken);
this.authorizedClientProvider.setSubjectTokenResolver(subjectTokenResolver);
OAuth2AccessTokenResponse accessTokenResponse = TestOAuth2AccessTokenResponses.accessTokenResponse().build();
OAuth2AccessTokenResponse accessTokenResponse = TestOAuth2AccessTokenResponses.accessTokenResponse()
.refreshToken("refresh")
.build();
given(this.accessTokenResponseClient.getTokenResponse(any(TokenExchangeGrantRequest.class)))
.willReturn(accessTokenResponse);
TestingAuthenticationToken principal = new TestingAuthenticationToken("user", "password");
@ -327,6 +338,7 @@ public class TokenExchangeOAuth2AuthorizedClientProviderTests {
assertThat(authorizedClient.getClientRegistration()).isSameAs(this.clientRegistration);
assertThat(authorizedClient.getPrincipalName()).isEqualTo(principal.getName());
assertThat(authorizedClient.getAccessToken()).isEqualTo(accessTokenResponse.getAccessToken());
assertThat(authorizedClient.getRefreshToken()).isEqualTo(accessTokenResponse.getRefreshToken());
verify(subjectTokenResolver).apply(authorizationContext);
ArgumentCaptor<TokenExchangeGrantRequest> grantRequestCaptor = ArgumentCaptor
.forClass(TokenExchangeGrantRequest.class);
@ -341,7 +353,9 @@ public class TokenExchangeOAuth2AuthorizedClientProviderTests {
Function<OAuth2AuthorizationContext, OAuth2Token> actorTokenResolver = mock(Function.class);
given(actorTokenResolver.apply(any(OAuth2AuthorizationContext.class))).willReturn(this.actorToken);
this.authorizedClientProvider.setActorTokenResolver(actorTokenResolver);
OAuth2AccessTokenResponse accessTokenResponse = TestOAuth2AccessTokenResponses.accessTokenResponse().build();
OAuth2AccessTokenResponse accessTokenResponse = TestOAuth2AccessTokenResponses.accessTokenResponse()
.refreshToken("refresh")
.build();
given(this.accessTokenResponseClient.getTokenResponse(any(TokenExchangeGrantRequest.class)))
.willReturn(accessTokenResponse);
// @formatter:off
@ -355,6 +369,7 @@ public class TokenExchangeOAuth2AuthorizedClientProviderTests {
assertThat(authorizedClient.getClientRegistration()).isSameAs(this.clientRegistration);
assertThat(authorizedClient.getPrincipalName()).isEqualTo(this.principal.getName());
assertThat(authorizedClient.getAccessToken()).isEqualTo(accessTokenResponse.getAccessToken());
assertThat(authorizedClient.getRefreshToken()).isEqualTo(accessTokenResponse.getRefreshToken());
verify(actorTokenResolver).apply(authorizationContext);
ArgumentCaptor<TokenExchangeGrantRequest> grantRequestCaptor = ArgumentCaptor
.forClass(TokenExchangeGrantRequest.class);

View File

@ -215,7 +215,9 @@ public class TokenExchangeReactiveOAuth2AuthorizedClientProviderTests {
issuedAt, expiresAt);
OAuth2AuthorizedClient authorizedClient = new OAuth2AuthorizedClient(this.clientRegistration,
this.principal.getName(), accessToken);
OAuth2AccessTokenResponse accessTokenResponse = TestOAuth2AccessTokenResponses.accessTokenResponse().build();
OAuth2AccessTokenResponse accessTokenResponse = TestOAuth2AccessTokenResponses.accessTokenResponse()
.refreshToken("refresh")
.build();
given(this.accessTokenResponseClient.getTokenResponse(any(TokenExchangeGrantRequest.class)))
.willReturn(Mono.just(accessTokenResponse));
// @formatter:off
@ -231,6 +233,7 @@ public class TokenExchangeReactiveOAuth2AuthorizedClientProviderTests {
assertThat(reauthorizedClient.getClientRegistration()).isSameAs(this.clientRegistration);
assertThat(reauthorizedClient.getPrincipalName()).isEqualTo(this.principal.getName());
assertThat(reauthorizedClient.getAccessToken()).isEqualTo(accessTokenResponse.getAccessToken());
assertThat(reauthorizedClient.getRefreshToken()).isEqualTo(accessTokenResponse.getRefreshToken());
ArgumentCaptor<TokenExchangeGrantRequest> grantRequestCaptor = ArgumentCaptor
.forClass(TokenExchangeGrantRequest.class);
verify(this.accessTokenResponseClient).getTokenResponse(grantRequestCaptor.capture());
@ -251,7 +254,9 @@ public class TokenExchangeReactiveOAuth2AuthorizedClientProviderTests {
// Shorten the lifespan of the access token by 90 seconds, which will ultimately
// force it to expire on the client
this.authorizedClientProvider.setClockSkew(Duration.ofSeconds(90));
OAuth2AccessTokenResponse accessTokenResponse = TestOAuth2AccessTokenResponses.accessTokenResponse().build();
OAuth2AccessTokenResponse accessTokenResponse = TestOAuth2AccessTokenResponses.accessTokenResponse()
.refreshToken("refresh")
.build();
given(this.accessTokenResponseClient.getTokenResponse(any(TokenExchangeGrantRequest.class)))
.willReturn(Mono.just(accessTokenResponse));
// @formatter:off
@ -267,6 +272,7 @@ public class TokenExchangeReactiveOAuth2AuthorizedClientProviderTests {
assertThat(reauthorizedClient.getClientRegistration()).isSameAs(this.clientRegistration);
assertThat(reauthorizedClient.getPrincipalName()).isEqualTo(this.principal.getName());
assertThat(reauthorizedClient.getAccessToken()).isEqualTo(accessTokenResponse.getAccessToken());
assertThat(reauthorizedClient.getRefreshToken()).isEqualTo(accessTokenResponse.getRefreshToken());
ArgumentCaptor<TokenExchangeGrantRequest> grantRequestCaptor = ArgumentCaptor
.forClass(TokenExchangeGrantRequest.class);
verify(this.accessTokenResponseClient).getTokenResponse(grantRequestCaptor.capture());
@ -289,7 +295,9 @@ public class TokenExchangeReactiveOAuth2AuthorizedClientProviderTests {
@Test
public void authorizeWhenTokenExchangeAndNotAuthorizedAndSubjectTokenResolvesThenAuthorized() {
OAuth2AccessTokenResponse accessTokenResponse = TestOAuth2AccessTokenResponses.accessTokenResponse().build();
OAuth2AccessTokenResponse accessTokenResponse = TestOAuth2AccessTokenResponses.accessTokenResponse()
.refreshToken("refresh")
.build();
given(this.accessTokenResponseClient.getTokenResponse(any(TokenExchangeGrantRequest.class)))
.willReturn(Mono.just(accessTokenResponse));
// @formatter:off
@ -303,6 +311,7 @@ public class TokenExchangeReactiveOAuth2AuthorizedClientProviderTests {
assertThat(authorizedClient.getClientRegistration()).isSameAs(this.clientRegistration);
assertThat(authorizedClient.getPrincipalName()).isEqualTo(this.principal.getName());
assertThat(authorizedClient.getAccessToken()).isEqualTo(accessTokenResponse.getAccessToken());
assertThat(authorizedClient.getRefreshToken()).isEqualTo(accessTokenResponse.getRefreshToken());
ArgumentCaptor<TokenExchangeGrantRequest> grantRequestCaptor = ArgumentCaptor
.forClass(TokenExchangeGrantRequest.class);
verify(this.accessTokenResponseClient).getTokenResponse(grantRequestCaptor.capture());
@ -317,7 +326,9 @@ public class TokenExchangeReactiveOAuth2AuthorizedClientProviderTests {
given(subjectTokenResolver.apply(any(OAuth2AuthorizationContext.class)))
.willReturn(Mono.just(this.subjectToken));
this.authorizedClientProvider.setSubjectTokenResolver(subjectTokenResolver);
OAuth2AccessTokenResponse accessTokenResponse = TestOAuth2AccessTokenResponses.accessTokenResponse().build();
OAuth2AccessTokenResponse accessTokenResponse = TestOAuth2AccessTokenResponses.accessTokenResponse()
.refreshToken("refresh")
.build();
given(this.accessTokenResponseClient.getTokenResponse(any(TokenExchangeGrantRequest.class)))
.willReturn(Mono.just(accessTokenResponse));
TestingAuthenticationToken principal = new TestingAuthenticationToken("user", "password");
@ -332,6 +343,7 @@ public class TokenExchangeReactiveOAuth2AuthorizedClientProviderTests {
assertThat(authorizedClient.getClientRegistration()).isSameAs(this.clientRegistration);
assertThat(authorizedClient.getPrincipalName()).isEqualTo(principal.getName());
assertThat(authorizedClient.getAccessToken()).isEqualTo(accessTokenResponse.getAccessToken());
assertThat(authorizedClient.getRefreshToken()).isEqualTo(accessTokenResponse.getRefreshToken());
verify(subjectTokenResolver).apply(authorizationContext);
ArgumentCaptor<TokenExchangeGrantRequest> grantRequestCaptor = ArgumentCaptor
.forClass(TokenExchangeGrantRequest.class);
@ -346,7 +358,9 @@ public class TokenExchangeReactiveOAuth2AuthorizedClientProviderTests {
Function<OAuth2AuthorizationContext, Mono<OAuth2Token>> actorTokenResolver = mock(Function.class);
given(actorTokenResolver.apply(any(OAuth2AuthorizationContext.class))).willReturn(Mono.just(this.actorToken));
this.authorizedClientProvider.setActorTokenResolver(actorTokenResolver);
OAuth2AccessTokenResponse accessTokenResponse = TestOAuth2AccessTokenResponses.accessTokenResponse().build();
OAuth2AccessTokenResponse accessTokenResponse = TestOAuth2AccessTokenResponses.accessTokenResponse()
.refreshToken("refresh")
.build();
given(this.accessTokenResponseClient.getTokenResponse(any(TokenExchangeGrantRequest.class)))
.willReturn(Mono.just(accessTokenResponse));
// @formatter:off
@ -360,6 +374,7 @@ public class TokenExchangeReactiveOAuth2AuthorizedClientProviderTests {
assertThat(authorizedClient.getClientRegistration()).isSameAs(this.clientRegistration);
assertThat(authorizedClient.getPrincipalName()).isEqualTo(this.principal.getName());
assertThat(authorizedClient.getAccessToken()).isEqualTo(accessTokenResponse.getAccessToken());
assertThat(authorizedClient.getRefreshToken()).isEqualTo(accessTokenResponse.getRefreshToken());
verify(actorTokenResolver).apply(authorizationContext);
ArgumentCaptor<TokenExchangeGrantRequest> grantRequestCaptor = ArgumentCaptor
.forClass(TokenExchangeGrantRequest.class);