This version is still in development and is not considered stable yet. For the latest stable version, please use Spring Security 6.4.2!

Authorization Grant Support

This section describes Spring Security’s support for authorization grants.

Authorization Code

See the OAuth 2.0 Authorization Framework for further details on the Authorization Code grant.

Obtaining Authorization

See the Authorization Request/Response protocol flow for the Authorization Code grant.

Initiating the Authorization Request

The OAuth2AuthorizationRequestRedirectFilter uses an OAuth2AuthorizationRequestResolver to resolve an OAuth2AuthorizationRequest and initiate the Authorization Code grant flow by redirecting the end-user’s user-agent to the Authorization Server’s Authorization Endpoint.

The primary role of the OAuth2AuthorizationRequestResolver is to resolve an OAuth2AuthorizationRequest from the provided web request. The default implementation DefaultOAuth2AuthorizationRequestResolver matches on the (default) path /oauth2/authorization/{registrationId}, extracting the registrationId, and using it to build the OAuth2AuthorizationRequest for the associated ClientRegistration.

Consider the following Spring Boot properties for an OAuth 2.0 Client registration:

spring:
  security:
    oauth2:
      client:
        registration:
          okta:
            client-id: okta-client-id
            client-secret: okta-client-secret
            authorization-grant-type: authorization_code
            redirect-uri: "{baseUrl}/authorized/okta"
            scope: read, write
        provider:
          okta:
            authorization-uri: https://dev-1234.oktapreview.com/oauth2/v1/authorize
            token-uri: https://dev-1234.oktapreview.com/oauth2/v1/token

Given the preceding properties, a request with the base path /oauth2/authorization/okta initiates the Authorization Request redirect by the OAuth2AuthorizationRequestRedirectFilter and ultimately starts the Authorization Code grant flow.

The AuthorizationCodeOAuth2AuthorizedClientProvider is an implementation of OAuth2AuthorizedClientProvider for the Authorization Code grant, which also initiates the Authorization Request redirect by the OAuth2AuthorizationRequestRedirectFilter.

If the OAuth 2.0 Client is a Public Client, configure the OAuth 2.0 Client registration as follows:

spring:
  security:
    oauth2:
      client:
        registration:
          okta:
            client-id: okta-client-id
            client-authentication-method: none
            authorization-grant-type: authorization_code
            redirect-uri: "{baseUrl}/authorized/okta"
            # ...

Public Clients are supported by using Proof Key for Code Exchange (PKCE). If the client is running in an untrusted environment (such as a native application or web browser-based application) and is therefore incapable of maintaining the confidentiality of its credentials, PKCE is automatically used when the following conditions are true:

  1. client-secret is omitted (or empty)

  2. client-authentication-method is set to none (ClientAuthenticationMethod.NONE)

If the OAuth 2.0 Provider supports PKCE for Confidential Clients, you may (optionally) configure it using DefaultOAuth2AuthorizationRequestResolver.setAuthorizationRequestCustomizer(OAuth2AuthorizationRequestCustomizers.withPkce()).

The DefaultOAuth2AuthorizationRequestResolver also supports URI template variables for the redirect-uri by using UriComponentsBuilder.

The following configuration uses all the supported URI template variables:

spring:
  security:
    oauth2:
      client:
        registration:
          okta:
            # ...
            redirect-uri: "{baseScheme}://{baseHost}{basePort}{basePath}/authorized/{registrationId}"
            # ...

{baseUrl} resolves to {baseScheme}://{baseHost}{basePort}{basePath}

Configuring the redirect-uri with URI template variables is especially useful when the OAuth 2.0 Client is running behind a Proxy Server. Doing so ensures that the X-Forwarded-* headers are used when expanding the redirect-uri.

Customizing the Authorization Request

One of the primary use cases an OAuth2AuthorizationRequestResolver can realize is the ability to customize the Authorization Request with additional parameters above the standard parameters defined in the OAuth 2.0 Authorization Framework.

For example, OpenID Connect defines additional OAuth 2.0 request parameters for the Authorization Code Flow extending from the standard parameters defined in the OAuth 2.0 Authorization Framework. One of those extended parameters is the prompt parameter.

The prompt parameter is optional. Space delimited, case sensitive list of ASCII string values that specifies whether the Authorization Server prompts the End-User for re-authentication and consent. The defined values are: none, login, consent, and select_account.

The following example shows how to configure the DefaultOAuth2AuthorizationRequestResolver with a Consumer<OAuth2AuthorizationRequest.Builder> that customizes the Authorization Request for oauth2Login(), by including the request parameter prompt=consent.

  • Java

  • Kotlin

@Configuration
@EnableWebSecurity
public class OAuth2LoginSecurityConfig {

	@Autowired
	private ClientRegistrationRepository clientRegistrationRepository;

	@Bean
	public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
		http
			.authorizeHttpRequests(authorize -> authorize
				.anyRequest().authenticated()
			)
			.oauth2Login(oauth2 -> oauth2
				.authorizationEndpoint(authorization -> authorization
					.authorizationRequestResolver(
						authorizationRequestResolver(this.clientRegistrationRepository)
					)
				)
			);
		return http.build();
	}

	private OAuth2AuthorizationRequestResolver authorizationRequestResolver(
			ClientRegistrationRepository clientRegistrationRepository) {

		DefaultOAuth2AuthorizationRequestResolver authorizationRequestResolver =
				new DefaultOAuth2AuthorizationRequestResolver(
						clientRegistrationRepository, "/oauth2/authorization");
		authorizationRequestResolver.setAuthorizationRequestCustomizer(
				authorizationRequestCustomizer());

		return  authorizationRequestResolver;
	}

	private Consumer<OAuth2AuthorizationRequest.Builder> authorizationRequestCustomizer() {
		return customizer -> customizer
					.additionalParameters(params -> params.put("prompt", "consent"));
	}
}
@Configuration
@EnableWebSecurity
class SecurityConfig {

    @Autowired
    private lateinit var customClientRegistrationRepository: ClientRegistrationRepository

    @Bean
    open fun filterChain(http: HttpSecurity): SecurityFilterChain {
        http {
            authorizeRequests {
                authorize(anyRequest, authenticated)
            }
            oauth2Login {
                authorizationEndpoint {
                    authorizationRequestResolver = authorizationRequestResolver(customClientRegistrationRepository)
                }
            }
        }
        return http.build()
    }

    private fun authorizationRequestResolver(
            clientRegistrationRepository: ClientRegistrationRepository?): OAuth2AuthorizationRequestResolver {
        val authorizationRequestResolver = DefaultOAuth2AuthorizationRequestResolver(
                clientRegistrationRepository, "/oauth2/authorization")
        authorizationRequestResolver.setAuthorizationRequestCustomizer(
                authorizationRequestCustomizer())
        return authorizationRequestResolver
    }

    private fun authorizationRequestCustomizer(): Consumer<OAuth2AuthorizationRequest.Builder> {
        return Consumer { customizer ->
            customizer
                    .additionalParameters { params -> params["prompt"] = "consent" }
        }
    }
}

For the simple use case where the additional request parameter is always the same for a specific provider, you can add it directly in the authorization-uri property.

For example, if the value for the request parameter prompt is always consent for the provider okta, you can configure it as follows:

spring:
  security:
    oauth2:
      client:
        provider:
          okta:
            authorization-uri: https://dev-1234.oktapreview.com/oauth2/v1/authorize?prompt=consent

The preceding example shows the common use case of adding a custom parameter on top of the standard parameters. Alternatively, if your requirements are more advanced, you can take full control in building the Authorization Request URI by overriding the OAuth2AuthorizationRequest.authorizationRequestUri property.

OAuth2AuthorizationRequest.Builder.build() constructs the OAuth2AuthorizationRequest.authorizationRequestUri, which represents the Authorization Request URI including all query parameters using the application/x-www-form-urlencoded format.

The following example shows a variation of authorizationRequestCustomizer() from the preceding example and instead overrides the OAuth2AuthorizationRequest.authorizationRequestUri property:

  • Java

  • Kotlin

private Consumer<OAuth2AuthorizationRequest.Builder> authorizationRequestCustomizer() {
	return customizer -> customizer
				.authorizationRequestUri(uriBuilder -> uriBuilder
					.queryParam("prompt", "consent").build());
}
private fun authorizationRequestCustomizer(): Consumer<OAuth2AuthorizationRequest.Builder> {
    return Consumer { customizer: OAuth2AuthorizationRequest.Builder ->
        customizer
                .authorizationRequestUri { uriBuilder: UriBuilder ->
                    uriBuilder
                            .queryParam("prompt", "consent").build()
                }
    }
}

Storing the Authorization Request

The AuthorizationRequestRepository is responsible for the persistence of the OAuth2AuthorizationRequest from the time the Authorization Request is initiated to the time the Authorization Response is received (the callback).

The OAuth2AuthorizationRequest is used to correlate and validate the Authorization Response.

The default implementation of AuthorizationRequestRepository is HttpSessionOAuth2AuthorizationRequestRepository, which stores the OAuth2AuthorizationRequest in the HttpSession.

If you have a custom implementation of AuthorizationRequestRepository, you can configure it as follows:

AuthorizationRequestRepository Configuration
  • Java

  • Kotlin

  • Xml

@Configuration
@EnableWebSecurity
public class OAuth2ClientSecurityConfig {

	@Bean
	public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
		http
			.oauth2Client(oauth2 -> oauth2
				.authorizationCodeGrant(codeGrant -> codeGrant
					.authorizationRequestRepository(this.authorizationRequestRepository())
					// ...
				)
			)
            .oauth2Login(oauth2 -> oauth2
                .authorizationEndpoint(endpoint -> endpoint
                    .authorizationRequestRepository(this.authorizationRequestRepository())
                    // ...
                )
            );
			return http.build();
	}

    @Bean
    public AuthorizationRequestRepository<OAuth2AuthorizationRequest> authorizationRequestRepository() {
        return new CustomOAuth2AuthorizationRequestRepository();
    }
}
@Configuration
@EnableWebSecurity
class OAuth2ClientSecurityConfig {

    @Bean
    open fun filterChain(http: HttpSecurity): SecurityFilterChain {
        http {
            oauth2Client {
                authorizationCodeGrant {
                    authorizationRequestRepository = authorizationRequestRepository()
                }
            }
        }
        return http.build()
    }
}
<http>
	<oauth2-client>
		<authorization-code-grant authorization-request-repository-ref="authorizationRequestRepository"/>
	</oauth2-client>
