authorized-clients.adoc 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272
  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 capability of resolving a method parameter to an argument value of type `OAuth2AuthorizedClient`.
  7. This is a convenient alternative compared to accessing the `OAuth2AuthorizedClient` using the `ReactiveOAuth2AuthorizedClientManager` or `ReactiveOAuth2AuthorizedClientService`.
  8. [tabs]
  9. ======
  10. Java::
  11. +
  12. [source,java,role="primary"]
  13. ----
  14. @Controller
  15. public class OAuth2ClientController {
  16. @GetMapping("/")
  17. public Mono<String> index(@RegisteredOAuth2AuthorizedClient("okta") OAuth2AuthorizedClient authorizedClient) {
  18. return Mono.just(authorizedClient.getAccessToken())
  19. ...
  20. .thenReturn("index");
  21. }
  22. }
  23. ----
  24. Kotlin::
  25. +
  26. [source,kotlin,role="secondary"]
  27. ----
  28. @Controller
  29. class OAuth2ClientController {
  30. @GetMapping("/")
  31. fun index(@RegisteredOAuth2AuthorizedClient("okta") authorizedClient: OAuth2AuthorizedClient): Mono<String> {
  32. return Mono.just(authorizedClient.accessToken)
  33. ...
  34. .thenReturn("index")
  35. }
  36. }
  37. ----
  38. ======
  39. The `@RegisteredOAuth2AuthorizedClient` annotation is handled by `OAuth2AuthorizedClientArgumentResolver`, which directly uses a xref:reactive/oauth2/client/core.adoc#oauth2Client-authorized-manager-provider[ReactiveOAuth2AuthorizedClientManager] and therefore inherits it's capabilities.
  40. [[oauth2-client-web-client]]
  41. == [[oauth2Client-webclient-webflux]]WebClient integration for Reactive Environments
  42. The OAuth 2.0 Client support integrates with `WebClient` using an `ExchangeFilterFunction`.
  43. The `ServerOAuth2AuthorizedClientExchangeFilterFunction` provides a simple mechanism for requesting protected resources by using an `OAuth2AuthorizedClient` and including the associated `OAuth2AccessToken` as a Bearer Token.
  44. It directly uses an xref:reactive/oauth2/client/core.adoc#oauth2Client-authorized-manager-provider[ReactiveOAuth2AuthorizedClientManager] and therefore inherits the following capabilities:
  45. * An `OAuth2AccessToken` will be requested if the client has not yet been authorized.
  46. ** `authorization_code` - triggers the Authorization Request redirect to initiate the flow
  47. ** `client_credentials` - the access token is obtained directly from the Token Endpoint
  48. * If the `OAuth2AccessToken` is expired, it will be refreshed (or renewed) if a `ReactiveOAuth2AuthorizedClientProvider` is available to perform the authorization
  49. The following code shows an example of how to configure `WebClient` with OAuth 2.0 Client support:
  50. [tabs]
  51. ======
  52. Java::
  53. +
  54. [source,java,role="primary"]
  55. ----
  56. @Bean
  57. WebClient webClient(ReactiveOAuth2AuthorizedClientManager authorizedClientManager) {
  58. ServerOAuth2AuthorizedClientExchangeFilterFunction oauth2Client =
  59. new ServerOAuth2AuthorizedClientExchangeFilterFunction(authorizedClientManager);
  60. return WebClient.builder()
  61. .filter(oauth2Client)
  62. .build();
  63. }
  64. ----
  65. Kotlin::
  66. +
  67. [source,kotlin,role="secondary"]
  68. ----
  69. @Bean
  70. fun webClient(authorizedClientManager: ReactiveOAuth2AuthorizedClientManager): WebClient {
  71. val oauth2Client = ServerOAuth2AuthorizedClientExchangeFilterFunction(authorizedClientManager)
  72. return WebClient.builder()
  73. .filter(oauth2Client)
  74. .build()
  75. }
  76. ----
  77. ======
  78. [[oauth2-client-web-client-authorized-client]]
  79. === Providing the Authorized Client
  80. The `ServerOAuth2AuthorizedClientExchangeFilterFunction` determines the client to use (for a request) by resolving the `OAuth2AuthorizedClient` from the `ClientRequest.attributes()` (request attributes).
  81. The following code shows how to set an `OAuth2AuthorizedClient` as a request attribute:
  82. [tabs]
  83. ======
  84. Java::
  85. +
  86. [source,java,role="primary"]
  87. ----
  88. @GetMapping("/")
  89. public Mono<String> index(@RegisteredOAuth2AuthorizedClient("okta") OAuth2AuthorizedClient authorizedClient) {
  90. String resourceUri = ...
  91. return webClient
  92. .get()
  93. .uri(resourceUri)
  94. .attributes(oauth2AuthorizedClient(authorizedClient)) <1>
  95. .retrieve()
  96. .bodyToMono(String.class)
  97. ...
  98. .thenReturn("index");
  99. }
  100. ----
  101. Kotlin::
  102. +
  103. [source,kotlin,role="secondary"]
  104. ----
  105. @GetMapping("/")
  106. fun index(@RegisteredOAuth2AuthorizedClient("okta") authorizedClient: OAuth2AuthorizedClient): Mono<String> {
  107. val resourceUri: String = ...
  108. return webClient
  109. .get()
  110. .uri(resourceUri)
  111. .attributes(oauth2AuthorizedClient(authorizedClient)) <1>
  112. .retrieve()
  113. .bodyToMono<String>()
  114. ...
  115. .thenReturn("index")
  116. }
  117. ----
  118. ======
  119. <1> `oauth2AuthorizedClient()` is a `static` method in `ServerOAuth2AuthorizedClientExchangeFilterFunction`.
  120. The following code shows how to set the `ClientRegistration.getRegistrationId()` as a request attribute:
  121. [tabs]
  122. ======
  123. Java::
  124. +
  125. [source,java,role="primary"]
  126. ----
  127. @GetMapping("/")
  128. public Mono<String> index() {
  129. String resourceUri = ...
  130. return webClient
  131. .get()
  132. .uri(resourceUri)
  133. .attributes(clientRegistrationId("okta")) <1>
  134. .retrieve()
  135. .bodyToMono(String.class)
  136. ...
  137. .thenReturn("index");
  138. }
  139. ----
  140. Kotlin::
  141. +
  142. [source,kotlin,role="secondary"]
  143. ----
  144. @GetMapping("/")
  145. fun index(): Mono<String> {
  146. val resourceUri: String = ...
  147. return webClient
  148. .get()
  149. .uri(resourceUri)
  150. .attributes(clientRegistrationId("okta")) <1>
  151. .retrieve()
  152. .bodyToMono<String>()
  153. ...
  154. .thenReturn("index")
  155. }
  156. ----
  157. ======
  158. <1> `clientRegistrationId()` is a `static` method in `ServerOAuth2AuthorizedClientExchangeFilterFunction`.
  159. [[oauth2-client-web-client-default-authorized-client]]
  160. === Defaulting the Authorized Client
  161. If neither `OAuth2AuthorizedClient` or `ClientRegistration.getRegistrationId()` is provided as a request attribute, the `ServerOAuth2AuthorizedClientExchangeFilterFunction` can determine the _default_ client to use depending on it's configuration.
  162. If `setDefaultOAuth2AuthorizedClient(true)` is configured and the user has authenticated using `ServerHttpSecurity.oauth2Login()`, the `OAuth2AccessToken` associated with the current `OAuth2AuthenticationToken` is used.
  163. The following code shows the specific configuration:
  164. [tabs]
  165. ======
  166. Java::
  167. +
  168. [source,java,role="primary"]
  169. ----
  170. @Bean
  171. WebClient webClient(ReactiveOAuth2AuthorizedClientManager authorizedClientManager) {
  172. ServerOAuth2AuthorizedClientExchangeFilterFunction oauth2Client =
  173. new ServerOAuth2AuthorizedClientExchangeFilterFunction(authorizedClientManager);
  174. oauth2Client.setDefaultOAuth2AuthorizedClient(true);
  175. return WebClient.builder()
  176. .filter(oauth2Client)
  177. .build();
  178. }
  179. ----
  180. Kotlin::
  181. +
  182. [source,kotlin,role="secondary"]
  183. ----
  184. @Bean
  185. fun webClient(authorizedClientManager: ReactiveOAuth2AuthorizedClientManager): WebClient {
  186. val oauth2Client = ServerOAuth2AuthorizedClientExchangeFilterFunction(authorizedClientManager)
  187. oauth2Client.setDefaultOAuth2AuthorizedClient(true)
  188. return WebClient.builder()
  189. .filter(oauth2Client)
  190. .build()
  191. }
  192. ----
  193. ======
  194. [WARNING]
  195. ====
  196. It is recommended to be cautious with this feature since all HTTP requests will receive the access token.
  197. ====
  198. Alternatively, if `setDefaultClientRegistrationId("okta")` is configured with a valid `ClientRegistration`, the `OAuth2AccessToken` associated with the `OAuth2AuthorizedClient` is used.
  199. The following code shows the specific configuration:
  200. [tabs]
  201. ======
  202. Java::
  203. +
  204. [source,java,role="primary"]
  205. ----
  206. @Bean
  207. WebClient webClient(ReactiveOAuth2AuthorizedClientManager authorizedClientManager) {
  208. ServerOAuth2AuthorizedClientExchangeFilterFunction oauth2Client =
  209. new ServerOAuth2AuthorizedClientExchangeFilterFunction(authorizedClientManager);
  210. oauth2Client.setDefaultClientRegistrationId("okta");
  211. return WebClient.builder()
  212. .filter(oauth2Client)
  213. .build();
  214. }
  215. ----
  216. Kotlin::
  217. +
  218. [source,kotlin,role="secondary"]
  219. ----
  220. @Bean
  221. fun webClient(authorizedClientManager: ReactiveOAuth2AuthorizedClientManager): WebClient {
  222. val oauth2Client = ServerOAuth2AuthorizedClientExchangeFilterFunction(authorizedClientManager)
  223. oauth2Client.setDefaultClientRegistrationId("okta")
  224. return WebClient.builder()
  225. .filter(oauth2Client)
  226. .build()
  227. }
  228. ----
  229. ======
  230. [WARNING]
  231. ====
  232. It is recommended to be cautious with this feature since all HTTP requests will receive the access token.
  233. ====