mirror of
				https://github.com/spring-projects/spring-security.git
				synced 2025-10-25 19:58:48 +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) | ||
|  | ---- | ||
|  | ====== |