Authorized Client Features
This section covers additional features provided by Spring Security for OAuth2 Client.
Resolving an Authorized Client
The @RegisteredOAuth2AuthorizedClient
annotation provides the capability of resolving a method parameter to an argument value of type OAuth2AuthorizedClient
.
This is a convenient alternative compared to accessing the OAuth2AuthorizedClient
using the ReactiveOAuth2AuthorizedClientManager
or ReactiveOAuth2AuthorizedClientService
.
-
Java
-
Kotlin
@Controller
public class OAuth2ClientController {
@GetMapping("/")
public Mono<String> index(@RegisteredOAuth2AuthorizedClient("okta") OAuth2AuthorizedClient authorizedClient) {
return Mono.just(authorizedClient.getAccessToken())
...
.thenReturn("index");
}
}
@Controller
class OAuth2ClientController {
@GetMapping("/")
fun index(@RegisteredOAuth2AuthorizedClient("okta") authorizedClient: OAuth2AuthorizedClient): Mono<String> {
return Mono.just(authorizedClient.accessToken)
...
.thenReturn("index")
}
}
The @RegisteredOAuth2AuthorizedClient
annotation is handled by OAuth2AuthorizedClientArgumentResolver
, which directly uses a ReactiveOAuth2AuthorizedClientManager and therefore inherits it’s capabilities.
WebClient integration for Reactive Environments
The OAuth 2.0 Client support integrates with WebClient
using an ExchangeFilterFunction
.
The ServerOAuth2AuthorizedClientExchangeFilterFunction
provides a simple mechanism for requesting protected resources by using an OAuth2AuthorizedClient
and including the associated OAuth2AccessToken
as a Bearer Token.
It directly uses an ReactiveOAuth2AuthorizedClientManager and therefore inherits the following capabilities:
-
An
OAuth2AccessToken
will be requested if the client has not yet been authorized.-
authorization_code
- triggers the Authorization Request redirect to initiate the flow -
client_credentials
- the access token is obtained directly from the Token Endpoint -
password
- the access token is obtained directly from the Token Endpoint
-
-
If the
OAuth2AccessToken
is expired, it will be refreshed (or renewed) if aReactiveOAuth2AuthorizedClientProvider
is available to perform the authorization
The following code shows an example of how to configure WebClient
with OAuth 2.0 Client support:
-
Java
-
Kotlin
@Bean
WebClient webClient(ReactiveOAuth2AuthorizedClientManager authorizedClientManager) {
ServerOAuth2AuthorizedClientExchangeFilterFunction oauth2Client =
new ServerOAuth2AuthorizedClientExchangeFilterFunction(authorizedClientManager);
return WebClient.builder()
.filter(oauth2Client)
.build();
}
@Bean
fun webClient(authorizedClientManager: ReactiveOAuth2AuthorizedClientManager): WebClient {
val oauth2Client = ServerOAuth2AuthorizedClientExchangeFilterFunction(authorizedClientManager)
return WebClient.builder()
.filter(oauth2Client)
.build()
}
Providing the Authorized Client
The ServerOAuth2AuthorizedClientExchangeFilterFunction
determines the client to use (for a request) by resolving the OAuth2AuthorizedClient
from the ClientRequest.attributes()
(request attributes).
The following code shows how to set an OAuth2AuthorizedClient
as a request attribute:
-
Java
-
Kotlin
@GetMapping("/")
public Mono<String> index(@RegisteredOAuth2AuthorizedClient("okta") OAuth2AuthorizedClient authorizedClient) {
String resourceUri = ...
return webClient
.get()
.uri(resourceUri)
.attributes(oauth2AuthorizedClient(authorizedClient)) (1)
.retrieve()
.bodyToMono(String.class)
...
.thenReturn("index");
}
@GetMapping("/")
fun index(@RegisteredOAuth2AuthorizedClient("okta") authorizedClient: OAuth2AuthorizedClient): Mono<String> {
val resourceUri: String = ...
return webClient
.get()
.uri(resourceUri)
.attributes(oauth2AuthorizedClient(authorizedClient)) (1)
.retrieve()
.bodyToMono<String>()
...
.thenReturn("index")
}
1 | oauth2AuthorizedClient() is a static method in ServerOAuth2AuthorizedClientExchangeFilterFunction . |
The following code shows how to set the ClientRegistration.getRegistrationId()
as a request attribute:
-
Java
-
Kotlin
@GetMapping("/")
public Mono<String> index() {
String resourceUri = ...
return webClient
.get()
.uri(resourceUri)
.attributes(clientRegistrationId("okta")) (1)
.retrieve()
.bodyToMono(String.class)
...
.thenReturn("index");
}
@GetMapping("/")
fun index(): Mono<String> {
val resourceUri: String = ...
return webClient
.get()
.uri(resourceUri)
.attributes(clientRegistrationId("okta")) (1)
.retrieve()
.bodyToMono<String>()
...
.thenReturn("index")
}
1 | clientRegistrationId() is a static method in ServerOAuth2AuthorizedClientExchangeFilterFunction . |
Defaulting the Authorized Client
If neither OAuth2AuthorizedClient
or ClientRegistration.getRegistrationId()
is provided as a request attribute, the ServerOAuth2AuthorizedClientExchangeFilterFunction
can determine the default client to use depending on it’s configuration.
If setDefaultOAuth2AuthorizedClient(true)
is configured and the user has authenticated using ServerHttpSecurity.oauth2Login()
, the OAuth2AccessToken
associated with the current OAuth2AuthenticationToken
is used.
The following code shows the specific configuration:
-
Java
-
Kotlin
@Bean
WebClient webClient(ReactiveOAuth2AuthorizedClientManager authorizedClientManager) {
ServerOAuth2AuthorizedClientExchangeFilterFunction oauth2Client =
new ServerOAuth2AuthorizedClientExchangeFilterFunction(authorizedClientManager);
oauth2Client.setDefaultOAuth2AuthorizedClient(true);
return WebClient.builder()
.filter(oauth2Client)
.build();
}
@Bean
fun webClient(authorizedClientManager: ReactiveOAuth2AuthorizedClientManager): WebClient {
val oauth2Client = ServerOAuth2AuthorizedClientExchangeFilterFunction(authorizedClientManager)
oauth2Client.setDefaultOAuth2AuthorizedClient(true)
return WebClient.builder()
.filter(oauth2Client)
.build()
}
It is recommended to be cautious with this feature since all HTTP requests will receive the access token. |
Alternatively, if setDefaultClientRegistrationId("okta")
is configured with a valid ClientRegistration
, the OAuth2AccessToken
associated with the OAuth2AuthorizedClient
is used.
The following code shows the specific configuration:
-
Java
-
Kotlin
@Bean
WebClient webClient(ReactiveOAuth2AuthorizedClientManager authorizedClientManager) {
ServerOAuth2AuthorizedClientExchangeFilterFunction oauth2Client =
new ServerOAuth2AuthorizedClientExchangeFilterFunction(authorizedClientManager);
oauth2Client.setDefaultClientRegistrationId("okta");
return WebClient.builder()
.filter(oauth2Client)
.build();
}
@Bean
fun webClient(authorizedClientManager: ReactiveOAuth2AuthorizedClientManager): WebClient {
val oauth2Client = ServerOAuth2AuthorizedClientExchangeFilterFunction(authorizedClientManager)
oauth2Client.setDefaultClientRegistrationId("okta")
return WebClient.builder()
.filter(oauth2Client)
.build()
}
It is recommended to be cautious with this feature since all HTTP requests will receive the access token. |