| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346 | [[oauth2Client-additional-features]]= Authorized Client FeaturesThis section covers additional features provided by Spring Security for the OAuth2 client.[[oauth2Client-registered-authorized-client]]== Resolving an Authorized ClientThe `@RegisteredOAuth2AuthorizedClient` annotation provides the ability to resolve a method parameter to an argument value of type `OAuth2AuthorizedClient`.This is a convenient alternative compared to accessing the `OAuth2AuthorizedClient` by using the `OAuth2AuthorizedClientManager` or `OAuth2AuthorizedClientService`.The following example shows how to use `@RegisteredOAuth2AuthorizedClient`:[tabs]======Java::+[source,java,role="primary"]----@Controllerpublic class OAuth2ClientController {	@GetMapping("/")	public String index(@RegisteredOAuth2AuthorizedClient("okta") OAuth2AuthorizedClient authorizedClient) {		OAuth2AccessToken accessToken = authorizedClient.getAccessToken();		...		return "index";	}}----Kotlin::+[source,kotlin,role="secondary"]----@Controllerclass OAuth2ClientController {    @GetMapping("/")    fun index(@RegisteredOAuth2AuthorizedClient("okta") authorizedClient: OAuth2AuthorizedClient): String {        val accessToken = authorizedClient.accessToken        ...        return "index"    }}----======The `@RegisteredOAuth2AuthorizedClient` annotation is handled by `OAuth2AuthorizedClientArgumentResolver`, which directly uses an xref:servlet/oauth2/client/core.adoc#oauth2Client-authorized-manager-provider[`OAuth2AuthorizedClientManager`] and, therefore, inherits its capabilities.[[oauth2Client-webclient-servlet]]== WebClient Integration for Servlet EnvironmentsThe OAuth 2.0 Client support integrates with `WebClient` by using an `ExchangeFilterFunction`.The `ServletOAuth2AuthorizedClientExchangeFilterFunction` provides a mechanism for requesting protected resources by using an `OAuth2AuthorizedClient` and including the associated `OAuth2AccessToken` as a Bearer Token.It directly uses an xref:servlet/oauth2/client/core.adoc#oauth2Client-authorized-manager-provider[`OAuth2AuthorizedClientManager`] and, therefore, inherits the following capabilities:* An `OAuth2AccessToken` is requested if the client has not yet been authorized.** `authorization_code`: Triggers the Authorization Request redirect to initiate the flow.** `client_credentials`: The access token is obtained directly from the Token Endpoint.** `password`: The access token is obtained directly from the Token Endpoint.* If the `OAuth2AccessToken` is expired, it is refreshed (or renewed) if an `OAuth2AuthorizedClientProvider` is available to perform the authorizationThe following code shows an example of how to configure `WebClient` with OAuth 2.0 Client support:[tabs]======Java::+[source,java,role="primary"]----@BeanWebClient webClient(OAuth2AuthorizedClientManager authorizedClientManager) {	ServletOAuth2AuthorizedClientExchangeFilterFunction oauth2Client =			new ServletOAuth2AuthorizedClientExchangeFilterFunction(authorizedClientManager);	return WebClient.builder()			.apply(oauth2Client.oauth2Configuration())			.build();}----Kotlin::+[source,kotlin,role="secondary"]----@Beanfun webClient(authorizedClientManager: OAuth2AuthorizedClientManager?): WebClient {    val oauth2Client = ServletOAuth2AuthorizedClientExchangeFilterFunction(authorizedClientManager)    return WebClient.builder()            .apply(oauth2Client.oauth2Configuration())            .build()}----========= Providing the Authorized ClientThe `ServletOAuth2AuthorizedClientExchangeFilterFunction` determines the client to use (for a request) by resolving the `OAuth2AuthorizedClient` from the `ClientRequest.attributes()` (request attributes).The following code shows how to set an `OAuth2AuthorizedClient` as a request attribute:[tabs]======Java::+[source,java,role="primary"]----@GetMapping("/")public String index(@RegisteredOAuth2AuthorizedClient("okta") OAuth2AuthorizedClient authorizedClient) {	String resourceUri = ...	String body = webClient			.get()			.uri(resourceUri)			.attributes(oauth2AuthorizedClient(authorizedClient))   <1>			.retrieve()			.bodyToMono(String.class)			.block();	...	return "index";}----Kotlin::+[source,kotlin,role="secondary"]----@GetMapping("/")fun index(@RegisteredOAuth2AuthorizedClient("okta") authorizedClient: OAuth2AuthorizedClient): String {    val resourceUri: String = ...    val body: String = webClient            .get()            .uri(resourceUri)            .attributes(oauth2AuthorizedClient(authorizedClient)) <1>            .retrieve()            .bodyToMono()            .block()    ...    return "index"}----======<1> `oauth2AuthorizedClient()` is a `static` method in `ServletOAuth2AuthorizedClientExchangeFilterFunction`.The following code shows how to set the `ClientRegistration.getRegistrationId()` as a request attribute:[tabs]======Java::+[source,java,role="primary"]----@GetMapping("/")public String index() {	String resourceUri = ...	String body = webClient			.get()			.uri(resourceUri)			.attributes(clientRegistrationId("okta"))   <1>			.retrieve()			.bodyToMono(String.class)			.block();	...	return "index";}----Kotlin::+[source,kotlin,role="secondary"]----@GetMapping("/")fun index(): String {    val resourceUri: String = ...    val body: String = webClient            .get()            .uri(resourceUri)            .attributes(clientRegistrationId("okta"))  <1>            .retrieve()            .bodyToMono()            .block()    ...    return "index"}----======<1> `clientRegistrationId()` is a `static` method in `ServletOAuth2AuthorizedClientExchangeFilterFunction`.The following code shows how to set an `Authentication` as a request attribute:[tabs]======Java::+[source,java,role="primary"]----@GetMapping("/")public String index() {	String resourceUri = ...	Authentication anonymousAuthentication = new AnonymousAuthenticationToken(			"anonymous", "anonymousUser", AuthorityUtils.createAuthorityList("ROLE_ANONYMOUS"));	String body = webClient			.get()			.uri(resourceUri)			.attributes(authentication(anonymousAuthentication))   <1>			.retrieve()			.bodyToMono(String.class)			.block();	...	return "index";}----Kotlin::+[source,kotlin,role="secondary"]----@GetMapping("/")fun index(): String {    val resourceUri: String = ...    val anonymousAuthentication: Authentication = AnonymousAuthenticationToken(            "anonymous", "anonymousUser", AuthorityUtils.createAuthorityList("ROLE_ANONYMOUS"))    val body: String = webClient            .get()            .uri(resourceUri)            .attributes(authentication(anonymousAuthentication))  <1>            .retrieve()            .bodyToMono()            .block()    ...    return "index"}----======<1> `authentication()` is a `static` method in `ServletOAuth2AuthorizedClientExchangeFilterFunction`.[WARNING]It is recommended to be cautious with this feature since all HTTP requests will receive an access token bound to the provided principal.=== Defaulting the Authorized ClientIf neither `OAuth2AuthorizedClient` or `ClientRegistration.getRegistrationId()` is provided as a request attribute, the `ServletOAuth2AuthorizedClientExchangeFilterFunction` can determine the _default_ client to use, depending on its configuration.If `setDefaultOAuth2AuthorizedClient(true)` is configured and the user has authenticated by using `HttpSecurity.oauth2Login()`, the `OAuth2AccessToken` associated with the current `OAuth2AuthenticationToken` is used.The following code shows the specific configuration:[tabs]======Java::+[source,java,role="primary"]----@BeanWebClient webClient(OAuth2AuthorizedClientManager authorizedClientManager) {	ServletOAuth2AuthorizedClientExchangeFilterFunction oauth2Client =			new ServletOAuth2AuthorizedClientExchangeFilterFunction(authorizedClientManager);	oauth2Client.setDefaultOAuth2AuthorizedClient(true);	return WebClient.builder()			.apply(oauth2Client.oauth2Configuration())			.build();}----Kotlin::+[source,kotlin,role="secondary"]----@Beanfun webClient(authorizedClientManager: OAuth2AuthorizedClientManager?): WebClient {    val oauth2Client = ServletOAuth2AuthorizedClientExchangeFilterFunction(authorizedClientManager)    oauth2Client.setDefaultOAuth2AuthorizedClient(true)    return WebClient.builder()            .apply(oauth2Client.oauth2Configuration())            .build()}----======[WARNING]====Be cautious with this feature, since all HTTP requests receive the access token.====Alternatively, if `setDefaultClientRegistrationId("okta")` is configured with a valid `ClientRegistration`, the `OAuth2AccessToken` associated with the `OAuth2AuthorizedClient` is used.The following code shows the specific configuration:[tabs]======Java::+[source,java,role="primary"]----@BeanWebClient webClient(OAuth2AuthorizedClientManager authorizedClientManager) {	ServletOAuth2AuthorizedClientExchangeFilterFunction oauth2Client =			new ServletOAuth2AuthorizedClientExchangeFilterFunction(authorizedClientManager);	oauth2Client.setDefaultClientRegistrationId("okta");	return WebClient.builder()			.apply(oauth2Client.oauth2Configuration())			.build();}----Kotlin::+[source,kotlin,role="secondary"]----@Beanfun webClient(authorizedClientManager: OAuth2AuthorizedClientManager?): WebClient {    val oauth2Client = ServletOAuth2AuthorizedClientExchangeFilterFunction(authorizedClientManager)    oauth2Client.setDefaultClientRegistrationId("okta")    return WebClient.builder()            .apply(oauth2Client.oauth2Configuration())            .build()}----======[WARNING]====Be cautious with this feature, since all HTTP requests receive the access token.====
 |