Hi, I am trying to configure a OAuth 2.0 aware WebClient using ServerOAuth2AuthorizedClientExchangeFilterFunction and have issues with expiring tokens.
Spring (security) version: 5.3.4
Current behavior:
If a token expires the RemoveAuthorizedClientReactiveOAuth2AuthorizationFailureHandler deletes the client and an error is returned leading to a failing request. For the following requests a new authenticated client is created.
Expected behavior
If a token expires, the RemoveAuthorizedClientReactiveOAuth2AuthorizationFailureHandler deletes the client and a new authenticated client is created. This new client is used for the current and the following requests. The request succeeds with the newly created client and no error is reported.
Question
Is it possible to implement a OAuth filter which handles the creation of a new client in a way that no error occurs and the request succeeds? Before we updated to version 5.3.X this issue did not occur.
Code snipped
I implemented my WebClient bean like this (kotlin):
@Bean(name = ["webclient"])
fun webClient(): WebClient {
val clientRegistryRepo = InMemoryReactiveClientRegistrationRepository(
ClientRegistration
.withRegistrationId("someId")
.tokenUri("someUri")
.clientId("someClientId")
.clientSecret("secret")
.authorizationGrantType(AuthorizationGrantType.CLIENT_CREDENTIALS)
.build()
)
val clientService = InMemoryReactiveOAuth2AuthorizedClientService(clientRegistryRepo)
val authorizedClientManager =
AuthorizedClientServiceReactiveOAuth2AuthorizedClientManager(clientRegistryRepo, clientService)
val oauthFilter = ServerOAuth2AuthorizedClientExchangeFilterFunction(authorizedClientManager)
oauthFilter.setDefaultClientRegistrationId(paymentServiceSecretsResult.value.registrationId)
oauthFilter.setAuthorizationFailureHandler(handleOAuthFailure(clientService))
return WebClient.builder()
.filter(oauthFilter)
.baseUrl("someBaseUrl")
.build()
}
private fun handleOAuthFailure(
clientService: ReactiveOAuth2AuthorizedClientService
) = RemoveAuthorizedClientReactiveOAuth2AuthorizationFailureHandler {clientRegistrationId, principal, _ ->
clientService.removeAuthorizedClient(clientRegistrationId, principal.name).block()
Mono.empty()
}
Comment From: jgrandja
Thanks for the report @chriskn.
If a token expires, the
RemoveAuthorizedClientReactiveOAuth2AuthorizationFailureHandlerdeletes the client and a new authenticated client is created.
It's not that easy to simply create a new OAuth2AuthorizedClient with a refreshed (or renewed) access token. For example, if the client was authorized via the authorization_code grant, then the flow must be triggered so the user can re-authorize the client during the consent screen.
Since the application knows which flow the OAuth2AuthorizedClient is configured for, it would be better to re-trigger the flow from the client application whenever there is an error response. Furthermore, depending on the application requirements, the application may not want to automatically trigger the flow. Introducing this behaviour would not work for these scenarios.
Another option is to configure a custom ReactiveOAuth2AuthorizationFailureHandler via ServerOAuth2AuthorizedClientExchangeFilterFunction.setAuthorizationFailureHandler(), that performs the logic you require.
I'm going to close this, as it's recommended that the client application be in control of re-triggering the flow on an error response - it should be pretty straight forward to replay the request from the client application.