</http>

Requesting an Access Token

See the Access Token Request/Response protocol flow for the Authorization Code grant.

There are two implementations of OAuth2AccessTokenResponseClient that can be used to make HTTP requests to the Token Endpoint in order to obtain an access token for the Authorization Code grant:

  • DefaultAuthorizationCodeTokenResponseClient (default)

  • RestClientAuthorizationCodeTokenResponseClient

The default implementation uses a RestOperations instance to exchange an authorization code for an access token at the Authorization Server’s Token Endpoint. Spring Security 6.4 introduces a new implementation based on RestClient, which provides similar functionality but is better aligned with the Reactive version of the component (based on WebClient) in order to provide consistent configuration for applications on either stack.

This section focuses on RestClientAuthorizationCodeTokenResponseClient. You can read about DefaultAuthorizationCodeTokenResponseClient in the Spring Security 6.3 documentation.

To opt-in to using RestClientAuthorizationCodeTokenResponseClient, simply provide a bean as in the following example and it will be picked up by the default OAuth2AuthorizedClientManager automatically:

Access Token Response Configuration
  • Java

  • Kotlin

@Bean
public OAuth2AccessTokenResponseClient<OAuth2AuthorizationCodeGrantRequest> accessTokenResponseClient() {
	return new RestClientAuthorizationCodeTokenResponseClient();
}
@Bean
fun accessTokenResponseClient(): OAuth2AccessTokenResponseClient<Authorization Code> {
	return RestClientAuthorizationCodeTokenResponseClient()
}

The new implementation will be the default in Spring Security 7.

RestClientAuthorizationCodeTokenResponseClient is very flexible and provides several options for customizing the OAuth 2.0 Access Token request and response for the Authorization Code grant. Choose from the following use cases to learn more:

Customizing the Access Token Request

RestClientAuthorizationCodeTokenResponseClient provides hooks for customizing HTTP headers and request parameters of the OAuth 2.0 Access Token Request.

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
  • Java

  • Kotlin

RestClientAuthorizationCodeTokenResponseClient accessTokenResponseClient =
	new RestClientAuthorizationCodeTokenResponseClient();
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;
});
val accessTokenResponseClient = RestClientAuthorizationCodeTokenResponseClient()
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
  • Java

  • Kotlin

DefaultOAuth2TokenRequestHeadersConverter headersConverter =
	new DefaultOAuth2TokenRequestHeadersConverter();
headersConverter.setEncodeClientCredentials(false);

RestClientAuthorizationCodeTokenResponseClient accessTokenResponseClient =
	new RestClientAuthorizationCodeTokenResponseClient();
accessTokenResponseClient.setHeadersConverter(headersConverter);
val headersConverter = DefaultOAuth2TokenRequestHeadersConverter()
headersConverter.setEncodeClientCredentials(false)

val accessTokenResponseClient = RestClientAuthorizationCodeTokenResponseClient()
accessTokenResponseClient.setHeadersConverter(headersConverter)

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()

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
  • Java

  • Kotlin

RestClientAuthorizationCodeTokenResponseClient accessTokenResponseClient =
	new RestClientAuthorizationCodeTokenResponseClient();
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;
});
val accessTokenResponseClient = RestClientAuthorizationCodeTokenResponseClient()
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
  • Java

  • Kotlin

RestClientAuthorizationCodeTokenResponseClient accessTokenResponseClient =
	new RestClientAuthorizationCodeTokenResponseClient();
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;
});
val parametersConverter = DefaultOAuth2TokenRequestParametersConverter<OAuth2AuthorizationCodeGrantRequest>()
parametersConverter.setParametersCustomizer { parameters ->
	if (parameters.containsKey(OAuth2ParameterNames.CLIENT_ASSERTION)) {
		parameters.remove(OAuth2ParameterNames.CLIENT_ID)
	}
}

val accessTokenResponseClient = RestClientAuthorizationCodeTokenResponseClient()
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
  • Java

  • Kotlin

RestClientAuthorizationCodeTokenResponseClient accessTokenResponseClient =
	new RestClientAuthorizationCodeTokenResponseClient();
accessTokenResponseClient.setParametersCustomizer(parameters -> {
	if (parameters.containsKey(OAuth2ParameterNames.CLIENT_ASSERTION)) {
		parameters.remove(OAuth2ParameterNames.CLIENT_ID);
	}
});
val accessTokenResponseClient = RestClientAuthorizationCodeTokenResponseClient()
accessTokenResponseClient.setParametersCustomizer { parameters ->
	if (parameters.containsKey(OAuth2ParameterNames.CLIENT_ASSERTION)) {
		parameters.remove(OAuth2ParameterNames.CLIENT_ID)
	}
}

Customizing the Access Token Response

RestClientAuthorizationCodeTokenResponseClient provides hooks for customizing response parameters and error handling of the OAuth 2.0 Access Token Response.

Customizing the WebClient

You can customize the Token Response by providing a pre-configured RestClient to setRestClient(). The default RestClient is configured as follows:

Default RestClient Configuration
  • Java

  • Kotlin

RestClient restClient = RestClient.builder()
	.messageConverters(messageConverters -> {
		messageConverters.clear();
		messageConverters.add(new FormHttpMessageConverter());
		messageConverters.add(new OAuth2AccessTokenResponseHttpMessageConverter());
	})
	.defaultStatusHandler(new OAuth2ErrorResponseErrorHandler())
	.build();

RestClientAuthorizationCodeTokenResponseClient accessTokenResponseClient =
	new RestClientAuthorizationCodeTokenResponseClient();
accessTokenResponseClient.setRestClient(restClient);
val restClient = RestClient.builder()
	.messageConverters { messageConverters ->
		messageConverters.clear()
		messageConverters.add(FormHttpMessageConverter())
		messageConverters.add(OAuth2AccessTokenResponseHttpMessageConverter())
	}
	.defaultStatusHandler(OAuth2ErrorResponseErrorHandler())
	.build()

val accessTokenResponseClient = RestClientAuthorizationCodeTokenResponseClient()
accessTokenResponseClient.setRestClient(restClient)

OAuth2AccessTokenResponseHttpMessageConverter is an HttpMessageConverter for an OAuth 2.0 Access Token Response. You can customize the conversion of Token Response parameters to an OAuth2AccessTokenResponse by calling setAccessTokenResponseConverter(). The default implementation is DefaultMapOAuth2AccessTokenResponseConverter.

OAuth2ErrorResponseErrorHandler is a ResponseErrorHandler that can handle an OAuth 2.0 Error, such as 400 Bad Request. It uses an OAuth2ErrorHttpMessageConverter for converting the OAuth 2.0 Error parameters to an OAuth2Error. You can customize the conversion of Token Response parameters to an OAuth2Error by calling setErrorConverter().

Spring MVC FormHttpMessageConverter is required, as it is used when sending the OAuth 2.0 Access Token Request.

Customizing Response Parameters

The following example provides a starting point for customizing the conversion of Token Response parameters to an OAuth2AccessTokenResponse:

Customize Access Token Response Converter
  • Java

  • Kotlin

OAuth2AccessTokenResponseHttpMessageConverter accessTokenResponseMessageConverter =
	new OAuth2AccessTokenResponseHttpMessageConverter();
accessTokenResponseMessageConverter.setAccessTokenResponseConverter(parameters -> {
	// ...
	return OAuth2AccessTokenResponse.withToken("custom-token")
		// ...
		.build();
});
val accessTokenResponseMessageConverter = OAuth2AccessTokenResponseHttpMessageConverter()
accessTokenResponseMessageConverter.setAccessTokenResponseConverter { parameters ->
	// ...
	return OAuth2AccessTokenResponse.withToken("custom-token")
		// ...
		.build()
}

Customizing Error Handling

The following example provides a starting point for customizing the conversion of Error parameters to an OAuth2Error:

Customize Access Token Error Handler
  • Java

  • Kotlin

OAuth2ErrorHttpMessageConverter errorConverter =
	new OAuth2ErrorHttpMessageConverter();
errorConverter.setErrorConverter(parameters -> {
	// ...
	return new OAuth2Error("custom-error", "custom description", "custom-uri");
});

OAuth2ErrorResponseErrorHandler errorHandler =
	new OAuth2ErrorResponseErrorHandler();
errorHandler.setErrorConverter(errorConverter);
val errorConverter = OAuth2ErrorHttpMessageConverter()
errorConverter.setErrorConverter { parameters ->
	// ...
	return OAuth2Error("custom-error", "custom description", "custom-uri")
}

val errorHandler = OAuth2ErrorResponseErrorHandler()
errorHandler.setErrorConverter(errorConverter)

Customize using the DSL

Whether you customize RestClientAuthorizationCodeTokenResponseClient or provide your own implementation of OAuth2AccessTokenResponseClient, you can configure it using the DSL (as an alternative to publishing a bean) as follows:

Access Token Response Configuration via DSL
  • Java

  • Kotlin

  • Xml

@Configuration
@EnableWebSecurity
public class OAuth2ClientSecurityConfig {

	@Bean
	public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
		http
			.oauth2Client(oauth2 -> oauth2
				.authorizationCodeGrant(codeGrant -> codeGrant
					.accessTokenResponseClient(this.accessTokenResponseClient())
					// ...
				)
			);
		return http.build();
	}
}
@Configuration
@EnableWebSecurity
class OAuth2ClientSecurityConfig {

    @Bean
    open fun filterChain(http: HttpSecurity): SecurityFilterChain {
        http {
            oauth2Client {
                authorizationCodeGrant {
                    accessTokenResponseClient = accessTokenResponseClient()
                }
            }
        }
        return http.build()
    }
}
<http>
	<oauth2-client>
		<authorization-code-grant access-token-response-client-ref="accessTokenResponseClient"/>
	</oauth2-client>
</http>

Refresh Token

See the OAuth 2.0 Authorization Framework for further details on the Refresh Token.

Refreshing an Access Token

See the Access Token Request/Response protocol flow for the Refresh Token grant.

There are two implementations of OAuth2AccessTokenResponseClient that can be used to make HTTP requests to the Token Endpoint in order to obtain an access token for the Refresh Token grant:

  • DefaultRefreshTokenTokenResponseClient (default)

  • RestClientRefreshTokenTokenResponseClient

The default implementation uses a RestOperations instance to exchange an authorization code for an access token at the Authorization Server’s Token Endpoint. Spring Security 6.4 introduces a new implementation based on RestClient, which provides similar functionality but is better aligned with the Reactive version of the component (based on WebClient) in order to provide consistent configuration for applications on either stack.

This section focuses on RestClientRefreshTokenTokenResponseClient. You can read about DefaultRefreshTokenTokenResponseClient in the Spring Security 6.3 documentation.

To opt-in to using RestClientRefreshTokenTokenResponseClient, simply provide a bean as in the following example and it will be picked up by the default OAuth2AuthorizedClientManager automatically:

Access Token Response Configuration
  • Java

  • Kotlin

@Bean
public OAuth2AccessTokenResponseClient<OAuth2RefreshTokenGrantRequest> accessTokenResponseClient() {
	return new RestClientRefreshTokenTokenResponseClient();
}
@Bean
fun accessTokenResponseClient(): OAuth2AccessTokenResponseClient<Refresh Token> {
	return RestClientRefreshTokenTokenResponseClient()
}

The new implementation will be the default in Spring Security 7.

RestClientRefreshTokenTokenResponseClient is very flexible and provides several options for customizing the OAuth 2.0 Access Token request and response for the Refresh Token grant. Choose from the following use cases to learn more:

Customizing the Access Token Request

RestClientRefreshTokenTokenResponseClient provides hooks for customizing HTTP headers and request parameters of the OAuth 2.0 Access Token Request.

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
  • Java

  • Kotlin

RestClientRefreshTokenTokenResponseClient accessTokenResponseClient =
	new RestClientRefreshTokenTokenResponseClient();
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;
});
val accessTokenResponseClient = RestClientRefreshTokenTokenResponseClient()
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
  • Java

  • Kotlin

DefaultOAuth2TokenRequestHeadersConverter headersConverter =
	new DefaultOAuth2TokenRequestHeadersConverter();
headersConverter.setEncodeClientCredentials(false);

RestClientRefreshTokenTokenResponseClient accessTokenResponseClient =
	new RestClientRefreshTokenTokenResponseClient();
accessTokenResponseClient.setHeadersConverter(headersConverter);
val headersConverter = DefaultOAuth2TokenRequestHeadersConverter()
headersConverter.setEncodeClientCredentials(false)

val accessTokenResponseClient = RestClientRefreshTokenTokenResponseClient()
accessTokenResponseClient.setHeadersConverter(headersConverter)

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()

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
  • Java

  • Kotlin

RestClientRefreshTokenTokenResponseClient accessTokenResponseClient =
	new RestClientRefreshTokenTokenResponseClient();
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;
});
val accessTokenResponseClient = RestClientRefreshTokenTokenResponseClient()
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
  • Java

  • Kotlin

RestClientRefreshTokenTokenResponseClient accessTokenResponseClient =
	new RestClientRefreshTokenTokenResponseClient();
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;
});
val parametersConverter = DefaultOAuth2TokenRequestParametersConverter<OAuth2RefreshTokenGrantRequest>()
parametersConverter.setParametersCustomizer { parameters ->
	if (parameters.containsKey(OAuth2ParameterNames.CLIENT_ASSERTION)) {
		parameters.remove(OAuth2ParameterNames.CLIENT_ID)
	}
}

val accessTokenResponseClient = RestClientRefreshTokenTokenResponseClient()
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
  • Java

  • Kotlin

RestClientRefreshTokenTokenResponseClient accessTokenResponseClient =
	new RestClientRefreshTokenTokenResponseClient();
accessTokenResponseClient.setParametersCustomizer(parameters -> {
	if (parameters.containsKey(OAuth2ParameterNames.CLIENT_ASSERTION)) {
		parameters.remove(OAuth2ParameterNames.CLIENT_ID);
	}
});
val accessTokenResponseClient = RestClientRefreshTokenTokenResponseClient()
accessTokenResponseClient.setParametersCustomizer { parameters ->
	if (parameters.containsKey(OAuth2ParameterNames.CLIENT_ASSERTION)) {
		parameters.remove(OAuth2ParameterNames.CLIENT_ID)
	}
}

Customizing the Access Token Response

RestClientRefreshTokenTokenResponseClient provides hooks for customizing response parameters and error handling of the OAuth 2.0 Access Token Response.

Customizing the WebClient

You can customize the Token Response by providing a pre-configured RestClient to setRestClient(). The default RestClient is configured as follows:

Default RestClient Configuration
  • Java

  • Kotlin

RestClient restClient = RestClient.builder()
	.messageConverters(messageConverters -> {
		messageConverters.clear();
		messageConverters.add(new FormHttpMessageConverter());
		messageConverters.add(new OAuth2AccessTokenResponseHttpMessageConverter());
	})
	.defaultStatusHandler(new OAuth2ErrorResponseErrorHandler())
	.build();

RestClientRefreshTokenTokenResponseClient accessTokenResponseClient =
	new RestClientRefreshTokenTokenResponseClient();
accessTokenResponseClient.setRestClient(restClient);
val restClient = RestClient.builder()
	.messageConverters { messageConverters ->
		messageConverters.clear()
		messageConverters.add(FormHttpMessageConverter())
		messageConverters.add(OAuth2AccessTokenResponseHttpMessageConverter())
	}
	.defaultStatusHandler(OAuth2ErrorResponseErrorHandler())
	.build()

val accessTokenResponseClient = RestClientRefreshTokenTokenResponseClient()
accessTokenResponseClient.setRestClient(restClient)

OAuth2AccessTokenResponseHttpMessageConverter is an HttpMessageConverter for an OAuth 2.0 Access Token Response. You can customize the conversion of Token Response parameters to an OAuth2AccessTokenResponse by calling setAccessTokenResponseConverter(). The default implementation is DefaultMapOAuth2AccessTokenResponseConverter.

OAuth2ErrorResponseErrorHandler is a ResponseErrorHandler that can handle an OAuth 2.0 Error, such as 400 Bad Request. It uses an OAuth2ErrorHttpMessageConverter for converting the OAuth 2.0 Error parameters to an OAuth2Error. You can customize the conversion of Token Response parameters to an OAuth2Error by calling setErrorConverter().

Spring MVC FormHttpMessageConverter is required, as it is used when sending the OAuth 2.0 Access Token Request.

Customizing Response Parameters

The following example provides a starting point for customizing the conversion of Token Response parameters to an OAuth2AccessTokenResponse:

Customize Access Token Response Converter
  • Java

  • Kotlin

OAuth2AccessTokenResponseHttpMessageConverter accessTokenResponseMessageConverter =
	new OAuth2AccessTokenResponseHttpMessageConverter();
accessTokenResponseMessageConverter.setAccessTokenResponseConverter(parameters -> {
	// ...
	return OAuth2AccessTokenResponse.withToken("custom-token")
		// ...
		.build();
});
val accessTokenResponseMessageConverter = OAuth2AccessTokenResponseHttpMessageConverter()
accessTokenResponseMessageConverter.setAccessTokenResponseConverter { parameters ->
	// ...
	return OAuth2AccessTokenResponse.withToken("custom-token")
		// ...
		.build()
}

Customizing Error Handling

The following example provides a starting point for customizing the conversion of Error parameters to an OAuth2Error:

Customize Access Token Error Handler
  • Java

  • Kotlin

OAuth2ErrorHttpMessageConverter errorConverter =
	new OAuth2ErrorHttpMessageConverter();
errorConverter.setErrorConverter(parameters -> {
	// ...
	return new OAuth2Error("custom-error", "custom description", "custom-uri");
});

OAuth2ErrorResponseErrorHandler errorHandler =
	new OAuth2ErrorResponseErrorHandler();
errorHandler.setErrorConverter(errorConverter);
val errorConverter = OAuth2ErrorHttpMessageConverter()
errorConverter.setErrorConverter { parameters ->
	// ...
	return OAuth2Error("custom-error", "custom description", "custom-uri")
}

val errorHandler = OAuth2ErrorResponseErrorHandler()
errorHandler.setErrorConverter(errorConverter)

Customize using the Builder

Whether you customize RestClientRefreshTokenTokenResponseClient or provide your own implementation of OAuth2AccessTokenResponseClient, you can configure it using the OAuth2AuthorizedClientProviderBuilder (as an alternative to publishing a bean) as follows:

Access Token Response Configuration via Builder
  • Java

  • Kotlin

// Customize
OAuth2AccessTokenResponseClient<OAuth2RefreshTokenGrantRequest> refreshTokenTokenResponseClient = ...

OAuth2AuthorizedClientProvider authorizedClientProvider =
		OAuth2AuthorizedClientProviderBuilder.builder()
				.authorizationCode()
				.refreshToken(configurer -> configurer.accessTokenResponseClient(refreshTokenTokenResponseClient))
				.build();

// ...

authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider);
// Customize
val refreshTokenTokenResponseClient: OAuth2AccessTokenResponseClient<OAuth2RefreshTokenGrantRequest> = ...

val authorizedClientProvider = OAuth2AuthorizedClientProviderBuilder.builder()
        .authorizationCode()
        .refreshToken { it.accessTokenResponseClient(refreshTokenTokenResponseClient) }
        .build()

// ...

authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider)

OAuth2AuthorizedClientProviderBuilder.builder().refreshToken() configures a RefreshTokenOAuth2AuthorizedClientProvider, which is an implementation of an OAuth2AuthorizedClientProvider for the Refresh Token grant.

The OAuth2RefreshToken can optionally be returned in the Access Token Response for the authorization_code and password grant types. If the OAuth2AuthorizedClient.getRefreshToken() is available and the OAuth2AuthorizedClient.getAccessToken() is expired, it is automatically refreshed by the RefreshTokenOAuth2AuthorizedClientProvider.

Client Credentials

Please refer to the OAuth 2.0 Authorization Framework for further details on the Client Credentials grant.

Requesting an Access Token

See the Access Token Request/Response protocol flow for the Client Credentials grant.

There are two implementations of OAuth2AccessTokenResponseClient that can be used to make HTTP requests to the Token Endpoint in order to obtain an access token for the Client Credentials grant:

  • DefaultClientCredentialsTokenResponseClient (default)

  • RestClientClientCredentialsTokenResponseClient

The default implementation uses a RestOperations instance to exchange an authorization code for an access token at the Authorization Server’s Token Endpoint. Spring Security 6.4 introduces a new implementation based on RestClient, which provides similar functionality but is better aligned with the Reactive version of the component (based on WebClient) in order to provide consistent configuration for applications on either stack.

This section focuses on RestClientClientCredentialsTokenResponseClient. You can read about DefaultClientCredentialsTokenResponseClient in the Spring Security 6.3 documentation.

To opt-in to using RestClientClientCredentialsTokenResponseClient, simply provide a bean as in the following example and it will be picked up by the default OAuth2AuthorizedClientManager automatically:

Access Token Response Configuration
  • Java

  • Kotlin

@Bean
public OAuth2AccessTokenResponseClient<OAuth2ClientCredentialsGrantRequest> accessTokenResponseClient() {
	return new RestClientClientCredentialsTokenResponseClient();
}
@Bean
fun accessTokenResponseClient(): OAuth2AccessTokenResponseClient<Client Credentials> {
	return RestClientClientCredentialsTokenResponseClient()
}

The new implementation will be the default in Spring Security 7.

RestClientClientCredentialsTokenResponseClient is very flexible and provides several options for customizing the OAuth 2.0 Access Token request and response for the Client Credentials grant. Choose from the following use cases to learn more:

Customizing the Access Token Request

RestClientClientCredentialsTokenResponseClient provides hooks for customizing HTTP headers and request parameters of the OAuth 2.0 Access Token Request.

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
  • Java

  • Kotlin

RestClientClientCredentialsTokenResponseClient accessTokenResponseClient =
	new RestClientClientCredentialsTokenResponseClient();
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;
});
val accessTokenResponseClient = RestClientClientCredentialsTokenResponseClient()
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
  • Java

  • Kotlin

DefaultOAuth2TokenRequestHeadersConverter headersConverter =
	new DefaultOAuth2TokenRequestHeadersConverter();
headersConverter.setEncodeClientCredentials(false);

RestClientClientCredentialsTokenResponseClient accessTokenResponseClient =
	new RestClientClientCredentialsTokenResponseClient();
accessTokenResponseClient.setHeadersConverter(headersConverter);
val headersConverter = DefaultOAuth2TokenRequestHeadersConverter()
headersConverter.setEncodeClientCredentials(false)

val accessTokenResponseClient = RestClientClientCredentialsTokenResponseClient()
accessTokenResponseClient.setHeadersConverter(headersConverter)

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()

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
  • Java

  • Kotlin

RestClientClientCredentialsTokenResponseClient accessTokenResponseClient =
	new RestClientClientCredentialsTokenResponseClient();
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;
});
val accessTokenResponseClient = RestClientClientCredentialsTokenResponseClient()
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
  • Java

  • Kotlin

RestClientClientCredentialsTokenResponseClient accessTokenResponseClient =
	new RestClientClientCredentialsTokenResponseClient();
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;
});
val parametersConverter = DefaultOAuth2TokenRequestParametersConverter<OAuth2ClientCredentialsGrantRequest>()
parametersConverter.setParametersCustomizer { parameters ->
	if (parameters.containsKey(OAuth2ParameterNames.CLIENT_ASSERTION)) {
		parameters.remove(OAuth2ParameterNames.CLIENT_ID)
	}
}

val accessTokenResponseClient = RestClientClientCredentialsTokenResponseClient()
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
  • Java

  • Kotlin

RestClientClientCredentialsTokenResponseClient accessTokenResponseClient =
	new RestClientClientCredentialsTokenResponseClient();
accessTokenResponseClient.setParametersCustomizer(parameters -> {
	if (parameters.containsKey(OAuth2ParameterNames.CLIENT_ASSERTION)) {
		parameters.remove(OAuth2ParameterNames.CLIENT_ID);
	}
});
val accessTokenResponseClient = RestClientClientCredentialsTokenResponseClient()
accessTokenResponseClient.setParametersCustomizer { parameters ->
	if (parameters.containsKey(OAuth2ParameterNames.CLIENT_ASSERTION)) {
		parameters.remove(OAuth2ParameterNames.CLIENT_ID)
	}
}

Customizing the Access Token Response

RestClientClientCredentialsTokenResponseClient provides hooks for customizing response parameters and error handling of the OAuth 2.0 Access Token Response.

Customizing the WebClient

You can customize the Token Response by providing a pre-configured RestClient to setRestClient(). The default RestClient is configured as follows:

Default RestClient Configuration
  • Java

  • Kotlin

RestClient restClient = RestClient.builder()
	.messageConverters(messageConverters -> {
		messageConverters.clear();
		messageConverters.add(new FormHttpMessageConverter());
		messageConverters.add(new OAuth2AccessTokenResponseHttpMessageConverter());
	})
	.defaultStatusHandler(new OAuth2ErrorResponseErrorHandler())
	.build();

RestClientClientCredentialsTokenResponseClient accessTokenResponseClient =
	new RestClientClientCredentialsTokenResponseClient();
accessTokenResponseClient.setRestClient(restClient);
val restClient = RestClient.builder()
	.messageConverters { messageConverters ->
		messageConverters.clear()
		messageConverters.add(FormHttpMessageConverter())
		messageConverters.add(OAuth2AccessTokenResponseHttpMessageConverter())
	}
	.defaultStatusHandler(OAuth2ErrorResponseErrorHandler())
	.build()

val accessTokenResponseClient = RestClientClientCredentialsTokenResponseClient()
accessTokenResponseClient.setRestClient(restClient)

OAuth2AccessTokenResponseHttpMessageConverter is an HttpMessageConverter for an OAuth 2.0 Access Token Response. You can customize the conversion of Token Response parameters to an OAuth2AccessTokenResponse by calling setAccessTokenResponseConverter(). The default implementation is DefaultMapOAuth2AccessTokenResponseConverter.

OAuth2ErrorResponseErrorHandler is a ResponseErrorHandler that can handle an OAuth 2.0 Error, such as 400 Bad Request. It uses an OAuth2ErrorHttpMessageConverter for converting the OAuth 2.0 Error parameters to an OAuth2Error. You can customize the conversion of Token Response parameters to an OAuth2Error by calling setErrorConverter().

Spring MVC FormHttpMessageConverter is required, as it is used when sending the OAuth 2.0 Access Token Request.

Customizing Response Parameters

The following example provides a starting point for customizing the conversion of Token Response parameters to an OAuth2AccessTokenResponse:

Customize Access Token Response Converter
  • Java

  • Kotlin

OAuth2AccessTokenResponseHttpMessageConverter accessTokenResponseMessageConverter =
	new OAuth2AccessTokenResponseHttpMessageConverter();
accessTokenResponseMessageConverter.setAccessTokenResponseConverter(parameters -> {
	// ...
	return OAuth2AccessTokenResponse.withToken("custom-token")
		// ...
		.build();
});
val accessTokenResponseMessageConverter = OAuth2AccessTokenResponseHttpMessageConverter()
accessTokenResponseMessageConverter.setAccessTokenResponseConverter { parameters ->
	// ...
	return OAuth2AccessTokenResponse.withToken("custom-token")
		// ...
		.build()
}

Customizing Error Handling

The following example provides a starting point for customizing the conversion of Error parameters to an OAuth2Error:

Customize Access Token Error Handler
  • Java

  • Kotlin

OAuth2ErrorHttpMessageConverter errorConverter =
	new OAuth2ErrorHttpMessageConverter();
errorConverter.setErrorConverter(parameters -> {
	// ...
	return new OAuth2Error("custom-error", "custom description", "custom-uri");
});

OAuth2ErrorResponseErrorHandler errorHandler =
	new OAuth2ErrorResponseErrorHandler();
errorHandler.setErrorConverter(errorConverter);
val errorConverter = OAuth2ErrorHttpMessageConverter()
errorConverter.setErrorConverter { parameters ->
	// ...
	return OAuth2Error("custom-error", "custom description", "custom-uri")
}

val errorHandler = OAuth2ErrorResponseErrorHandler()
errorHandler.setErrorConverter(errorConverter)

Customize using the Builder

Whether you customize RestClientClientCredentialsTokenResponseClient or provide your own implementation of OAuth2AccessTokenResponseClient, you can configure it using the OAuth2AuthorizedClientProviderBuilder (as an alternative to publishing a bean) as follows:

Access Token Response Configuration via Builder
  • Java

  • Kotlin

// Customize
OAuth2AccessTokenResponseClient<OAuth2ClientCredentialsGrantRequest> clientCredentialsTokenResponseClient = ...

OAuth2AuthorizedClientProvider authorizedClientProvider =
		OAuth2AuthorizedClientProviderBuilder.builder()
				.clientCredentials(configurer -> configurer.accessTokenResponseClient(clientCredentialsTokenResponseClient))
				.build();

// ...

authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider);
// Customize
val clientCredentialsTokenResponseClient: OAuth2AccessTokenResponseClient<OAuth2ClientCredentialsGrantRequest> = ...

val authorizedClientProvider = OAuth2AuthorizedClientProviderBuilder.builder()
        .clientCredentials { it.accessTokenResponseClient(clientCredentialsTokenResponseClient) }
        .build()

// ...

authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider)

OAuth2AuthorizedClientProviderBuilder.builder().clientCredentials() configures a ClientCredentialsOAuth2AuthorizedClientProvider, which is an implementation of an OAuth2AuthorizedClientProvider for the Client Credentials grant.

Using the Access Token

Consider the following Spring Boot properties for an OAuth 2.0 Client registration:

spring:
  security:
    oauth2:
      client:
        registration:
          okta:
            client-id: okta-client-id
            client-secret: okta-client-secret
            authorization-grant-type: client_credentials
            scope: read, write
        provider:
          okta:
            token-uri: https://dev-1234.oktapreview.com/oauth2/v1/token

Further consider the following OAuth2AuthorizedClientManager @Bean:

  • Java

  • Kotlin

