Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WebClient support should get new access token when expired and client_credentials #6127

Conversation

warrenbailey
Copy link

Updating the Servlet and Server OAuth2 authorized exchange filter functions so that they get a new access token when the current access token has expired and client_credentials flow is used.

Fixes #5893

@@ -245,13 +249,26 @@ public void setAccessTokenExpiresSkew(Duration accessTokenExpiresSkew) {
}

private Mono<OAuth2AuthorizedClient> refreshIfNecessary(ClientRequest request, ExchangeFunction next, OAuth2AuthorizedClient authorizedClient) {
if (shouldRefresh(authorizedClient)) {
ClientRegistration clientRegistration = authorizedClient.getClientRegistration();
if (AuthorizationGrantType.CLIENT_CREDENTIALS.equals(clientRegistration.getAuthorizationGrantType()) && hasExpired(authorizedClient)) {
Copy link

@lfarmer lfarmer Nov 23, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if a helper method would be better here? Something like

isClientCredentialsGrantType(authorizedClient)

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep can do that

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

Copy link
Contributor

@jgrandja jgrandja left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the PR @warrenbailey! This looks good and we should be able to merge after you apply some of the minor updates. See my comments inline.

return hasExpired(authorizedClient);
}

private boolean hasExpired(OAuth2AuthorizedClient authorizedClient) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's rename the method to hasTokenExpired

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

this(authorizedClientRepository, new OAuth2AuthorizedClientResolver(clientRegistrationRepository, authorizedClientRepository));
}

protected ServerOAuth2AuthorizedClientExchangeFilterFunction(ServerOAuth2AuthorizedClientRepository authorizedClientRepository, OAuth2AuthorizedClientResolver authorizedClientResolver) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's remove protected so it's package-private instead.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

return AuthorizationGrantType.CLIENT_CREDENTIALS.equals(clientRegistration.getAuthorizationGrantType());
}

private Mono<OAuth2AuthorizedClient> getNewAuthorizedClient(ClientRegistration clientRegistration, OAuth2AuthorizedClientResolver.Request request) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's rename the method to authorizeWithClientCredentials

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

return hasExpired(authorizedClient);
}

private boolean hasExpired(OAuth2AuthorizedClient authorizedClient) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's rename the method to hasTokenExpired

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

@@ -423,6 +424,80 @@ public void filterWhenRefreshRequiredThenRefresh() {
assertThat(getBody(request1)).isEmpty();
}

@Test
public void filterWhenClientCredentialsNotExpiresDoesNotGetNewToken() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we rename the method to filterWhenClientCredentialsTokenNotExpiredThenUseCurrentToken

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

}

@Test
public void filterWhenClientCredentialsHasNotExpiredDoNotGetNewToken() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we rename the method to filterWhenClientCredentialsTokenNotExpiredThenUseCurrentToken

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

@@ -144,6 +149,88 @@ public void filterWhenExistingAuthorizationThenSingleAuthorizationHeader() {
assertThat(headers.get(HttpHeaders.AUTHORIZATION)).containsOnly("Bearer " + this.accessToken.getTokenValue());
}

@Test
public void filterWhenClientCredentialsExpiresGetNewToken() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we rename the method to filterWhenClientCredentialsTokenExpiredThenGetNewToken

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done


this.function = new ServerOAuth2AuthorizedClientExchangeFilterFunction(this.authorizedClientRepository, this.oAuth2AuthorizedClientResolver);

OAuth2AccessToken newOauth2AccessToken = new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please rename to newAccessToken

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

when(this.oAuth2AuthorizedClientResolver.clientCredentials(any(), any(), any())).thenReturn(Mono.just(newAuthorizedClient));
when(this.oAuth2AuthorizedClientResolver.createDefaultedRequest(any(), any(), any())).thenReturn(Mono.just(r));

when(this.authorizedClientRepository.saveAuthorizedClient(any(), any(), any())).thenReturn(Mono.empty());
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this needed?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, otherwise I end up with a null pointer in the authorizedWithClientCredentials method line 273

@@ -133,7 +133,7 @@ public void setClientCredentialsTokenResponseClient(
});
}

private Mono<? extends OAuth2AuthorizedClient> clientCredentials(
public Mono<OAuth2AuthorizedClient> clientCredentials(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think we should expose a public API here. What you can do instead of:

when(this.oAuth2AuthorizedClientResolver.clientCredentials(any(), any(), any())).thenReturn(Mono.just(newAuthorizedClient));

try this

Mock ReactiveOAuth2AccessTokenResponseClient<OAuth2ClientCredentialsGrantRequest> and set it via OAuth2AuthorizedClientResolver.setClientCredentialsTokenResponseClient() and you'll also have to spy(oAuth2AuthorizedClientResolver)

Makes sense?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok I'll have a think about how it could be reworked.

Though I made it public as I need access in the authorizeWithClientCredentials method in ServerOAuthorizedCientExchangeFilterFunction not just for tests.

Copy link
Contributor

@jgrandja jgrandja Dec 17, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

on second review, instead of what I suggested previously for the test, let's just change the method signature to:

Mono<OAuth2AuthorizedClient> clientCredentials (with package-private access)

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds good, i'll do that now

@jgrandja jgrandja changed the title When using a Client Credentials Flow retrieve a new token if the current token expires client_credentials client should get new token when expired Dec 13, 2018
@jgrandja jgrandja changed the title client_credentials client should get new token when expired WebClient support should get new access token when expired and client_credentials Dec 13, 2018
@jgrandja jgrandja added type: enhancement A general enhancement Reactive in: oauth2 An issue in OAuth2 modules (oauth2-core, oauth2-client, oauth2-resource-server, oauth2-jose) labels Dec 13, 2018
@jgrandja jgrandja added this to the 5.2.0.M1 milestone Dec 13, 2018
@warrenbailey
Copy link
Author

Thanks for the feedback @jgrandja I'll make those changes

@jgrandja
Copy link
Contributor

Thanks for the updates @warrenbailey! There are a couple of comments I provided.

After those are resolved please squash the commits to 1 and format the commit message to include "Fixes gh-5893".

…ials token.

Once client credentials access token has expired retrieve a new token from the OAuth2 authorization server.
These tokens can't be refreshed because they do not have a refresh token associated with. This is standard behaviour for Oauth 2 client credentails
@warrenbailey warrenbailey force-pushed the gh-5893-new-client-credentials-token branch from 27d1a2b to 321bb75 Compare December 22, 2018 10:26
jgrandja added a commit that referenced this pull request Jan 7, 2019
@jgrandja
Copy link
Contributor

jgrandja commented Jan 7, 2019

Thank you @warrenbailey. This is now in master! I also added a polish commit for this feedback comment.

@jgrandja jgrandja closed this Jan 7, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
in: oauth2 An issue in OAuth2 modules (oauth2-core, oauth2-client, oauth2-resource-server, oauth2-jose) type: enhancement A general enhancement
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants