Description
WebClient Support
This is now resolved. Demo usage can be found in oauth2webclient and oauth2webclient-webflux samples. A quick tour of the support:
Built In WebClient Support
WebClient
has built in support for easily adding a Bearer token. For example:
webClient.get()
.headers(h -> h.setBearerAuth(token))
...
Why use Spring Security Extensions?
Spring Security provides first class support for OAuth2. A few advantages of using this support are:
- If an access token is requested and not present, Spring Security will automatically request the access token.
- For authorization_code this involves performing the redirect and then replaying the original request
- For client_credentials the token is simply requested and saved
- Spring Security will automatically refresh expired tokens (if a refresh token is present)
- Builds on other Spring Security OAuth2 support making things like using discovery of endpoints very simple
- Users can choose to transparently include the current OAuth token or explicitly select which token should be used.
Setup
The first step is ensuring to setup the WebClient
correctly.
For a Servlet environment this looks like:
@Bean
WebClient webClient(ClientRegistrationRepository clientRegistrationRepository, OAuth2AuthorizedClientRepository authorizedClientRepository) {
ServletOAuth2AuthorizedClientExchangeFilterFunction oauth2 = new ServletOAuth2AuthorizedClientExchangeFilterFunction(clientRegistrationRepository, authorizedClientRepository);
// (optional) explicitly opt into using the oauth2Login to provide an access token implicitly
oauth2.setDefaultOAuth2AuthorizedClient(true);
return WebClient.builder()
.apply(oauth2.oauth2Configuration())
.build();
}
For other (i.e. WebFlux) environments it looks like:
@Bean
WebClient webClient(ReactiveClientRegistrationRepository clientRegistrationRepository,
ServerOAuth2AuthorizedClientRepository authorizedClientRepository) {
ServerOAuth2AuthorizedClientExchangeFilterFunction oauth =
new ServerOAuth2AuthorizedClientExchangeFilterFunction(clientRegistrationRepository, authorizedClientRepository);
// (optional) explicitly opt into using the oauth2Login to provide an access token implicitly
oauth.setDefaultOAuth2AuthorizedClient(true);
return WebClient.builder()
.filter(oauth)
.build();
}
Implicit OAuth2AuthorizedClient
If we set defaultOAuth2AuthorizedClient to true in our setup and the user authenticated with oauth2Login (i.e. OIDC), then the current authentication is used to automatically provide the access token. This is convenient, but in environments where not all endpoints should get the access token, it is dangerous (you might provide the wrong access token to an endpoint).
Mono<String> body = this.webClient
.get()
.uri(this.uri)
.retrieve()
.bodyToMono(String.class);
Explicit OAuth2AuthorizedClient
You can also explicitly provide an OAuth2AuthorizedClient
by setting it on the requests attributes. In the example below we resolve the OAuth2AuthorizedClient
using Spring WebFlux or Spring MVC argument resolver support. However, the user can choose to resolve the OAuth2AuthorizedClient
however they wish.
@GetMapping("/explicit")
Mono<String> explicit(@RegisteredOAuth2AuthorizedClient("client-id") OAuth2AuthorizedClient authorizedClient) {
return this.webClient
.get()
.uri(this.uri)
.attributes(oauth2AuthorizedClient(authorizedClient))
.retrieve()
.bodyToMono(String.class);
}
clientRegistrationId
Alternatively, it is possible to specify the clientRegistrationId
on the request attributes and the WebClient
will attempt to lookup the OAuth2AuthorizedClient
. If it is not found, one will automatically be acquired.
Mono<String> body = this.webClient
.get()
.uri(this.uri)
.attributes(clientRegistrationId("client-id"))
.retrieve()
.bodyToMono(String.class);
Summary (original)
We should add OAuth + WebClient support. The support for using WebClient in a Servlet environment and WebFlux must be separate because:
- The default values will be obtained from different types of contexts. For example, Spring Security's Authentication will be obtained from the SecurityContextHolder in a Servlet environment but from the ReactiveSecurityContextHolder in WebFlux.
- In the Servlet world we must be able to save to the HttpSession which is a blocking API. The rest of the OAuth support in the Servlet world is also using a blocking API
This is going to be broken up into multiple issues:
- Add WebClient Bearer token support #5389 - Add Bearer Token Support
- Add ServletOAuth2AuthorizedClientExchangeFilterFunction #5545 - Add ServletOAuth2AuthorizedClientExchangeFilterFunction
- Add ServerOAuth2AuthorizedClientExchangeFilterFunction #5386 - Add ServerOAuth2AuthorizedClientExchangeFilterFunction
- Add WebClient contextual inclusion of OAuth Tokens #5413 - Add a globally applicable token
- Provide support for refresh_token grant #4371 - Provide support for refresh_token grant
- ServletOAuth2AuthorizedClientExchangeFilterFunction support client_credentials #5639 - Provide support for client_credentials