@Bean
public OAuth2AuthorizedClientManager authorizedClientManager(
		ClientRegistrationRepository clientRegistrationRepository,
		OAuth2AuthorizedClientRepository authorizedClientRepository) {

	OAuth2AuthorizedClientProvider authorizedClientProvider =
			OAuth2AuthorizedClientProviderBuilder.builder()
					.clientCredentials()
					.build();

	DefaultOAuth2AuthorizedClientManager authorizedClientManager =
			new DefaultOAuth2AuthorizedClientManager(
					clientRegistrationRepository, authorizedClientRepository);
	authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider);

	return authorizedClientManager;
}
@Bean
fun authorizedClientManager(
        clientRegistrationRepository: ClientRegistrationRepository,
        authorizedClientRepository: OAuth2AuthorizedClientRepository): OAuth2AuthorizedClientManager {
    val authorizedClientProvider = OAuth2AuthorizedClientProviderBuilder.builder()
            .clientCredentials()
            .build()
    val authorizedClientManager = DefaultOAuth2AuthorizedClientManager(
            clientRegistrationRepository, authorizedClientRepository)
    authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider)
    return authorizedClientManager
}

Given the preceding properties and bean, you can obtain the OAuth2AccessToken as follows:

  • Java

  • Kotlin

@Controller
public class OAuth2ClientController {

	@Autowired
	private OAuth2AuthorizedClientManager authorizedClientManager;

	@GetMapping("/")
	public String index(Authentication authentication,
						HttpServletRequest servletRequest,
						HttpServletResponse servletResponse) {

		OAuth2AuthorizeRequest authorizeRequest = OAuth2AuthorizeRequest.withClientRegistrationId("okta")
				.principal(authentication)
				.attributes(attrs -> {
					attrs.put(HttpServletRequest.class.getName(), servletRequest);
					attrs.put(HttpServletResponse.class.getName(), servletResponse);
				})
				.build();
		OAuth2AuthorizedClient authorizedClient = this.authorizedClientManager.authorize(authorizeRequest);

		OAuth2AccessToken accessToken = authorizedClient.getAccessToken();

		// ...

		return "index";
	}
}
class OAuth2ClientController {

    @Autowired
    private lateinit var authorizedClientManager: OAuth2AuthorizedClientManager

    @GetMapping("/")
    fun index(authentication: Authentication?,
              servletRequest: HttpServletRequest,
              servletResponse: HttpServletResponse): String {
        val authorizeRequest: OAuth2AuthorizeRequest = OAuth2AuthorizeRequest.withClientRegistrationId("okta")
                .principal(authentication)
                .attributes(Consumer { attrs: MutableMap<String, Any> ->
                    attrs[HttpServletRequest::class.java.name] = servletRequest
                    attrs[HttpServletResponse::class.java.name] = servletResponse
                })
                .build()
        val authorizedClient = authorizedClientManager.authorize(authorizeRequest)
        val accessToken: OAuth2AccessToken = authorizedClient.accessToken

        // ...

        return "index"
    }
}

HttpServletRequest and HttpServletResponse are both OPTIONAL attributes. If not provided, they default to ServletRequestAttributes by using RequestContextHolder.getRequestAttributes().

Resource Owner Password Credentials

See the OAuth 2.0 Authorization Framework for further details on the Resource Owner Password Credentials grant.

Requesting an Access Token

See the Access Token Request/Response protocol flow for the Resource Owner Password Credentials grant.

The default implementation of OAuth2AccessTokenResponseClient for the Resource Owner Password Credentials grant is DefaultPasswordTokenResponseClient, which uses a RestOperations when requesting an access token at the Authorization Server’s Token Endpoint.

The DefaultPasswordTokenResponseClient class and support for the Resource Owner Password Credentials grant are deprecated. This section will be removed in Spring Security 7.

The DefaultPasswordTokenResponseClient is flexible, as it lets you customize the pre-processing of the Token Request or post-handling of the Token Response.

Customizing the Access Token Request

If you need to customize the pre-processing of the Token Request, you can provide DefaultPasswordTokenResponseClient.setRequestEntityConverter() with a custom Converter<OAuth2PasswordGrantRequest, RequestEntity<?>>. The default implementation (OAuth2PasswordGrantRequestEntityConverter) builds a RequestEntity representation of a standard OAuth 2.0 Access Token Request. However, providing a custom Converter would let you extend the standard Token Request and add custom parameter(s).

To customize only the parameters of the request, you can provide OAuth2PasswordGrantRequestEntityConverter.setParametersConverter() with a custom Converter<OAuth2PasswordGrantRequest, MultiValueMap<String, String>> to completely override the parameters sent with the request. This is often simpler than constructing a RequestEntity directly.

If you prefer to only add additional parameters, you can provide OAuth2PasswordGrantRequestEntityConverter.addParametersConverter() with a custom Converter<OAuth2PasswordGrantRequest, MultiValueMap<String, String>> which constructs an aggregate Converter.

The custom Converter must return a valid RequestEntity representation of an OAuth 2.0 Access Token Request that is understood by the intended OAuth 2.0 Provider.

Customizing the Access Token Response

On the other end, if you need to customize the post-handling of the Token Response, you need to provide DefaultPasswordTokenResponseClient.setRestOperations() with a custom configured RestOperations. The default RestOperations is configured as follows:

  • Java

  • Kotlin

RestTemplate restTemplate = new RestTemplate(Arrays.asList(
		new FormHttpMessageConverter(),
		new OAuth2AccessTokenResponseHttpMessageConverter()));

restTemplate.setErrorHandler(new OAuth2ErrorResponseErrorHandler());
val restTemplate = RestTemplate(listOf(
        FormHttpMessageConverter(),
        OAuth2AccessTokenResponseHttpMessageConverter()))

restTemplate.errorHandler = OAuth2ErrorResponseErrorHandler()

Spring MVC FormHttpMessageConverter is required, as it is used when sending the OAuth 2.0 Access Token Request.

OAuth2AccessTokenResponseHttpMessageConverter is a HttpMessageConverter for an OAuth 2.0 Access Token Response. You can provide OAuth2AccessTokenResponseHttpMessageConverter.setTokenResponseConverter() with a custom Converter<Map<String, String>, OAuth2AccessTokenResponse> that is used to convert the OAuth 2.0 Access Token Response parameters to an OAuth2AccessTokenResponse.

OAuth2ErrorResponseErrorHandler is a ResponseErrorHandler that can handle an OAuth 2.0 Error, such as 400 Bad Request. It uses an OAuth2ErrorHttpMessageConverter to convert the OAuth 2.0 Error parameters to an OAuth2Error.

Customize using the Builder

Whether you customize DefaultPasswordTokenResponseClient or provide your own implementation of OAuth2AccessTokenResponseClient, you need to configure it as follows:

Access Token Response Configuration via Builder
  • Java

  • Kotlin

// Customize
OAuth2AccessTokenResponseClient<OAuth2PasswordGrantRequest> passwordTokenResponseClient = ...

OAuth2AuthorizedClientProvider authorizedClientProvider =
		OAuth2AuthorizedClientProviderBuilder.builder()
				.password(configurer -> configurer.accessTokenResponseClient(passwordTokenResponseClient))
				.refreshToken()
				.build();

// ...

authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider);
val passwordTokenResponseClient: OAuth2AccessTokenResponseClient<OAuth2PasswordGrantRequest> = ...

val authorizedClientProvider = OAuth2AuthorizedClientProviderBuilder.builder()
        .password { it.accessTokenResponseClient(passwordTokenResponseClient) }
        .refreshToken()
        .build()

// ...

authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider)

OAuth2AuthorizedClientProviderBuilder.builder().password() configures a PasswordOAuth2AuthorizedClientProvider, which is an implementation of an OAuth2AuthorizedClientProvider for the Resource Owner Password Credentials grant.

Using the Access Token

Consider the following Spring Boot properties for an OAuth 2.0 Client registration:

spring:
  security:
    oauth2:
      client:
        registration:
          okta:
            client-id: okta-client-id
            client-secret: okta-client-secret
            authorization-grant-type: password
            scope: read, write
        provider:
          okta:
            token-uri: https://dev-1234.oktapreview.com/oauth2/v1/token

Further consider the OAuth2AuthorizedClientManager @Bean:

  • Java

  • Kotlin

@Bean
public OAuth2AuthorizedClientManager authorizedClientManager(
		ClientRegistrationRepository clientRegistrationRepository,
		OAuth2AuthorizedClientRepository authorizedClientRepository) {

	OAuth2AuthorizedClientProvider authorizedClientProvider =
			OAuth2AuthorizedClientProviderBuilder.builder()
					.password()
					.refreshToken()
					.build();

	DefaultOAuth2AuthorizedClientManager authorizedClientManager =
			new DefaultOAuth2AuthorizedClientManager(
					clientRegistrationRepository, authorizedClientRepository);
	authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider);

	// Assuming the `username` and `password` are supplied as `HttpServletRequest` parameters,
	// map the `HttpServletRequest` parameters to `OAuth2AuthorizationContext.getAttributes()`
	authorizedClientManager.setContextAttributesMapper(contextAttributesMapper());

	return authorizedClientManager;
}

private Function<OAuth2AuthorizeRequest, Map<String, Object>> contextAttributesMapper() {
	return authorizeRequest -> {
		Map<String, Object> contextAttributes = Collections.emptyMap();
		HttpServletRequest servletRequest = authorizeRequest.getAttribute(HttpServletRequest.class.getName());
		String username = servletRequest.getParameter(OAuth2ParameterNames.USERNAME);
		String password = servletRequest.getParameter(OAuth2ParameterNames.PASSWORD);
		if (StringUtils.hasText(username) && StringUtils.hasText(password)) {
			contextAttributes = new HashMap<>();

			// `PasswordOAuth2AuthorizedClientProvider` requires both attributes
			contextAttributes.put(OAuth2AuthorizationContext.USERNAME_ATTRIBUTE_NAME, username);
			contextAttributes.put(OAuth2AuthorizationContext.PASSWORD_ATTRIBUTE_NAME, password);
		}
		return contextAttributes;
	};
}
@Bean
fun authorizedClientManager(
        clientRegistrationRepository: ClientRegistrationRepository,
        authorizedClientRepository: OAuth2AuthorizedClientRepository): OAuth2AuthorizedClientManager {
    val authorizedClientProvider = OAuth2AuthorizedClientProviderBuilder.builder()
            .password()
            .refreshToken()
            .build()
    val authorizedClientManager = DefaultOAuth2AuthorizedClientManager(
            clientRegistrationRepository, authorizedClientRepository)
    authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider)

    // Assuming the `username` and `password` are supplied as `HttpServletRequest` parameters,
    // map the `HttpServletRequest` parameters to `OAuth2AuthorizationContext.getAttributes()`
    authorizedClientManager.setContextAttributesMapper(contextAttributesMapper())
    return authorizedClientManager
}

