Expected Behavior
ServletOAuth2AuthorizedClientExchangeFilterFunction should have setAuthorizedClientManager() method.
If so, it becomes a little easier to customize RestTemplate (e.g. setting timeout) in ServletOAuth2AuthorizedClientExchangeFilterFunction.
// Create RestTemplate with timeout
RestTemplate restTemplate = restTemplateBuilder
.setConnectTimeout(Duration.ofMillis(1000)) // configure timeout
.setReadTimeout(Duration.ofMillis(1000)) // configure timeout
.messageConverters(new FormHttpMessageConverter(),
new OAuth2AccessTokenResponseHttpMessageConverter())
.errorHandler(new OAuth2ErrorResponseErrorHandler())
.build();
// Create TokenResponseClient
DefaultRefreshTokenTokenResponseClient accessTokenResponseClient =
new DefaultRefreshTokenTokenResponseClient();
accessTokenResponseClient.setRestOperations(restTemplate);
// Create OAuth2AuthorizedClientProvider
OAuth2AuthorizedClientProvider authorizedClientProvider =
OAuth2AuthorizedClientProviderBuilder.builder()
.authorizationCode()
.refreshToken(refresh -> refresh.accessTokenResponseClient(accessTokenResponseClient))
.build();
// Create OAuth2AuthorizedClientManager
DefaultOAuth2AuthorizedClientManager authorizedClientManager =
new DefaultOAuth2AuthorizedClientManager(clientRegistrationRepository, authorizedClientRepository);
authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider);
// We don't have to create RemoveAuthorizedClientOAuth2AuthorizationFailureHandler
// Create ServletOAuth2AuthorizedClientExchangeFilterFunction
// We can get clientRegistrationRepository and authorizedClientRepository easily because they are in ApplicationContext
ServletOAuth2AuthorizedClientExchangeFilterFunction oAuth2Client =
new ServletOAuth2AuthorizedClientExchangeFilterFunction(clientRegistrationRepository, authorizedClientRepository);
// I want this setter method!!
oAuth2Client.setAuthorizedClientManager(authorizedClientManager);
Current Behavior
When we customize RestTemplate (e.g. setting timeout) in ServletOAuth2AuthorizedClientExchangeFilterFunction, we have to write codes like below:
// Create RestTemplate with timeout
RestTemplate restTemplate = restTemplateBuilder
.setConnectTimeout(Duration.ofMillis(1000)) // configure timeout
.setReadTimeout(Duration.ofMillis(1000)) // configure timeout
.messageConverters(new FormHttpMessageConverter(),
new OAuth2AccessTokenResponseHttpMessageConverter())
.errorHandler(new OAuth2ErrorResponseErrorHandler())
.build();
// Create TokenResponseClient
DefaultRefreshTokenTokenResponseClient accessTokenResponseClient =
new DefaultRefreshTokenTokenResponseClient();
accessTokenResponseClient.setRestOperations(restTemplate);
// Create OAuth2AuthorizedClientProvider
OAuth2AuthorizedClientProvider authorizedClientProvider =
OAuth2AuthorizedClientProviderBuilder.builder()
.authorizationCode()
.refreshToken(refresh -> refresh.accessTokenResponseClient(accessTokenResponseClient))
.build();
// Create OAuth2AuthorizedClientManager
DefaultOAuth2AuthorizedClientManager authorizedClientManager =
new DefaultOAuth2AuthorizedClientManager(clientRegistrationRepository, authorizedClientRepository);
authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider);
// Create RemoveAuthorizedClientOAuth2AuthorizationFailureHandler
OAuth2AuthorizationFailureHandler authorizationFailureHandler =
new RemoveAuthorizedClientOAuth2AuthorizationFailureHandler(
(clientRegistrationId, principal, attributes) ->
authorizedClientRepository.removeAuthorizedClient(clientRegistrationId, principal,
(HttpServletRequest) attributes.get(HttpServletRequest.class.getName()),
(HttpServletResponse) attributes.get(HttpServletResponse.class.getName())));
// Create ServletOAuth2AuthorizedClientExchangeFilterFunction
// CANNOT use ServletOAuth2AuthorizedClientExchangeFilterFunction(ClientRegistrationRepository, OAuth2AuthorizedClientRepository) constructor
ServletOAuth2AuthorizedClientExchangeFilterFunction oAuth2Client =
new ServletOAuth2AuthorizedClientExchangeFilterFunction(authorizedClientManager);
oAuth2Client.setAuthorizationFailureHandler(authorizationFailureHandler);
There is no way to set OAuth2AuthorizedClientManager to ServletOAuth2AuthorizedClientExchangeFilterFunction when we use ServletOAuth2AuthorizedClientExchangeFilterFunction(ClientRegistrationRepository, OAuth2AuthorizedClientRepository) constructor.
And if we don't use the constructor above, we have to create OAuth2AuthorizationFailureHandler by ourselves.
I think this is verbose.
Context
When an authorization server is slow or hanging, RestTemplate without timeout will block the thread.
Comment From: jgrandja
@MasatoshiTada I don't think this makes sense:
// Create ServletOAuth2AuthorizedClientExchangeFilterFunction
// We can get clientRegistrationRepository and authorizedClientRepository easily because they are in ApplicationContext
ServletOAuth2AuthorizedClientExchangeFilterFunction oAuth2Client =
new ServletOAuth2AuthorizedClientExchangeFilterFunction(clientRegistrationRepository, authorizedClientRepository);
// I want this setter method!!
oAuth2Client.setAuthorizedClientManager(authorizedClientManager);
Passing in ClientRegistrationRepository and OAuth2AuthorizedClientRepository into the first constructor and then calling setAuthorizedClientManager(), which is composed of ClientRegistrationRepository and OAuth2AuthorizedClientRepository, will make it confusing to the user. A user should not have to associate ClientRegistrationRepository and OAuth2AuthorizedClientRepository 2 times.
The purpose of the first constructor is to provide a default OAuth2AuthorizedClientManager by simply providing ClientRegistrationRepository and OAuth2AuthorizedClientRepository.
The second constructor gives the control back to the user, where they can customize the OAuth2AuthorizedClientManager based on their application requirements. However, using this constructor also requires the user to provide a custom OAuth2AuthorizationFailureHandler via setAuthorizationFailureHandler(), only if the default is not sufficient.
Given your custom OAuth2AuthorizationFailureHandler:
// Create RemoveAuthorizedClientOAuth2AuthorizationFailureHandler
OAuth2AuthorizationFailureHandler authorizationFailureHandler =
new RemoveAuthorizedClientOAuth2AuthorizationFailureHandler(
(clientRegistrationId, principal, attributes) ->
authorizedClientRepository.removeAuthorizedClient(clientRegistrationId, principal,
(HttpServletRequest) attributes.get(HttpServletRequest.class.getName()),
(HttpServletResponse) attributes.get(HttpServletResponse.class.getName())));
This needs to be supplied to setAuthorizationFailureHandler(), since the constructor ServletOAuth2AuthorizedClientExchangeFilterFunction(OAuth2AuthorizedClientManager) cannot get access to the authorizedClientRepository.
Just to re-iterate, the first constructor is more of a convenience as it supplies the defaults, whereas the second constructor provides more control to the user but it's also their responsibility to provide all necessary components for configuration.
I'm going to close this based on the explanation above.