mirror of
				https://github.com/spring-projects/spring-security.git
				synced 2025-11-04 00:28:54 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			335 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
			
		
		
	
	
			335 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
To customize `{class-name}`, simply provide a bean as in the following example and it will be picked up by the default `ReactiveOAuth2AuthorizedClientManager` automatically:
 | 
						|
 | 
						|
[#oauth2-client-{section-id}-access-token-response-client-bean]
 | 
						|
.Access Token Response Configuration
 | 
						|
[tabs]
 | 
						|
======
 | 
						|
Java::
 | 
						|
+
 | 
						|
[source,java,role="primary",subs="+attributes"]
 | 
						|
----
 | 
						|
@Bean
 | 
						|
public ReactiveOAuth2AccessTokenResponseClient<{grant-request}> accessTokenResponseClient() {
 | 
						|
	{class-name} accessTokenResponseClient =
 | 
						|
		new {class-name}();
 | 
						|
	// ...
 | 
						|
	return accessTokenResponseClient;
 | 
						|
}
 | 
						|
----
 | 
						|
 | 
						|
Kotlin::
 | 
						|
+
 | 
						|
[source,kotlin,role="secondary",subs="+attributes"]
 | 
						|
----
 | 
						|
@Bean
 | 
						|
fun accessTokenResponseClient(): ReactiveOAuth2AccessTokenResponseClient<{grant-type}> {
 | 
						|
	val accessTokenResponseClient = {class-name}()
 | 
						|
	// ...
 | 
						|
	return accessTokenResponseClient
 | 
						|
}
 | 
						|
----
 | 
						|
======
 | 
						|
 | 
						|
`{class-name}` is very flexible and provides several options for customizing the OAuth 2.0 Access Token request and response for the {grant-type} grant.
 | 
						|
Choose from the following use cases to learn more:
 | 
						|
 | 
						|
* I want to <<oauth2-client-{section-id}-access-token-request-headers,customize headers of the Access Token request>>
 | 
						|
* I want to <<oauth2-client-{section-id}-access-token-request-parameters,customize parameters of the Access Token request>>
 | 
						|
* I want to <<oauth2-client-{section-id}-access-token-response-parameters,customize parameters of the Access Token response>>
 | 
						|
* I want to <<oauth2-client-{section-id}-access-token-response-web-client,customize the instance of `WebClient` that is used>>
 | 
						|
 | 
						|
[#oauth2-client-{section-id}-access-token-request]
 | 
						|
== Customizing the Access Token Request
 | 
						|
 | 
						|
`{class-name}` provides hooks for customizing HTTP headers and request parameters of the Token Request.
 | 
						|
 | 
						|
[#oauth2-client-{section-id}-access-token-request-headers]
 | 
						|
=== Customizing Request Headers
 | 
						|
 | 
						|
There are two options for customizing HTTP headers:
 | 
						|
 | 
						|
* Add additional headers by calling `addHeadersConverter()`
 | 
						|
* Fully customize headers by calling `setHeadersConverter()`
 | 
						|
 | 
						|
You can include additional headers without affecting the default headers added to every request using `addHeadersConverter()`.
 | 
						|
The following example adds a `User-Agent` header to the request when the `registrationId` is `spring`:
 | 
						|
 | 
						|
.Include Additional HTTP Headers
 | 
						|
[tabs]
 | 
						|
======
 | 
						|
Java::
 | 
						|
+
 | 
						|
[source,java,role="primary",subs="+attributes"]
 | 
						|
----
 | 
						|
{class-name} accessTokenResponseClient =
 | 
						|
	new {class-name}();
 | 
						|
accessTokenResponseClient.addHeadersConverter(grantRequest -> {
 | 
						|
	ClientRegistration clientRegistration = grantRequest.getClientRegistration();
 | 
						|
	HttpHeaders headers = new HttpHeaders();
 | 
						|
	if (clientRegistration.getRegistrationId().equals("spring")) {
 | 
						|
		headers.set(HttpHeaders.USER_AGENT, "my-user-agent");
 | 
						|
	}
 | 
						|
	return headers;
 | 
						|
});
 | 
						|
----
 | 
						|
 | 
						|
Kotlin::
 | 
						|
+
 | 
						|
[source,kotlin,role="secondary",subs="+attributes"]
 | 
						|
----
 | 
						|
val accessTokenResponseClient = {class-name}()
 | 
						|
accessTokenResponseClient.addHeadersConverter { grantRequest ->
 | 
						|
	val clientRegistration = grantRequest.getClientRegistration()
 | 
						|
	val headers = HttpHeaders()
 | 
						|
	if (clientRegistration.getRegistrationId() == "spring") {
 | 
						|
        headers[HttpHeaders.USER_AGENT] = "my-user-agent"
 | 
						|
	}
 | 
						|
	headers
 | 
						|
}
 | 
						|
----
 | 
						|
======
 | 
						|
 | 
						|
You can fully customize headers by re-using `DefaultOAuth2TokenRequestHeadersConverter` or providing a custom implementation using `setHeadersConverter()`.
 | 
						|
The following example re-uses `DefaultOAuth2TokenRequestHeadersConverter` and disables `encodeClientCredentials` so that HTTP Basic credentials are no longer encoded with `application/x-www-form-urlencoded`:
 | 
						|
 | 
						|
.Customize HTTP Headers
 | 
						|
[tabs]
 | 
						|
======
 | 
						|
Java::
 | 
						|
+
 | 
						|
[source,java,role="primary",subs="+attributes"]
 | 
						|
----
 | 
						|
DefaultOAuth2TokenRequestHeadersConverter headersConverter =
 | 
						|
	new DefaultOAuth2TokenRequestHeadersConverter();
 | 
						|
headersConverter.setEncodeClientCredentials(false);
 | 
						|
 | 
						|
{class-name} accessTokenResponseClient =
 | 
						|
	new {class-name}();
 | 
						|
accessTokenResponseClient.setHeadersConverter(headersConverter);
 | 
						|
----
 | 
						|
 | 
						|
Kotlin::
 | 
						|
+
 | 
						|
[source,kotlin,role="secondary",subs="+attributes"]
 | 
						|
----
 | 
						|
val headersConverter = DefaultOAuth2TokenRequestHeadersConverter()
 | 
						|
headersConverter.setEncodeClientCredentials(false)
 | 
						|
 | 
						|
val accessTokenResponseClient = {class-name}()
 | 
						|
accessTokenResponseClient.setHeadersConverter(headersConverter)
 | 
						|
----
 | 
						|
======
 | 
						|
 | 
						|
[#oauth2-client-{section-id}-access-token-request-parameters]
 | 
						|
=== Customizing Request Parameters
 | 
						|
 | 
						|
There are three options for customizing request parameters:
 | 
						|
 | 
						|
* Add additional parameters by calling `addParametersConverter()`
 | 
						|
* Override parameters by calling `setParametersConverter()`
 | 
						|
* Fully customize parameters by calling `setParametersCustomizer()`
 | 
						|
 | 
						|
[NOTE]
 | 
						|
====
 | 
						|
Using `setParametersConverter()` does not fully customize parameters because it would require the user to provide all default parameters themselves.
 | 
						|
Default parameters are always provided, but can be fully customized or omitted by calling `setParametersCustomizer()`.
 | 
						|
====
 | 
						|
 | 
						|
You can include additional parameters without affecting the default parameters added to every request using `addParametersConverter()`.
 | 
						|
The following example adds an `audience` parameter to the request when the `registrationId` is `keycloak`:
 | 
						|
 | 
						|
.Include Additional Request Parameters
 | 
						|
[tabs]
 | 
						|
======
 | 
						|
Java::
 | 
						|
+
 | 
						|
[source,java,role="primary",subs="+attributes"]
 | 
						|
----
 | 
						|
{class-name} accessTokenResponseClient =
 | 
						|
	new {class-name}();
 | 
						|
accessTokenResponseClient.addParametersConverter(grantRequest -> {
 | 
						|
	ClientRegistration clientRegistration = grantRequest.getClientRegistration();
 | 
						|
	MultiValueMap<String, String> parameters = new LinkedMultiValueMap<String, String>();
 | 
						|
	if (clientRegistration.getRegistrationId().equals("keycloak")) {
 | 
						|
		parameters.set(OAuth2ParameterNames.AUDIENCE, "my-audience");
 | 
						|
	}
 | 
						|
	return parameters;
 | 
						|
});
 | 
						|
----
 | 
						|
 | 
						|
Kotlin::
 | 
						|
+
 | 
						|
[source,kotlin,role="secondary",subs="+attributes"]
 | 
						|
----
 | 
						|
val accessTokenResponseClient = {class-name}()
 | 
						|
accessTokenResponseClient.addParametersConverter { grantRequest ->
 | 
						|
	val clientRegistration = grantRequest.getClientRegistration()
 | 
						|
	val parameters = LinkedMultiValueMap<String, String>()
 | 
						|
	if (clientRegistration.getRegistrationId() == "keycloak") {
 | 
						|
        parameters[OAuth2ParameterNames.AUDIENCE] = "my-audience"
 | 
						|
	}
 | 
						|
	parameters
 | 
						|
}
 | 
						|
----
 | 
						|
======
 | 
						|
 | 
						|
You can override default parameters using `setParametersConverter()`.
 | 
						|
The following example overrides the `client_id` parameter when the `registrationId` is `okta`:
 | 
						|
 | 
						|
.Override Request Parameters
 | 
						|
[tabs]
 | 
						|
======
 | 
						|
Java::
 | 
						|
+
 | 
						|
[source,java,role="primary",subs="+attributes"]
 | 
						|
----
 | 
						|
{class-name} accessTokenResponseClient =
 | 
						|
	new {class-name}();
 | 
						|
accessTokenResponseClient.setParametersConverter(grantRequest -> {
 | 
						|
	ClientRegistration clientRegistration = grantRequest.getClientRegistration();
 | 
						|
	LinkedMultiValueMap<String, String> parameters = new LinkedMultiValueMap<>();
 | 
						|
	if (clientRegistration.getRegistrationId().equals("okta")) {
 | 
						|
		parameters.set(OAuth2ParameterNames.CLIENT_ID, "my-client");
 | 
						|
	}
 | 
						|
	return parameters;
 | 
						|
});
 | 
						|
----
 | 
						|
 | 
						|
Kotlin::
 | 
						|
+
 | 
						|
[source,kotlin,role="secondary",subs="+attributes"]
 | 
						|
----
 | 
						|
val accessTokenResponseClient = {class-name}()
 | 
						|
accessTokenResponseClient.setParametersConverter { grantRequest ->
 | 
						|
    val clientRegistration = grantRequest.getClientRegistration()
 | 
						|
	val parameters = LinkedMultiValueMap<String, String>()
 | 
						|
	if (clientRegistration.getRegistrationId() == "okta") {
 | 
						|
        parameters[OAuth2ParameterNames.CLIENT_ID] = "my-client"
 | 
						|
	}
 | 
						|
	parameters
 | 
						|
}
 | 
						|
----
 | 
						|
======
 | 
						|
 | 
						|
You can fully customize parameters (including omitting default parameters) using `setParametersCustomizer()`.
 | 
						|
The following example omits the `client_id` parameter when the `client_assertion` parameter is present in the request:
 | 
						|
 | 
						|
.Omit Request Parameters
 | 
						|
[tabs]
 | 
						|
======
 | 
						|
Java::
 | 
						|
+
 | 
						|
[source,java,role="primary",subs="+attributes"]
 | 
						|
----
 | 
						|
{class-name} accessTokenResponseClient =
 | 
						|
	new {class-name}();
 | 
						|
accessTokenResponseClient.setParametersCustomizer(parameters -> {
 | 
						|
	if (parameters.containsKey(OAuth2ParameterNames.CLIENT_ASSERTION)) {
 | 
						|
		parameters.remove(OAuth2ParameterNames.CLIENT_ID);
 | 
						|
	}
 | 
						|
});
 | 
						|
----
 | 
						|
 | 
						|
Kotlin::
 | 
						|
+
 | 
						|
[source,kotlin,role="secondary",subs="+attributes"]
 | 
						|
----
 | 
						|
val accessTokenResponseClient = {class-name}()
 | 
						|
accessTokenResponseClient.setParametersCustomizer { parameters ->
 | 
						|
	if (parameters.containsKey(OAuth2ParameterNames.CLIENT_ASSERTION)) {
 | 
						|
		parameters.remove(OAuth2ParameterNames.CLIENT_ID)
 | 
						|
	}
 | 
						|
}
 | 
						|
----
 | 
						|
======
 | 
						|
 | 
						|
[#oauth2-client-{section-id}-access-token-response]
 | 
						|
== Customizing the Access Token Response
 | 
						|
 | 
						|
`{class-name}` provides hooks for customizing the OAuth 2.0 Access Token Response.
 | 
						|
 | 
						|
[#oauth2-client-{section-id}-access-token-response-parameters]
 | 
						|
=== Customizing Response Parameters
 | 
						|
 | 
						|
You can customize the conversion of Token Response parameters to an `OAuth2AccessTokenResponse` by calling `setBodyExtractor()`.
 | 
						|
The default implementation provided by `OAuth2BodyExtractors.oauth2AccessTokenResponse()` parses the response and handles errors accordingly.
 | 
						|
 | 
						|
The following example provides a starting point for customizing the conversion of Token Response parameters to an `OAuth2AccessTokenResponse`:
 | 
						|
 | 
						|
.Customize Body Extractor
 | 
						|
[tabs]
 | 
						|
======
 | 
						|
Java::
 | 
						|
+
 | 
						|
[source,java,role="primary",subs="+attributes"]
 | 
						|
----
 | 
						|
{class-name} accessTokenResponseClient =
 | 
						|
	new {class-name}();
 | 
						|
 | 
						|
BodyExtractor<Mono<Map<String, Object>>, ReactiveHttpInputMessage> bodyExtractor =
 | 
						|
	BodyExtractors.toMono(new ParameterizedTypeReference<>() {});
 | 
						|
accessTokenResponseClient.setBodyExtractor((inputMessage, context) ->
 | 
						|
	bodyExtractor.extract(inputMessage, context)
 | 
						|
		.map(parameters -> OAuth2AccessTokenResponse.withToken("custom-token")
 | 
						|
			// ...
 | 
						|
			.build()
 | 
						|
		)
 | 
						|
);
 | 
						|
----
 | 
						|
 | 
						|
Kotlin::
 | 
						|
+
 | 
						|
[source,kotlin,role="secondary",subs="+attributes"]
 | 
						|
----
 | 
						|
val accessTokenResponseClient = {class-name}()
 | 
						|
 | 
						|
val bodyExtractor = BodyExtractors.toMono(object : ParameterizedTypeReference<Map<String, Any>>() {})
 | 
						|
accessTokenResponseClient.setBodyExtractor { inputMessage, context ->
 | 
						|
	bodyExtractor.extract(inputMessage, context).map { parameters ->
 | 
						|
		OAuth2AccessTokenResponse.withToken("custom-token")
 | 
						|
			// ...
 | 
						|
			.build()
 | 
						|
	}
 | 
						|
}
 | 
						|
----
 | 
						|
======
 | 
						|
 | 
						|
[CAUTION]
 | 
						|
====
 | 
						|
When providing a custom `BodyExtractor`, you are responsible for detecting and converting an OAuth 2.0 Error Response to a `Mono.error()` with `OAuth2Error` based on parameters of the response.
 | 
						|
====
 | 
						|
 | 
						|
[#oauth2-client-{section-id}-access-token-response-web-client]
 | 
						|
=== Customizing the `WebClient`
 | 
						|
 | 
						|
Alternatively, if your requirements are more advanced, you can take full control of the request and/or response by providing a pre-configured `WebClient` to `setWebClient()` as the following example shows:
 | 
						|
 | 
						|
.Customize `WebClient`
 | 
						|
[tabs]
 | 
						|
======
 | 
						|
Java::
 | 
						|
+
 | 
						|
[source,java,role="primary",subs="+attributes"]
 | 
						|
----
 | 
						|
WebClient webClient = WebClient.builder()
 | 
						|
	// ...
 | 
						|
	.build();
 | 
						|
 | 
						|
{class-name} accessTokenResponseClient =
 | 
						|
	new {class-name}();
 | 
						|
accessTokenResponseClient.setWebClient(webClient);
 | 
						|
----
 | 
						|
 | 
						|
Kotlin::
 | 
						|
+
 | 
						|
[source,kotlin,role="secondary",subs="+attributes"]
 | 
						|
----
 | 
						|
val webClient = WebClient.builder()
 | 
						|
	// ...
 | 
						|
	.build()
 | 
						|
 | 
						|
val accessTokenResponseClient = {class-name}()
 | 
						|
accessTokenResponseClient.setWebClient(webClient)
 | 
						|
----
 | 
						|
======
 |