private fun contextAttributesMapper(): Function<OAuth2AuthorizeRequest, MutableMap<String, Any>> {
    return Function { authorizeRequest ->
        var contextAttributes: MutableMap<String, Any> = mutableMapOf()
        val servletRequest: HttpServletRequest = authorizeRequest.getAttribute(HttpServletRequest::class.java.name)
        val username = servletRequest.getParameter(OAuth2ParameterNames.USERNAME)
        val password = servletRequest.getParameter(OAuth2ParameterNames.PASSWORD)
        if (StringUtils.hasText(username) && StringUtils.hasText(password)) {
            contextAttributes = hashMapOf()

            // `PasswordOAuth2AuthorizedClientProvider` requires both attributes
            contextAttributes[OAuth2AuthorizationContext.USERNAME_ATTRIBUTE_NAME] = username
            contextAttributes[OAuth2AuthorizationContext.PASSWORD_ATTRIBUTE_NAME] = password
        }
        contextAttributes
    }
}

Given the preceding properties and bean, you can obtain the OAuth2AccessToken as follows:

  • Java

  • Kotlin

@Controller
public class OAuth2ClientController {

	@Autowired
	private OAuth2AuthorizedClientManager authorizedClientManager;

	@GetMapping("/")
	public String index(Authentication authentication,
						HttpServletRequest servletRequest,
						HttpServletResponse servletResponse) {

		OAuth2AuthorizeRequest authorizeRequest = OAuth2AuthorizeRequest.withClientRegistrationId("okta")
				.principal(authentication)
				.attributes(attrs -> {
					attrs.put(HttpServletRequest.class.getName(), servletRequest);
					attrs.put(HttpServletResponse.class.getName(), servletResponse);
				})
				.build();
		OAuth2AuthorizedClient authorizedClient = this.authorizedClientManager.authorize(authorizeRequest);

		OAuth2AccessToken accessToken = authorizedClient.getAccessToken();

		// ...

		return "index";
	}
}
@Controller
class OAuth2ClientController {
    @Autowired
    private lateinit var authorizedClientManager: OAuth2AuthorizedClientManager

    @GetMapping("/")
    fun index(authentication: Authentication?,
              servletRequest: HttpServletRequest,
              servletResponse: HttpServletResponse): String {
        val authorizeRequest: OAuth2AuthorizeRequest = OAuth2AuthorizeRequest.withClientRegistrationId("okta")
                .principal(authentication)
                .attributes(Consumer {
                    it[HttpServletRequest::class.java.name] = servletRequest
                    it[HttpServletResponse::class.java.name] = servletResponse
                })
                .build()
        val authorizedClient = authorizedClientManager.authorize(authorizeRequest)
        val accessToken: OAuth2AccessToken = authorizedClient.accessToken

        // ...

        return "index"
    }
}

HttpServletRequest and HttpServletResponse are both OPTIONAL attributes. If not provided, they default to ServletRequestAttributes using RequestContextHolder.getRequestAttributes().

JWT Bearer

Please refer to JSON Web Token (JWT) Profile for OAuth 2.0 Client Authentication and Authorization Grants for further details on the JWT Bearer grant.

Requesting an Access Token

Please refer to the Access Token Request/Response protocol flow for the JWT Bearer grant.

There are two implementations of OAuth2AccessTokenResponseClient that can be used to make HTTP requests to the Token Endpoint in order to obtain an access token for the JWT Bearer grant:

  • DefaultJwtBearerTokenResponseClient (default)

  • RestClientJwtBearerTokenResponseClient

The default implementation uses a RestOperations instance to exchange an authorization code for an access token at the Authorization Server’s Token Endpoint. Spring Security 6.4 introduces a new implementation based on RestClient, which provides similar functionality but is better aligned with the Reactive version of the component (based on WebClient) in order to provide consistent configuration for applications on either stack.

This section focuses on RestClientJwtBearerTokenResponseClient. You can read about DefaultClientCredentialsTokenResponseClient in the Spring Security 6.3 documentation.

To opt-in to using RestClientJwtBearerTokenResponseClient, simply provide a bean as in the following example and it will be picked up by the default OAuth2AuthorizedClientManager automatically:

Access Token Response Configuration
  • Java

  • Kotlin

@Bean
public OAuth2AccessTokenResponseClient<JwtBearerGrantRequest> accessTokenResponseClient() {
	return new RestClientJwtBearerTokenResponseClient();
}
@Bean
fun accessTokenResponseClient(): OAuth2AccessTokenResponseClient<JWT Bearer> {
	return RestClientJwtBearerTokenResponseClient()
}

The new implementation will be the default in Spring Security 7.

RestClientJwtBearerTokenResponseClient is very flexible and provides several options for customizing the OAuth 2.0 Access Token request and response for the JWT Bearer grant. Choose from the following use cases to learn more:

Customizing the Access Token Request

RestClientJwtBearerTokenResponseClient provides hooks for customizing HTTP headers and request parameters of the OAuth 2.0 Access Token Request.

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
  • Java

  • Kotlin

RestClientJwtBearerTokenResponseClient accessTokenResponseClient =
	new RestClientJwtBearerTokenResponseClient();
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;
});
val accessTokenResponseClient = RestClientJwtBearerTokenResponseClient()
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
  • Java

  • Kotlin

DefaultOAuth2TokenRequestHeadersConverter headersConverter =
	new DefaultOAuth2TokenRequestHeadersConverter();
headersConverter.setEncodeClientCredentials(false);

RestClientJwtBearerTokenResponseClient accessTokenResponseClient =
	new RestClientJwtBearerTokenResponseClient();
accessTokenResponseClient.setHeadersConverter(headersConverter);
val headersConverter = DefaultOAuth2TokenRequestHeadersConverter()
headersConverter.setEncodeClientCredentials(false)

val accessTokenResponseClient = RestClientJwtBearerTokenResponseClient()
accessTokenResponseClient.setHeadersConverter(headersConverter)

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()

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
  • Java

  • Kotlin

RestClientJwtBearerTokenResponseClient accessTokenResponseClient =
	new RestClientJwtBearerTokenResponseClient();
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;
});
val accessTokenResponseClient = RestClientJwtBearerTokenResponseClient()
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
  • Java

  • Kotlin

RestClientJwtBearerTokenResponseClient accessTokenResponseClient =
	new RestClientJwtBearerTokenResponseClient();
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;
});
val parametersConverter = DefaultOAuth2TokenRequestParametersConverter<JwtBearerGrantRequest>()
parametersConverter.setParametersCustomizer { parameters ->
	if (parameters.containsKey(OAuth2ParameterNames.CLIENT_ASSERTION)) {
		parameters.remove(OAuth2ParameterNames.CLIENT_ID)
	}
}

val accessTokenResponseClient = RestClientJwtBearerTokenResponseClient()
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
  • Java

  • Kotlin

RestClientJwtBearerTokenResponseClient accessTokenResponseClient =
	new RestClientJwtBearerTokenResponseClient();
accessTokenResponseClient.setParametersCustomizer(parameters -> {
	if (parameters.containsKey(OAuth2ParameterNames.CLIENT_ASSERTION)) {
		parameters.remove(OAuth2ParameterNames.CLIENT_ID);
	}
});
val accessTokenResponseClient = RestClientJwtBearerTokenResponseClient()
accessTokenResponseClient.setParametersCustomizer { parameters ->
	if (parameters.containsKey(OAuth2ParameterNames.CLIENT_ASSERTION)) {
		parameters.remove(OAuth2ParameterNames.CLIENT_ID)
	}
}

Customizing the Access Token Response

RestClientJwtBearerTokenResponseClient provides hooks for customizing response parameters and error handling of the OAuth 2.0 Access Token Response.

Customizing the WebClient

You can customize the Token Response by providing a pre-configured RestClient to setRestClient(). The default RestClient is configured as follows:

Default RestClient Configuration
  • Java

  • Kotlin

RestClient restClient = RestClient.builder()
	.messageConverters(messageConverters -> {
		messageConverters.clear();
		messageConverters.add(new FormHttpMessageConverter());
		messageConverters.add(new OAuth2AccessTokenResponseHttpMessageConverter());
	})
	.defaultStatusHandler(new OAuth2ErrorResponseErrorHandler())
	.build();

RestClientJwtBearerTokenResponseClient accessTokenResponseClient =
	new RestClientJwtBearerTokenResponseClient();
accessTokenResponseClient.setRestClient(restClient);
val restClient = RestClient.builder()
	.messageConverters { messageConverters ->
		messageConverters.clear()
		messageConverters.add(FormHttpMessageConverter())
		messageConverters.add(OAuth2AccessTokenResponseHttpMessageConverter())
	}
	.defaultStatusHandler(OAuth2ErrorResponseErrorHandler())
	.build()

val accessTokenResponseClient = RestClientJwtBearerTokenResponseClient()
accessTokenResponseClient.setRestClient(restClient)

OAuth2AccessTokenResponseHttpMessageConverter is an HttpMessageConverter for an OAuth 2.0 Access Token Response. You can customize the conversion of Token Response parameters to an OAuth2AccessTokenResponse by calling setAccessTokenResponseConverter(). The default implementation is DefaultMapOAuth2AccessTokenResponseConverter.

OAuth2ErrorResponseErrorHandler is a ResponseErrorHandler that can handle an OAuth 2.0 Error, such as 400 Bad Request. It uses an OAuth2ErrorHttpMessageConverter for converting the OAuth 2.0 Error parameters to an OAuth2Error. You can customize the conversion of Token Response parameters to an OAuth2Error by calling setErrorConverter().

Spring MVC FormHttpMessageConverter is required, as it is used when sending the OAuth 2.0 Access Token Request.

Customizing Response Parameters

The following example provides a starting point for customizing the conversion of Token Response parameters to an OAuth2AccessTokenResponse:

Customize Access Token Response Converter
  • Java

  • Kotlin

OAuth2AccessTokenResponseHttpMessageConverter accessTokenResponseMessageConverter =
	new OAuth2AccessTokenResponseHttpMessageConverter();
