mirror of
				https://github.com/spring-projects/spring-security.git
				synced 2025-10-31 14:48:54 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			274 lines
		
	
	
		
			8.4 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
			
		
		
	
	
			274 lines
		
	
	
		
			8.4 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
| [[oauth2-client-additional-features]]
 | |
| = [[oauth2Client-additional-features]]Authorized Client Features
 | |
| 
 | |
| This section covers additional features provided by Spring Security for OAuth2 Client.
 | |
| 
 | |
| [[oauth2-client-registered-authorized-client]]
 | |
| == [[oauth2Client-registered-authorized-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`.
 | |
| 
 | |
| [tabs]
 | |
| ======
 | |
| Java::
 | |
| +
 | |
| [source,java,role="primary"]
 | |
| ----
 | |
| @Controller
 | |
| public class OAuth2ClientController {
 | |
| 
 | |
| 	@GetMapping("/")
 | |
| 	public Mono<String> index(@RegisteredOAuth2AuthorizedClient("okta") OAuth2AuthorizedClient authorizedClient) {
 | |
| 		return Mono.just(authorizedClient.getAccessToken())
 | |
| 				...
 | |
| 				.thenReturn("index");
 | |
| 	}
 | |
| }
 | |
| ----
 | |
| 
 | |
| Kotlin::
 | |
| +
 | |
| [source,kotlin,role="secondary"]
 | |
| ----
 | |
| @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 xref:reactive/oauth2/client/core.adoc#oauth2Client-authorized-manager-provider[ReactiveOAuth2AuthorizedClientManager] and therefore inherits it's capabilities.
 | |
| 
 | |
| [[oauth2-client-web-client]]
 | |
| == [[oauth2Client-webclient-webflux]]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 xref:reactive/oauth2/client/core.adoc#oauth2Client-authorized-manager-provider[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 a `ReactiveOAuth2AuthorizedClientProvider` is available to perform the authorization
 | |
| 
 | |
| The following code shows an example of how to configure `WebClient` with OAuth 2.0 Client support:
 | |
| 
 | |
| [tabs]
 | |
| ======
 | |
| Java::
 | |
| +
 | |
| [source,java,role="primary"]
 | |
| ----
 | |
| @Bean
 | |
| WebClient webClient(ReactiveOAuth2AuthorizedClientManager authorizedClientManager) {
 | |
| 	ServerOAuth2AuthorizedClientExchangeFilterFunction oauth2Client =
 | |
| 			new ServerOAuth2AuthorizedClientExchangeFilterFunction(authorizedClientManager);
 | |
| 	return WebClient.builder()
 | |
| 			.filter(oauth2Client)
 | |
| 			.build();
 | |
| }
 | |
| ----
 | |
| 
 | |
| Kotlin::
 | |
| +
 | |
| [source,kotlin,role="secondary"]
 | |
| ----
 | |
| @Bean
 | |
| fun webClient(authorizedClientManager: ReactiveOAuth2AuthorizedClientManager): WebClient {
 | |
|     val oauth2Client = ServerOAuth2AuthorizedClientExchangeFilterFunction(authorizedClientManager)
 | |
|     return WebClient.builder()
 | |
|             .filter(oauth2Client)
 | |
|             .build()
 | |
| }
 | |
| ----
 | |
| ======
 | |
| 
 | |
| [[oauth2-client-web-client-authorized-client]]
 | |
| === 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:
 | |
| 
 | |
| [tabs]
 | |
| ======
 | |
| Java::
 | |
| +
 | |
| [source,java,role="primary"]
 | |
| ----
 | |
| @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");
 | |
| }
 | |
| ----
 | |
| 
 | |
| Kotlin::
 | |
| +
 | |
| [source,kotlin,role="secondary"]
 | |
| ----
 | |
| @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:
 | |
| 
 | |
| [tabs]
 | |
| ======
 | |
| Java::
 | |
| +
 | |
| [source,java,role="primary"]
 | |
| ----
 | |
| @GetMapping("/")
 | |
| public Mono<String> index() {
 | |
| 	String resourceUri = ...
 | |
| 
 | |
| 	return webClient
 | |
| 			.get()
 | |
| 			.uri(resourceUri)
 | |
| 			.attributes(clientRegistrationId("okta"))   <1>
 | |
| 			.retrieve()
 | |
| 			.bodyToMono(String.class)
 | |
| 			...
 | |
| 			.thenReturn("index");
 | |
| }
 | |
| ----
 | |
| 
 | |
| Kotlin::
 | |
| +
 | |
| [source,kotlin,role="secondary"]
 | |
| ----
 | |
| @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`.
 | |
| 
 | |
| [[oauth2-client-web-client-default-authorized-client]]
 | |
| === 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:
 | |
| 
 | |
| [tabs]
 | |
| ======
 | |
| Java::
 | |
| +
 | |
| [source,java,role="primary"]
 | |
| ----
 | |
| @Bean
 | |
| WebClient webClient(ReactiveOAuth2AuthorizedClientManager authorizedClientManager) {
 | |
| 	ServerOAuth2AuthorizedClientExchangeFilterFunction oauth2Client =
 | |
| 			new ServerOAuth2AuthorizedClientExchangeFilterFunction(authorizedClientManager);
 | |
| 	oauth2Client.setDefaultOAuth2AuthorizedClient(true);
 | |
| 	return WebClient.builder()
 | |
| 			.filter(oauth2Client)
 | |
| 			.build();
 | |
| }
 | |
| ----
 | |
| 
 | |
| Kotlin::
 | |
| +
 | |
| [source,kotlin,role="secondary"]
 | |
| ----
 | |
| @Bean
 | |
| fun webClient(authorizedClientManager: ReactiveOAuth2AuthorizedClientManager): WebClient {
 | |
|     val oauth2Client = ServerOAuth2AuthorizedClientExchangeFilterFunction(authorizedClientManager)
 | |
|     oauth2Client.setDefaultOAuth2AuthorizedClient(true)
 | |
|     return WebClient.builder()
 | |
|             .filter(oauth2Client)
 | |
|             .build()
 | |
| }
 | |
| ----
 | |
| ======
 | |
| 
 | |
| [WARNING]
 | |
| ====
 | |
| 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:
 | |
| 
 | |
| [tabs]
 | |
| ======
 | |
| Java::
 | |
| +
 | |
| [source,java,role="primary"]
 | |
| ----
 | |
| @Bean
 | |
| WebClient webClient(ReactiveOAuth2AuthorizedClientManager authorizedClientManager) {
 | |
| 	ServerOAuth2AuthorizedClientExchangeFilterFunction oauth2Client =
 | |
| 			new ServerOAuth2AuthorizedClientExchangeFilterFunction(authorizedClientManager);
 | |
| 	oauth2Client.setDefaultClientRegistrationId("okta");
 | |
| 	return WebClient.builder()
 | |
| 			.filter(oauth2Client)
 | |
| 			.build();
 | |
| }
 | |
| ----
 | |
| 
 | |
| Kotlin::
 | |
| +
 | |
| [source,kotlin,role="secondary"]
 | |
| ----
 | |
| @Bean
 | |
| fun webClient(authorizedClientManager: ReactiveOAuth2AuthorizedClientManager): WebClient {
 | |
|     val oauth2Client = ServerOAuth2AuthorizedClientExchangeFilterFunction(authorizedClientManager)
 | |
|     oauth2Client.setDefaultClientRegistrationId("okta")
 | |
|     return WebClient.builder()
 | |
|             .filter(oauth2Client)
 | |
|             .build()
 | |
| }
 | |
| ----
 | |
| ======
 | |
| 
 | |
| [WARNING]
 | |
| ====
 | |
| It is recommended to be cautious with this feature since all HTTP requests will receive the access token.
 | |
| ====
 |