authorized-clients.adoc 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795
  1. [[oauth2-client-additional-features]]
  2. = [[oauth2Client-additional-features]]Authorized Client Features
  3. This section covers additional features provided by Spring Security for OAuth2 client.
  4. [[oauth2-client-registered-authorized-client]]
  5. == [[oauth2Client-registered-authorized-client]]Resolving an Authorized Client
  6. The `@RegisteredOAuth2AuthorizedClient` annotation provides the ability to resolve a method parameter to an argument value of type `OAuth2AuthorizedClient`.
  7. This is a convenient alternative compared to accessing the `OAuth2AuthorizedClient` by using the `OAuth2AuthorizedClientManager` or `OAuth2AuthorizedClientService`.
  8. The following example shows how to use `@RegisteredOAuth2AuthorizedClient`:
  9. [tabs]
  10. ======
  11. Java::
  12. +
  13. [source,java,role="primary"]
  14. ----
  15. @Controller
  16. public class OAuth2ClientController {
  17. @GetMapping("/")
  18. public String index(@RegisteredOAuth2AuthorizedClient("okta") OAuth2AuthorizedClient authorizedClient) {
  19. OAuth2AccessToken accessToken = authorizedClient.getAccessToken();
  20. ...
  21. return "index";
  22. }
  23. }
  24. ----
  25. Kotlin::
  26. +
  27. [source,kotlin,role="secondary"]
  28. ----
  29. @Controller
  30. class OAuth2ClientController {
  31. @GetMapping("/")
  32. fun index(@RegisteredOAuth2AuthorizedClient("okta") authorizedClient: OAuth2AuthorizedClient): String {
  33. val accessToken = authorizedClient.accessToken
  34. ...
  35. return "index"
  36. }
  37. }
  38. ----
  39. ======
  40. 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.
  41. [[oauth2-client-rest-client]]
  42. == RestClient Integration
  43. Support for `RestClient` is provided by `OAuth2ClientHttpRequestInterceptor`.
  44. This interceptor provides the ability to make protected resources requests by placing a `Bearer` token in the `Authorization` header of an outbound request.
  45. The interceptor directly uses an `OAuth2AuthorizedClientManager` and therefore inherits the following capabilities:
  46. * Performs an OAuth 2.0 Access Token request to obtain `OAuth2AccessToken` if the client has not yet been authorized
  47. ** `authorization_code`: Triggers the Authorization Request redirect to initiate the flow
  48. ** `client_credentials`: The access token is obtained directly from the Token Endpoint
  49. ** Additional grant types are supported by xref:servlet/oauth2/index.adoc#oauth2-client-enable-extension-grant-type[enabling extension grant types]
  50. * If an existing `OAuth2AccessToken` is expired, it is refreshed (or renewed)
  51. The following example uses the default `OAuth2AuthorizedClientManager` to configure a `RestClient` capable of accessing protected resources by placing `Bearer` tokens in the `Authorization` header of each request:
  52. .Configure `RestClient` with `ClientHttpRequestInterceptor`
  53. [tabs]
  54. =====
  55. Java::
  56. +
  57. [source,java,role="primary"]
  58. ----
  59. @Configuration
  60. public class RestClientConfig {
  61. @Bean
  62. public RestClient restClient(OAuth2AuthorizedClientManager authorizedClientManager) {
  63. OAuth2ClientHttpRequestInterceptor requestInterceptor =
  64. new OAuth2ClientHttpRequestInterceptor(authorizedClientManager);
  65. return RestClient.builder()
  66. .requestInterceptor(requestInterceptor)
  67. .build();
  68. }
  69. }
  70. ----
  71. Kotlin::
  72. +
  73. [source,kotlin,role="secondary"]
  74. ----
  75. @Configuration
  76. class RestClientConfig {
  77. @Bean
  78. fun restClient(authorizedClientManager: OAuth2AuthorizedClientManager): RestClient {
  79. val requestInterceptor = OAuth2ClientHttpRequestInterceptor(authorizedClientManager)
  80. return RestClient.builder()
  81. .requestInterceptor(requestInterceptor)
  82. .build()
  83. }
  84. }
  85. ----
  86. =====
  87. [[oauth2-client-rest-client-registration-id]]
  88. === Providing the `clientRegistrationId`
  89. `OAuth2ClientHttpRequestInterceptor` uses a `ClientRegistrationIdResolver` to determine which client is used to obtain an access token.
  90. By default, `RequestAttributeClientRegistrationIdResolver` is used to resolve the `clientRegistrationId` from `HttpRequest#attributes()`.
  91. The following example demonstrates providing a `clientRegistrationId` via attributes:
  92. .Provide `clientRegistrationId` via attributes
  93. [tabs]
  94. ======
  95. Java::
  96. +
  97. [source,java,role="primary"]
  98. ----
  99. import static org.springframework.security.oauth2.client.web.client.RequestAttributeClientRegistrationIdResolver.clientRegistrationId;
  100. @Controller
  101. public class ResourceController {
  102. private final RestClient restClient;
  103. public ResourceController(RestClient restClient) {
  104. this.restClient = restClient;
  105. }
  106. @GetMapping("/")
  107. public String index() {
  108. String resourceUri = "...";
  109. String body = this.restClient.get()
  110. .uri(resourceUri)
  111. .attributes(clientRegistrationId("okta")) // <1>
  112. .retrieve()
  113. .body(String.class);
  114. // ...
  115. return "index";
  116. }
  117. }
  118. ----
  119. Kotlin::
  120. +
  121. [source,kotlin,role="secondary"]
  122. ----
  123. import org.springframework.security.oauth2.client.web.client.RequestAttributeClientRegistrationIdResolver.clientRegistrationId
  124. import org.springframework.web.client.body
  125. @Controller
  126. class ResourceController(private restClient: RestClient) {
  127. @GetMapping("/")
  128. fun index(): String {
  129. val resourceUri = "..."
  130. val body: String = restClient.get()
  131. .uri(resourceUri)
  132. .attributes(clientRegistrationId("okta")) // <1>
  133. .retrieve()
  134. .body<String>()
  135. // ...
  136. return "index"
  137. }
  138. }
  139. ----
  140. ======
  141. <1> `clientRegistrationId()` is a `static` method in `RequestAttributeClientRegistrationIdResolver`.
  142. Alternatively, a custom `ClientRegistrationIdResolver` can be provided.
  143. The following example configures a custom implementation that resolves the `clientRegistrationId` from the current user.
  144. .Configure `ClientHttpRequestInterceptor` with custom `ClientRegistrationIdResolver`
  145. [tabs]
  146. =====
  147. Java::
  148. +
  149. [source,java,role="primary"]
  150. ----
  151. @Configuration
  152. public class RestClientConfig {
  153. @Bean
  154. public RestClient restClient(OAuth2AuthorizedClientManager authorizedClientManager) {
  155. OAuth2ClientHttpRequestInterceptor requestInterceptor =
  156. new OAuth2ClientHttpRequestInterceptor(authorizedClientManager);
  157. requestInterceptor.setClientRegistrationIdResolver(clientRegistrationIdResolver());
  158. return RestClient.builder()
  159. .requestInterceptor(requestInterceptor)
  160. .build();
  161. }
  162. private static ClientRegistrationIdResolver clientRegistrationIdResolver() {
  163. return (request) -> {
  164. Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
  165. return (authentication instanceof OAuth2AuthenticationToken principal)
  166. ? principal.getAuthorizedClientRegistrationId() : null;
  167. };
  168. }
  169. }
  170. ----
  171. Kotlin::
  172. +
  173. [source,kotlin,role="secondary"]
  174. ----
  175. @Configuration
  176. class RestClientConfig {
  177. @Bean
  178. fun restClient(authorizedClientManager: OAuth2AuthorizedClientManager): RestClient {
  179. val requestInterceptor = OAuth2ClientHttpRequestInterceptor(authorizedClientManager)
  180. requestInterceptor.setClientRegistrationIdResolver(clientRegistrationIdResolver())
  181. return RestClient.builder()
  182. .requestInterceptor(requestInterceptor)
  183. .build()
  184. }
  185. fun clientRegistrationIdResolver(): ClientRegistrationIdResolver {
  186. return ClientRegistrationIdResolver { request ->
  187. val authentication = SecurityContextHolder.getContext().getAuthentication()
  188. return if (authentication instanceof OAuth2AuthenticationToken) {
  189. authentication.getAuthorizedClientRegistrationId()
  190. } else {
  191. null
  192. }
  193. }
  194. }
  195. }
  196. ----
  197. =====
  198. [[oauth2-client-rest-client-principal]]
  199. === Providing the `principal`
  200. `OAuth2ClientHttpRequestInterceptor` uses a `PrincipalResolver` to determine which principal name is associated with the access token, which allows an application to choose how to scope the `OAuth2AuthorizedClient` that is stored.
  201. By default, `SecurityContextHolderPrincipalResolver` is used to resolve the current `principal` from the `SecurityContextHolder`.
  202. Alternatively, the `principal` can be resolved from `HttpRequest#attributes()` by configuring `RequestAttributePrincipalResolver`, as the following example shows:
  203. .Configure `ClientHttpRequestInterceptor` with `RequestAttributePrincipalResolver`
  204. [tabs]
  205. =====
  206. Java::
  207. +
  208. [source,java,role="primary"]
  209. ----
  210. @Configuration
  211. public class RestClientConfig {
  212. @Bean
  213. public RestClient restClient(OAuth2AuthorizedClientManager authorizedClientManager) {
  214. OAuth2ClientHttpRequestInterceptor requestInterceptor =
  215. new OAuth2ClientHttpRequestInterceptor(authorizedClientManager);
  216. requestInterceptor.setPrincipalResolver(new RequestAttributePrincipalResolver());
  217. return RestClient.builder()
  218. .requestInterceptor(requestInterceptor)
  219. .build();
  220. }
  221. }
  222. ----
  223. Kotlin::
  224. +
  225. [source,kotlin,role="secondary"]
  226. ----
  227. @Configuration
  228. class RestClientConfig {
  229. @Bean
  230. fun restClient(authorizedClientManager: OAuth2AuthorizedClientManager): RestClient {
  231. val requestInterceptor = OAuth2ClientHttpRequestInterceptor(authorizedClientManager)
  232. requestInterceptor.setPrincipalResolver(RequestAttributePrincipalResolver())
  233. return RestClient.builder()
  234. .requestInterceptor(requestInterceptor)
  235. .build()
  236. }
  237. }
  238. ----
  239. =====
  240. The following example demonstrates providing a `principal` name via attributes that scopes the `OAuth2AuthorizedClient` to the application instead of the current user:
  241. .Provide `principal` name via attributes
  242. [tabs]
  243. ======
  244. Java::
  245. +
  246. [source,java,role="primary"]
  247. ----
  248. import static org.springframework.security.oauth2.client.web.client.RequestAttributeClientRegistrationIdResolver.clientRegistrationId;
  249. import static org.springframework.security.oauth2.client.web.client.RequestAttributePrincipalResolver.principal;
  250. @Controller
  251. public class ResourceController {
  252. private final RestClient restClient;
  253. public ResourceController(RestClient restClient) {
  254. this.restClient = restClient;
  255. }
  256. @GetMapping("/")
  257. public String index() {
  258. String resourceUri = "...";
  259. String body = this.restClient.get()
  260. .uri(resourceUri)
  261. .attributes(clientRegistrationId("okta"))
  262. .attributes(principal("my-application")) // <1>
  263. .retrieve()
  264. .body(String.class);
  265. // ...
  266. return "index";
  267. }
  268. }
  269. ----
  270. Kotlin::
  271. +
  272. [source,kotlin,role="secondary"]
  273. ----
  274. import org.springframework.security.oauth2.client.web.client.RequestAttributeClientRegistrationIdResolver.clientRegistrationId
  275. import org.springframework.security.oauth2.client.web.client.RequestAttributePrincipalResolver.principal
  276. import org.springframework.web.client.body
  277. @Controller
  278. class ResourceController(private restClient: RestClient) {
  279. @GetMapping("/")
  280. fun index(): String {
  281. val resourceUri = "..."
  282. val body: String = restClient.get()
  283. .uri(resourceUri)
  284. .attributes(clientRegistrationId("okta"))
  285. .attributes(principal("my-application")) // <1>
  286. .retrieve()
  287. .body<String>()
  288. // ...
  289. return "index"
  290. }
  291. }
  292. ----
  293. ======
  294. <1> `principal()` is a `static` method in `RequestAttributePrincipalResolver`.
  295. [[oauth2-client-rest-client-authorization-failure-handler]]
  296. === Handling Failure
  297. If an access token is invalid for any reason (e.g. expired token), it can be beneficial to handle the failure by removing the access token so that it cannot be used again.
  298. You can set up the interceptor to do this automatically by providing an `OAuth2AuthorizationFailureHandler` to remove the access token.
  299. The following example uses an `OAuth2AuthorizedClientRepository` to set up an `OAuth2AuthorizationFailureHandler` that removes an invalid `OAuth2AuthorizedClient` *within* the context of an `HttpServletRequest`:
  300. .Configure `OAuth2AuthorizationFailureHandler` using `OAuth2AuthorizedClientRepository`
  301. [tabs]
  302. =====
  303. Java::
  304. +
  305. [source,java,role="primary"]
  306. ----
  307. @Configuration
  308. public class RestClientConfig {
  309. @Bean
  310. public RestClient restClient(OAuth2AuthorizedClientManager authorizedClientManager,
  311. OAuth2AuthorizedClientRepository authorizedClientRepository) {
  312. OAuth2ClientHttpRequestInterceptor requestInterceptor =
  313. new OAuth2ClientHttpRequestInterceptor(authorizedClientManager);
  314. OAuth2AuthorizationFailureHandler authorizationFailureHandler =
  315. OAuth2ClientHttpRequestInterceptor.authorizationFailureHandler(authorizedClientRepository);
  316. requestInterceptor.setAuthorizationFailureHandler(authorizationFailureHandler);
  317. return RestClient.builder()
  318. .requestInterceptor(requestInterceptor)
  319. .build();
  320. }
  321. }
  322. ----
  323. Kotlin::
  324. +
  325. [source,kotlin,role="secondary"]
  326. ----
  327. @Configuration
  328. class RestClientConfig {
  329. @Bean
  330. fun restClient(authorizedClientManager: OAuth2AuthorizedClientManager,
  331. authorizedClientRepository: OAuth2AuthorizedClientRepository): RestClient {
  332. val requestInterceptor = OAuth2ClientHttpRequestInterceptor(authorizedClientManager)
  333. val authorizationFailureHandler = OAuth2ClientHttpRequestInterceptor
  334. .authorizationFailureHandler(authorizedClientRepository)
  335. requestInterceptor.setAuthorizationFailureHandler(authorizationFailureHandler)
  336. return RestClient.builder()
  337. .requestInterceptor(requestInterceptor)
  338. .build()
  339. }
  340. }
  341. ----
  342. =====
  343. Alternatively, an `OAuth2AuthorizedClientService` can be used to remove an invalid `OAuth2AuthorizedClient` *outside* the context of an `HttpServletRequest`, as the following example shows:
  344. .Configure `OAuth2AuthorizationFailureHandler` using `OAuth2AuthorizedClientService`
  345. [tabs]
  346. =====
  347. Java::
  348. +
  349. [source,java,role="primary"]
  350. ----
  351. @Configuration
  352. public class RestClientConfig {
  353. @Bean
  354. public RestClient restClient(OAuth2AuthorizedClientManager authorizedClientManager,
  355. OAuth2AuthorizedClientService authorizedClientService) {
  356. OAuth2ClientHttpRequestInterceptor requestInterceptor =
  357. new OAuth2ClientHttpRequestInterceptor(authorizedClientManager);
  358. OAuth2AuthorizationFailureHandler authorizationFailureHandler =
  359. OAuth2ClientHttpRequestInterceptor.authorizationFailureHandler(authorizedClientService);
  360. requestInterceptor.setAuthorizationFailureHandler(authorizationFailureHandler);
  361. return RestClient.builder()
  362. .requestInterceptor(requestInterceptor)
  363. .build();
  364. }
  365. }
  366. ----
  367. Kotlin::
  368. +
  369. [source,kotlin,role="secondary"]
  370. ----
  371. @Configuration
  372. class RestClientConfig {
  373. @Bean
  374. fun restClient(authorizedClientManager: OAuth2AuthorizedClientManager,
  375. authorizedClientService: OAuth2AuthorizedClientService): RestClient {
  376. val requestInterceptor = OAuth2ClientHttpRequestInterceptor(authorizedClientManager)
  377. val authorizationFailureHandler = OAuth2ClientHttpRequestInterceptor
  378. .authorizationFailureHandler(authorizedClientService)
  379. requestInterceptor.setAuthorizationFailureHandler(authorizationFailureHandler)
  380. return RestClient.builder()
  381. .requestInterceptor(requestInterceptor)
  382. .build()
  383. }
  384. }
  385. ----
  386. =====
  387. [[oauth2-client-rest-client-interface]]
  388. === HTTP Interface Integration
  389. Spring Security's OAuth support integrates with xref:features/integrations/rest/http-interface.adoc[].
  390. [[oauth2-client-web-client]]
  391. == [[oauth2Client-webclient-servlet]]WebClient Integration for Servlet Environments
  392. The OAuth 2.0 Client support integrates with `WebClient` by using an `ExchangeFilterFunction`.
  393. The `ServletOAuth2AuthorizedClientExchangeFilterFunction` provides a mechanism for requesting protected resources by using an `OAuth2AuthorizedClient` and including the associated `OAuth2AccessToken` as a Bearer Token.
  394. It directly uses an xref:servlet/oauth2/client/core.adoc#oauth2Client-authorized-manager-provider[`OAuth2AuthorizedClientManager`] and, therefore, inherits the following capabilities:
  395. * An `OAuth2AccessToken` is requested if the client has not yet been authorized.
  396. ** `authorization_code`: Triggers the Authorization Request redirect to initiate the flow.
  397. ** `client_credentials`: The access token is obtained directly from the Token Endpoint.
  398. * If the `OAuth2AccessToken` is expired, it is refreshed (or renewed) if an `OAuth2AuthorizedClientProvider` is available to perform the authorization
  399. The following code shows an example of how to configure `WebClient` with OAuth 2.0 Client support:
  400. [tabs]
  401. ======
  402. Java::
  403. +
  404. [source,java,role="primary"]
  405. ----
  406. @Bean
  407. WebClient webClient(OAuth2AuthorizedClientManager authorizedClientManager) {
  408. ServletOAuth2AuthorizedClientExchangeFilterFunction oauth2Client =
  409. new ServletOAuth2AuthorizedClientExchangeFilterFunction(authorizedClientManager);
  410. return WebClient.builder()
  411. .apply(oauth2Client.oauth2Configuration())
  412. .build();
  413. }
  414. ----
  415. Kotlin::
  416. +
  417. [source,kotlin,role="secondary"]
  418. ----
  419. @Bean
  420. fun webClient(authorizedClientManager: OAuth2AuthorizedClientManager?): WebClient {
  421. val oauth2Client = ServletOAuth2AuthorizedClientExchangeFilterFunction(authorizedClientManager)
  422. return WebClient.builder()
  423. .apply(oauth2Client.oauth2Configuration())
  424. .build()
  425. }
  426. ----
  427. ======
  428. [[oauth2-client-web-client-authorized-client]]
  429. === Providing the Authorized Client
  430. The `ServletOAuth2AuthorizedClientExchangeFilterFunction` determines the client to use (for a request) by resolving the `OAuth2AuthorizedClient` from the `ClientRequest.attributes()` (request attributes).
  431. The following code shows how to set an `OAuth2AuthorizedClient` as a request attribute:
  432. [tabs]
  433. ======
  434. Java::
  435. +
  436. [source,java,role="primary"]
  437. ----
  438. @GetMapping("/")
  439. public String index(@RegisteredOAuth2AuthorizedClient("okta") OAuth2AuthorizedClient authorizedClient) {
  440. String resourceUri = ...
  441. String body = webClient
  442. .get()
  443. .uri(resourceUri)
  444. .attributes(oauth2AuthorizedClient(authorizedClient)) <1>
  445. .retrieve()
  446. .bodyToMono(String.class)
  447. .block();
  448. ...
  449. return "index";
  450. }
  451. ----
  452. Kotlin::
  453. +
  454. [source,kotlin,role="secondary"]
  455. ----
  456. @GetMapping("/")
  457. fun index(@RegisteredOAuth2AuthorizedClient("okta") authorizedClient: OAuth2AuthorizedClient): String {
  458. val resourceUri: String = ...
  459. val body: String = webClient
  460. .get()
  461. .uri(resourceUri)
  462. .attributes(oauth2AuthorizedClient(authorizedClient)) <1>
  463. .retrieve()
  464. .bodyToMono()
  465. .block()
  466. ...
  467. return "index"
  468. }
  469. ----
  470. ======
  471. <1> `oauth2AuthorizedClient()` is a `static` method in `ServletOAuth2AuthorizedClientExchangeFilterFunction`.
  472. The following code shows how to set the `ClientRegistration.getRegistrationId()` as a request attribute:
  473. [tabs]
  474. ======
  475. Java::
  476. +
  477. [source,java,role="primary"]
  478. ----
  479. @GetMapping("/")
  480. public String index() {
  481. String resourceUri = ...
  482. String body = webClient
  483. .get()
  484. .uri(resourceUri)
  485. .attributes(clientRegistrationId("okta")) <1>
  486. .retrieve()
  487. .bodyToMono(String.class)
  488. .block();
  489. ...
  490. return "index";
  491. }
  492. ----
  493. Kotlin::
  494. +
  495. [source,kotlin,role="secondary"]
  496. ----
  497. @GetMapping("/")
  498. fun index(): String {
  499. val resourceUri: String = ...
  500. val body: String = webClient
  501. .get()
  502. .uri(resourceUri)
  503. .attributes(clientRegistrationId("okta")) <1>
  504. .retrieve()
  505. .bodyToMono()
  506. .block()
  507. ...
  508. return "index"
  509. }
  510. ----
  511. ======
  512. <1> `clientRegistrationId()` is a `static` method in `ServletOAuth2AuthorizedClientExchangeFilterFunction`.
  513. The following code shows how to set an `Authentication` as a request attribute:
  514. [tabs]
  515. ======
  516. Java::
  517. +
  518. [source,java,role="primary"]
  519. ----
  520. @GetMapping("/")
  521. public String index() {
  522. String resourceUri = ...
  523. Authentication anonymousAuthentication = new AnonymousAuthenticationToken(
  524. "anonymous", "anonymousUser", AuthorityUtils.createAuthorityList("ROLE_ANONYMOUS"));
  525. String body = webClient
  526. .get()
  527. .uri(resourceUri)
  528. .attributes(authentication(anonymousAuthentication)) <1>
  529. .retrieve()
  530. .bodyToMono(String.class)
  531. .block();
  532. ...
  533. return "index";
  534. }
  535. ----
  536. Kotlin::
  537. +
  538. [source,kotlin,role="secondary"]
  539. ----
  540. @GetMapping("/")
  541. fun index(): String {
  542. val resourceUri: String = ...
  543. val anonymousAuthentication: Authentication = AnonymousAuthenticationToken(
  544. "anonymous", "anonymousUser", AuthorityUtils.createAuthorityList("ROLE_ANONYMOUS"))
  545. val body: String = webClient
  546. .get()
  547. .uri(resourceUri)
  548. .attributes(authentication(anonymousAuthentication)) <1>
  549. .retrieve()
  550. .bodyToMono()
  551. .block()
  552. ...
  553. return "index"
  554. }
  555. ----
  556. ======
  557. <1> `authentication()` is a `static` method in `ServletOAuth2AuthorizedClientExchangeFilterFunction`.
  558. [WARNING]
  559. ====
  560. It is recommended to be cautious with this feature since all HTTP requests will receive an access token bound to the provided principal.
  561. ====
  562. [[oauth2-client-web-client-default-authorized-client]]
  563. === Defaulting the Authorized Client
  564. If neither `OAuth2AuthorizedClient` or `ClientRegistration.getRegistrationId()` is provided as a request attribute, the `ServletOAuth2AuthorizedClientExchangeFilterFunction` can determine the _default_ client to use, depending on its configuration.
  565. If `setDefaultOAuth2AuthorizedClient(true)` is configured and the user has authenticated by using `HttpSecurity.oauth2Login()`, the `OAuth2AccessToken` associated with the current `OAuth2AuthenticationToken` is used.
  566. The following code shows the specific configuration:
  567. [tabs]
  568. ======
  569. Java::
  570. +
  571. [source,java,role="primary"]
  572. ----
  573. @Bean
  574. WebClient webClient(OAuth2AuthorizedClientManager authorizedClientManager) {
  575. ServletOAuth2AuthorizedClientExchangeFilterFunction oauth2Client =
  576. new ServletOAuth2AuthorizedClientExchangeFilterFunction(authorizedClientManager);
  577. oauth2Client.setDefaultOAuth2AuthorizedClient(true);
  578. return WebClient.builder()
  579. .apply(oauth2Client.oauth2Configuration())
  580. .build();
  581. }
  582. ----
  583. Kotlin::
  584. +
  585. [source,kotlin,role="secondary"]
  586. ----
  587. @Bean
  588. fun webClient(authorizedClientManager: OAuth2AuthorizedClientManager?): WebClient {
  589. val oauth2Client = ServletOAuth2AuthorizedClientExchangeFilterFunction(authorizedClientManager)
  590. oauth2Client.setDefaultOAuth2AuthorizedClient(true)
  591. return WebClient.builder()
  592. .apply(oauth2Client.oauth2Configuration())
  593. .build()
  594. }
  595. ----
  596. ======
  597. [WARNING]
  598. ====
  599. Be cautious with this feature, since all HTTP requests receive the access token.
  600. ====
  601. Alternatively, if `setDefaultClientRegistrationId("okta")` is configured with a valid `ClientRegistration`, the `OAuth2AccessToken` associated with the `OAuth2AuthorizedClient` is used.
  602. The following code shows the specific configuration:
  603. [tabs]
  604. ======
  605. Java::
  606. +
  607. [source,java,role="primary"]
  608. ----
  609. @Bean
  610. WebClient webClient(OAuth2AuthorizedClientManager authorizedClientManager) {
  611. ServletOAuth2AuthorizedClientExchangeFilterFunction oauth2Client =
  612. new ServletOAuth2AuthorizedClientExchangeFilterFunction(authorizedClientManager);
  613. oauth2Client.setDefaultClientRegistrationId("okta");
  614. return WebClient.builder()
  615. .apply(oauth2Client.oauth2Configuration())
  616. .build();
  617. }
  618. ----
  619. Kotlin::
  620. +
  621. [source,kotlin,role="secondary"]
  622. ----
  623. @Bean
  624. fun webClient(authorizedClientManager: OAuth2AuthorizedClientManager?): WebClient {
  625. val oauth2Client = ServletOAuth2AuthorizedClientExchangeFilterFunction(authorizedClientManager)
  626. oauth2Client.setDefaultClientRegistrationId("okta")
  627. return WebClient.builder()
  628. .apply(oauth2Client.oauth2Configuration())
  629. .build()
  630. }
  631. ----
  632. ======
  633. [WARNING]
  634. ====
  635. Be cautious with this feature, since all HTTP requests receive the access token.
  636. ====