accessTokenResponseMessageConverter.setAccessTokenResponseConverter(parameters -> {
	// ...
	return OAuth2AccessTokenResponse.withToken("custom-token")
		// ...
		.build();
});
val accessTokenResponseMessageConverter = OAuth2AccessTokenResponseHttpMessageConverter()
accessTokenResponseMessageConverter.setAccessTokenResponseConverter { parameters ->
	// ...
	return OAuth2AccessTokenResponse.withToken("custom-token")
		// ...
		.build()
}

Customizing Error Handling

The following example provides a starting point for customizing the conversion of Error parameters to an OAuth2Error:

Customize Access Token Error Handler
  • Java

  • Kotlin

OAuth2ErrorHttpMessageConverter errorConverter =
	new OAuth2ErrorHttpMessageConverter();
errorConverter.setErrorConverter(parameters -> {
	// ...
	return new OAuth2Error("custom-error", "custom description", "custom-uri");
});

OAuth2ErrorResponseErrorHandler errorHandler =
	new OAuth2ErrorResponseErrorHandler();
errorHandler.setErrorConverter(errorConverter);
val errorConverter = OAuth2ErrorHttpMessageConverter()
errorConverter.setErrorConverter { parameters ->
	// ...
	return OAuth2Error("custom-error", "custom description", "custom-uri")
}

val errorHandler = OAuth2ErrorResponseErrorHandler()
errorHandler.setErrorConverter(errorConverter)

Customize using the Builder

Whether you customize RestClientJwtBearerTokenResponseClient or provide your own implementation of OAuth2AccessTokenResponseClient, you can configure it using the OAuth2AuthorizedClientProviderBuilder (as an alternative to publishing a bean) as follows:

Access Token Response Configuration via Builder
  • Java

  • Kotlin

// Customize
OAuth2AccessTokenResponseClient<JwtBearerGrantRequest> jwtBearerTokenResponseClient = ...

JwtBearerOAuth2AuthorizedClientProvider jwtBearerAuthorizedClientProvider = new JwtBearerOAuth2AuthorizedClientProvider();
jwtBearerAuthorizedClientProvider.setAccessTokenResponseClient(jwtBearerTokenResponseClient);

OAuth2AuthorizedClientProvider authorizedClientProvider =
		OAuth2AuthorizedClientProviderBuilder.builder()
				.provider(jwtBearerAuthorizedClientProvider)
				.build();

// ...

authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider);
// Customize
val jwtBearerTokenResponseClient: OAuth2AccessTokenResponseClient<JwtBearerGrantRequest> = ...

val jwtBearerAuthorizedClientProvider = JwtBearerOAuth2AuthorizedClientProvider()
jwtBearerAuthorizedClientProvider.setAccessTokenResponseClient(jwtBearerTokenResponseClient)

val authorizedClientProvider = OAuth2AuthorizedClientProviderBuilder.builder()
        .provider(jwtBearerAuthorizedClientProvider)
        .build()

// ...

authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider)

Using the Access Token

Given the following Spring Boot properties for an OAuth 2.0 Client registration:

spring:
  security:
    oauth2:
      client:
        registration:
          okta:
            client-id: okta-client-id
            client-secret: okta-client-secret
            authorization-grant-type: urn:ietf:params:oauth:grant-type:jwt-bearer
            scope: read
        provider:
          okta:
            token-uri: https://dev-1234.oktapreview.com/oauth2/v1/token

…​and the OAuth2AuthorizedClientManager @Bean:

  • Java

  • Kotlin

@Bean
public OAuth2AuthorizedClientManager authorizedClientManager(
		ClientRegistrationRepository clientRegistrationRepository,
		OAuth2AuthorizedClientRepository authorizedClientRepository) {

	JwtBearerOAuth2AuthorizedClientProvider jwtBearerAuthorizedClientProvider =
			new JwtBearerOAuth2AuthorizedClientProvider();

	OAuth2AuthorizedClientProvider authorizedClientProvider =
			OAuth2AuthorizedClientProviderBuilder.builder()
					.provider(jwtBearerAuthorizedClientProvider)
					.build();

	DefaultOAuth2AuthorizedClientManager authorizedClientManager =
			new DefaultOAuth2AuthorizedClientManager(
					clientRegistrationRepository, authorizedClientRepository);
	authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider);

	return authorizedClientManager;
}
@Bean
fun authorizedClientManager(
        clientRegistrationRepository: ClientRegistrationRepository,
        authorizedClientRepository: OAuth2AuthorizedClientRepository): OAuth2AuthorizedClientManager {
    val jwtBearerAuthorizedClientProvider = JwtBearerOAuth2AuthorizedClientProvider()
    val authorizedClientProvider = OAuth2AuthorizedClientProviderBuilder.builder()
            .provider(jwtBearerAuthorizedClientProvider)
            .build()
    val authorizedClientManager = DefaultOAuth2AuthorizedClientManager(
            clientRegistrationRepository, authorizedClientRepository)
    authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider)
    return authorizedClientManager
}

You may obtain the OAuth2AccessToken as follows:

  • Java

  • Kotlin

@RestController
public class OAuth2ResourceServerController {

	@Autowired
	private OAuth2AuthorizedClientManager authorizedClientManager;

	@GetMapping("/resource")
	public String resource(JwtAuthenticationToken jwtAuthentication) {
		OAuth2AuthorizeRequest authorizeRequest = OAuth2AuthorizeRequest.withClientRegistrationId("okta")
				.principal(jwtAuthentication)
				.build();
		OAuth2AuthorizedClient authorizedClient = this.authorizedClientManager.authorize(authorizeRequest);
		OAuth2AccessToken accessToken = authorizedClient.getAccessToken();

		// ...

	}
}
class OAuth2ResourceServerController {

    @Autowired
    private lateinit var authorizedClientManager: OAuth2AuthorizedClientManager

    @GetMapping("/resource")
    fun resource(jwtAuthentication: JwtAuthenticationToken?): String {
        val authorizeRequest: OAuth2AuthorizeRequest = OAuth2AuthorizeRequest.withClientRegistrationId("okta")
                .principal(jwtAuthentication)
                .build()
        val authorizedClient = authorizedClientManager.authorize(authorizeRequest)
        val accessToken: OAuth2AccessToken = authorizedClient.accessToken

        // ...

    }
}

JwtBearerOAuth2AuthorizedClientProvider resolves the Jwt assertion via OAuth2AuthorizationContext.getPrincipal().getPrincipal() by default, hence the use of JwtAuthenticationToken in the preceding example.

If you need to resolve the Jwt assertion from a different source, you can provide JwtBearerOAuth2AuthorizedClientProvider.setJwtAssertionResolver() with a custom Function<OAuth2AuthorizationContext, Jwt>.

Token Exchange

Please refer to OAuth 2.0 Token Exchange for further details on the Token Exchange grant.

Requesting an Access Token

Please refer to the Token Exchange Request and Response protocol flow for the Token Exchange grant.

There are two implementations of OAuth2AccessTokenResponseClient that can be used to make HTTP requests to the Token Endpoint in order to obtain an access token for the Token Exchange grant:

  • DefaultTokenExchangeTokenResponseClient (default)

  • RestClientTokenExchangeTokenResponseClient

The default implementation uses a RestOperations instance to exchange an authorization code for an access token at the Authorization Server’s Token Endpoint. Spring Security 6.4 introduces a new implementation based on RestClient, which provides similar functionality but is better aligned with the Reactive version of the component (based on WebClient) in order to provide consistent configuration for applications on either stack.

This section focuses on RestClientTokenExchangeTokenResponseClient. You can read about DefaultTokenExchangeTokenResponseClient in the Spring Security 6.3 documentation.

To opt-in to using RestClientTokenExchangeTokenResponseClient, simply provide a bean as in the following example and it will be picked up by the default OAuth2AuthorizedClientManager automatically:

Access Token Response Configuration
  • Java

  • Kotlin

@Bean
public OAuth2AccessTokenResponseClient<TokenExchangeGrantRequest> accessTokenResponseClient() {
	return new RestClientTokenExchangeTokenResponseClient();
}
@Bean
fun accessTokenResponseClient(): OAuth2AccessTokenResponseClient<Token Exchange> {
	return RestClientTokenExchangeTokenResponseClient()
}

The new implementation will be the default in Spring Security 7.

RestClientTokenExchangeTokenResponseClient is very flexible and provides several options for customizing the OAuth 2.0 Access Token request and response for the Token Exchange grant. Choose from the following use cases to learn more:

Customizing the Access Token Request

RestClientTokenExchangeTokenResponseClient provides hooks for customizing HTTP headers and request parameters of the OAuth 2.0 Access Token Request.

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
  • Java

  • Kotlin

RestClientTokenExchangeTokenResponseClient accessTokenResponseClient =
	new RestClientTokenExchangeTokenResponseClient();
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;
});
val accessTokenResponseClient = RestClientTokenExchangeTokenResponseClient()
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
  • Java

  • Kotlin

DefaultOAuth2TokenRequestHeadersConverter headersConverter =
	new DefaultOAuth2TokenRequestHeadersConverter();
headersConverter.setEncodeClientCredentials(false);

RestClientTokenExchangeTokenResponseClient accessTokenResponseClient =
	new RestClientTokenExchangeTokenResponseClient();
accessTokenResponseClient.setHeadersConverter(headersConverter);
val headersConverter = DefaultOAuth2TokenRequestHeadersConverter()
headersConverter.setEncodeClientCredentials(false)

val accessTokenResponseClient = RestClientTokenExchangeTokenResponseClient()
accessTokenResponseClient.setHeadersConverter(headersConverter)

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()

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
  • Java

  • Kotlin

RestClientTokenExchangeTokenResponseClient accessTokenResponseClient =
	new RestClientTokenExchangeTokenResponseClient();
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;
});
val accessTokenResponseClient = RestClientTokenExchangeTokenResponseClient()
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
  • Java

  • Kotlin

RestClientTokenExchangeTokenResponseClient accessTokenResponseClient =
	new RestClientTokenExchangeTokenResponseClient();
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;
});
val parametersConverter = DefaultOAuth2TokenRequestParametersConverter<TokenExchangeGrantRequest>()
parametersConverter.setParametersCustomizer { parameters ->
	if (parameters.containsKey(OAuth2ParameterNames.CLIENT_ASSERTION)) {
		parameters.remove(OAuth2ParameterNames.CLIENT_ID)
	}
}

val accessTokenResponseClient = RestClientTokenExchangeTokenResponseClient()
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
  • Java

  • Kotlin

RestClientTokenExchangeTokenResponseClient accessTokenResponseClient =
	new RestClientTokenExchangeTokenResponseClient();
accessTokenResponseClient.setParametersCustomizer(parameters -> {
	if (parameters.containsKey(OAuth2ParameterNames.CLIENT_ASSERTION)) {
		parameters.remove(OAuth2ParameterNames.CLIENT_ID);
	}
});
val accessTokenResponseClient = RestClientTokenExchangeTokenResponseClient()
accessTokenResponseClient.setParametersCustomizer { parameters ->
	if (parameters.containsKey(OAuth2ParameterNames.CLIENT_ASSERTION)) {
		parameters.remove(OAuth2ParameterNames.CLIENT_ID)
	}
}

Customizing the Access Token Response

RestClientTokenExchangeTokenResponseClient provides hooks for customizing response parameters and error handling of the OAuth 2.0 Access Token Response.

Customizing the WebClient

You can customize the Token Response by providing a pre-configured RestClient to setRestClient(). The default RestClient is configured as follows:

Default RestClient Configuration
  • Java

  • Kotlin

RestClient restClient = RestClient.builder()
	.messageConverters(messageConverters -> {
		messageConverters.clear();
		messageConverters.add(new FormHttpMessageConverter());
		messageConverters.add(new OAuth2AccessTokenResponseHttpMessageConverter());
	})
	.defaultStatusHandler(new OAuth2ErrorResponseErrorHandler())
	.build();

RestClientTokenExchangeTokenResponseClient accessTokenResponseClient =
	new RestClientTokenExchangeTokenResponseClient();
accessTokenResponseClient.setRestClient(restClient);
val restClient = RestClient.builder()
	.messageConverters { messageConverters ->
		messageConverters.clear()
		messageConverters.add(FormHttpMessageConverter())
		messageConverters.add(OAuth2AccessTokenResponseHttpMessageConverter())
	}
	.defaultStatusHandler(OAuth2ErrorResponseErrorHandler())
	.build()

val accessTokenResponseClient = RestClientTokenExchangeTokenResponseClient()
accessTokenResponseClient.setRestClient(restClient)

OAuth2AccessTokenResponseHttpMessageConverter is an HttpMessageConverter for an OAuth 2.0 Access Token Response. You can customize the conversion of Token Response parameters to an OAuth2AccessTokenResponse by calling setAccessTokenResponseConverter(). The default implementation is DefaultMapOAuth2AccessTokenResponseConverter.

OAuth2ErrorResponseErrorHandler is a ResponseErrorHandler that can handle an OAuth 2.0 Error, such as 400 Bad Request. It uses an OAuth2ErrorHttpMessageConverter for converting the OAuth 2.0 Error parameters to an OAuth2Error. You can customize the conversion of Token Response parameters to an OAuth2Error by calling setErrorConverter().

Spring MVC FormHttpMessageConverter is required, as it is used when sending the OAuth 2.0 Access Token Request.

Customizing Response Parameters

The following example provides a starting point for customizing the conversion of Token Response parameters to an OAuth2AccessTokenResponse:

Customize Access Token Response Converter
  • Java

  • Kotlin

OAuth2AccessTokenResponseHttpMessageConverter accessTokenResponseMessageConverter =
	new OAuth2AccessTokenResponseHttpMessageConverter();
accessTokenResponseMessageConverter.setAccessTokenResponseConverter(parameters -> {
	// ...
	return OAuth2AccessTokenResponse.withToken("custom-token")
		// ...
		.build();
});
val accessTokenResponseMessageConverter = OAuth2AccessTokenResponseHttpMessageConverter()
accessTokenResponseMessageConverter.setAccessTokenResponseConverter { parameters ->
	// ...
	return OAuth2AccessTokenResponse.withToken("custom-token")
		// ...
		.build()
}

Customizing Error Handling

The following example provides a starting point for customizing the conversion of Error parameters to an OAuth2Error:

Customize Access Token Error Handler
  • Java

  • Kotlin

OAuth2ErrorHttpMessageConverter errorConverter =
	new OAuth2ErrorHttpMessageConverter();
errorConverter.setErrorConverter(parameters -> {
	// ...
	return new OAuth2Error("custom-error", "custom description", "custom-uri");
});

OAuth2ErrorResponseErrorHandler errorHandler =
	new OAuth2ErrorResponseErrorHandler();
errorHandler.setErrorConverter(errorConverter);
val errorConverter = OAuth2ErrorHttpMessageConverter()
errorConverter.setErrorConverter { parameters ->
	// ...
	return OAuth2Error("custom-error", "custom description", "custom-uri")
}

val errorHandler = OAuth2ErrorResponseErrorHandler()
errorHandler.setErrorConverter(errorConverter)

Customize using the Builder

Whether you customize RestClientTokenExchangeTokenResponseClient or provide your own implementation of OAuth2AccessTokenResponseClient, you can configure it using the OAuth2AuthorizedClientProviderBuilder (as an alternative to publishing a bean) as follows:

Access Token Response Configuration via Builder
  • Java

  • Kotlin

// Customize
OAuth2AccessTokenResponseClient<TokenExchangeGrantRequest> tokenExchangeTokenResponseClient = ...

TokenExchangeOAuth2AuthorizedClientProvider tokenExchangeAuthorizedClientProvider = new TokenExchangeOAuth2AuthorizedClientProvider();
tokenExchangeAuthorizedClientProvider.setAccessTokenResponseClient(tokenExchangeTokenResponseClient);

OAuth2AuthorizedClientProvider authorizedClientProvider =
		OAuth2AuthorizedClientProviderBuilder.builder()
				.provider(tokenExchangeAuthorizedClientProvider)
				.build();

// ...

authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider);
// Customize
val tokenExchangeTokenResponseClient: OAuth2AccessTokenResponseClient<TokenExchangeGrantRequest> = ...

val tokenExchangeAuthorizedClientProvider = TokenExchangeOAuth2AuthorizedClientProvider()
tokenExchangeAuthorizedClientProvider.setAccessTokenResponseClient(tokenExchangeTokenResponseClient)

val authorizedClientProvider = OAuth2AuthorizedClientProviderBuilder.builder()
        .provider(tokenExchangeAuthorizedClientProvider)
        .build()

// ...

authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider)

Using the Access Token

Given the following Spring Boot properties for an OAuth 2.0 Client registration:

spring:
  security:
    oauth2:
      client:
        registration:
          okta:
            client-id: okta-client-id
            client-secret: okta-client-secret
            authorization-grant-type: urn:ietf:params:oauth:grant-type:token-exchange
            scope: read
        provider:
          okta:
            token-uri: https://dev-1234.oktapreview.com/oauth2/v1/token

…​and the OAuth2AuthorizedClientManager @Bean:

  • Java

  • Kotlin

@Bean
public OAuth2AuthorizedClientManager authorizedClientManager(
		ClientRegistrationRepository clientRegistrationRepository,
		OAuth2AuthorizedClientRepository authorizedClientRepository) {

	TokenExchangeOAuth2AuthorizedClientProvider tokenExchangeAuthorizedClientProvider =
			new TokenExchangeOAuth2AuthorizedClientProvider();

	OAuth2AuthorizedClientProvider authorizedClientProvider =
			OAuth2AuthorizedClientProviderBuilder.builder()
					.provider(tokenExchangeAuthorizedClientProvider)
					.build();

	DefaultOAuth2AuthorizedClientManager authorizedClientManager =
			new DefaultOAuth2AuthorizedClientManager(
					clientRegistrationRepository, authorizedClientRepository);
	authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider);

	return authorizedClientManager;
}
@Bean
fun authorizedClientManager(
        clientRegistrationRepository: ClientRegistrationRepository,
        authorizedClientRepository: OAuth2AuthorizedClientRepository): OAuth2AuthorizedClientManager {
    val tokenExchangeAuthorizedClientProvider = TokenExchangeOAuth2AuthorizedClientProvider()
    val authorizedClientProvider = OAuth2AuthorizedClientProviderBuilder.builder()
            .provider(tokenExchangeAuthorizedClientProvider)
            .build()
    val authorizedClientManager = DefaultOAuth2AuthorizedClientManager(
            clientRegistrationRepository, authorizedClientRepository)
    authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider)
    return authorizedClientManager
}

You may obtain the OAuth2AccessToken as follows:

  • Java

  • Kotlin

@RestController
public class OAuth2ResourceServerController {

	@Autowired
	private OAuth2AuthorizedClientManager authorizedClientManager;

	@GetMapping("/resource")
	public String resource(JwtAuthenticationToken jwtAuthentication) {
		OAuth2AuthorizeRequest authorizeRequest = OAuth2AuthorizeRequest.withClientRegistrationId("okta")
				.principal(jwtAuthentication)
				.build();
		OAuth2AuthorizedClient authorizedClient = this.authorizedClientManager.authorize(authorizeRequest);
		OAuth2AccessToken accessToken = authorizedClient.getAccessToken();

		// ...

	}
}
class OAuth2ResourceServerController {

    @Autowired
    private lateinit var authorizedClientManager: OAuth2AuthorizedClientManager

    @GetMapping("/resource")
    fun resource(jwtAuthentication: JwtAuthenticationToken?): String {
        val authorizeRequest: OAuth2AuthorizeRequest = OAuth2AuthorizeRequest.withClientRegistrationId("okta")
                .principal(jwtAuthentication)
                .build()
        val authorizedClient = authorizedClientManager.authorize(authorizeRequest)
        val accessToken: OAuth2AccessToken = authorizedClient.accessToken

        // ...

    }
}

TokenExchangeOAuth2AuthorizedClientProvider resolves the subject token (as an OAuth2Token) via OAuth2AuthorizationContext.getPrincipal().getPrincipal() by default, hence the use of JwtAuthenticationToken in the preceding example. An actor token is not resolved by default.

If you need to resolve the subject token from a different source, you can provide TokenExchangeOAuth2AuthorizedClientProvider.setSubjectTokenResolver() with a custom Function<OAuth2AuthorizationContext, OAuth2Token>.

If you need to resolve an actor token, you can provide TokenExchangeOAuth2AuthorizedClientProvider.setActorTokenResolver() with a custom Function<OAuth2AuthorizationContext, OAuth2Token>.