authorized-clients.adoc 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273
  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. ** `password` - the access token is obtained directly from the Token Endpoint
  49. * If the `OAuth2AccessToken` is expired, it will be refreshed (or renewed) if a `ReactiveOAuth2AuthorizedClientProvider` is available to perform the authorization
  50. The following code shows an example of how to configure `WebClient` with OAuth 2.0 Client support:
  51. [tabs]
  52. ======
  53. Java::
  54. +
  55. [source,java,role="primary"]
  56. ----
  57. @Bean
  58. WebClient webClient(ReactiveOAuth2AuthorizedClientManager authorizedClientManager) {
  59. ServerOAuth2AuthorizedClientExchangeFilterFunction oauth2Client =
  60. new ServerOAuth2AuthorizedClientExchangeFilterFunction(authorizedClientManager);
  61. return WebClient.builder()
  62. .filter(oauth2Client)
  63. .build();
  64. }
  65. ----
  66. Kotlin::
  67. +
  68. [source,kotlin,role="secondary"]
  69. ----
  70. @Bean
  71. fun webClient(authorizedClientManager: ReactiveOAuth2AuthorizedClientManager): WebClient {
  72. val oauth2Client = ServerOAuth2AuthorizedClientExchangeFilterFunction(authorizedClientManager)
  73. return WebClient.builder()
  74. .filter(oauth2Client)
  75. .build()
  76. }
  77. ----
  78. ======
  79. [[oauth2-client-web-client-authorized-client]]
  80. === Providing the Authorized Client
  81. The `ServerOAuth2AuthorizedClientExchangeFilterFunction` determines the client to use (for a request) by resolving the `OAuth2AuthorizedClient` from the `ClientRequest.attributes()` (request attributes).
  82. The following code shows how to set an `OAuth2AuthorizedClient` as a request attribute:
  83. [tabs]
  84. ======
  85. Java::
  86. +
  87. [source,java,role="primary"]
  88. ----
  89. @GetMapping("/")
  90. public Mono<String> index(@RegisteredOAuth2AuthorizedClient("okta") OAuth2AuthorizedClient authorizedClient) {
  91. String resourceUri = ...
  92. return webClient
  93. .get()
  94. .uri(resourceUri)
  95. .attributes(oauth2AuthorizedClient(authorizedClient)) <1>
  96. .retrieve()
  97. .bodyToMono(String.class)
  98. ...
  99. .thenReturn("index");
  100. }
  101. ----
  102. Kotlin::
  103. +
  104. [source,kotlin,role="secondary"]
  105. ----
  106. @GetMapping("/")
  107. fun index(@RegisteredOAuth2AuthorizedClient("okta") authorizedClient: OAuth2AuthorizedClient): Mono<String> {
  108. val resourceUri: String = ...
  109. return webClient
  110. .get()
  111. .uri(resourceUri)
  112. .attributes(oauth2AuthorizedClient(authorizedClient)) <1>
  113. .retrieve()
  114. .bodyToMono<String>()
  115. ...
  116. .thenReturn("index")
  117. }
  118. ----
  119. ======
  120. <1> `oauth2AuthorizedClient()` is a `static` method in `ServerOAuth2AuthorizedClientExchangeFilterFunction`.
  121. The following code shows how to set the `ClientRegistration.getRegistrationId()` as a request attribute:
  122. [tabs]
  123. ======
  124. Java::
  125. +
  126. [source,java,role="primary"]
  127. ----
  128. @GetMapping("/")
  129. public Mono<String> index() {
  130. String resourceUri = ...
  131. return webClient
  132. .get()
  133. .uri(resourceUri)
  134. .attributes(clientRegistrationId("okta")) <1>
  135. .retrieve()
  136. .bodyToMono(String.class)
  137. ...
  138. .thenReturn("index");
  139. }
  140. ----
  141. Kotlin::
  142. +
  143. [source,kotlin,role="secondary"]
  144. ----
  145. @GetMapping("/")
  146. fun index(): Mono<String> {
  147. val resourceUri: String = ...
  148. return webClient
  149. .get()
  150. .uri(resourceUri)
  151. .attributes(clientRegistrationId("okta")) <1>
  152. .retrieve()
  153. .bodyToMono<String>()
  154. ...
  155. .thenReturn("index")
  156. }
  157. ----
  158. ======
  159. <1> `clientRegistrationId()` is a `static` method in `ServerOAuth2AuthorizedClientExchangeFilterFunction`.
  160. [[oauth2-client-web-client-default-authorized-client]]
  161. === Defaulting the Authorized Client
  162. 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.
  163. If `setDefaultOAuth2AuthorizedClient(true)` is configured and the user has authenticated using `ServerHttpSecurity.oauth2Login()`, the `OAuth2AccessToken` associated with the current `OAuth2AuthenticationToken` is used.
  164. The following code shows the specific configuration:
  165. [tabs]
  166. ======
  167. Java::
  168. +
  169. [source,java,role="primary"]
  170. ----
  171. @Bean
  172. WebClient webClient(ReactiveOAuth2AuthorizedClientManager authorizedClientManager) {
  173. ServerOAuth2AuthorizedClientExchangeFilterFunction oauth2Client =
  174. new ServerOAuth2AuthorizedClientExchangeFilterFunction(authorizedClientManager);
  175. oauth2Client.setDefaultOAuth2AuthorizedClient(true);
  176. return WebClient.builder()
  177. .filter(oauth2Client)
  178. .build();
  179. }
  180. ----
  181. Kotlin::
  182. +
  183. [source,kotlin,role="secondary"]
  184. ----
  185. @Bean
  186. fun webClient(authorizedClientManager: ReactiveOAuth2AuthorizedClientManager): WebClient {
  187. val oauth2Client = ServerOAuth2AuthorizedClientExchangeFilterFunction(authorizedClientManager)
  188. oauth2Client.setDefaultOAuth2AuthorizedClient(true)
  189. return WebClient.builder()
  190. .filter(oauth2Client)
  191. .build()
  192. }
  193. ----
  194. ======
  195. [WARNING]
  196. ====
  197. It is recommended to be cautious with this feature since all HTTP requests will receive the access token.
  198. ====
  199. Alternatively, if `setDefaultClientRegistrationId("okta")` is configured with a valid `ClientRegistration`, the `OAuth2AccessToken` associated with the `OAuth2AuthorizedClient` is used.
  200. The following code shows the specific configuration:
  201. [tabs]
  202. ======
  203. Java::
  204. +
  205. [source,java,role="primary"]
  206. ----
  207. @Bean
  208. WebClient webClient(ReactiveOAuth2AuthorizedClientManager authorizedClientManager) {
  209. ServerOAuth2AuthorizedClientExchangeFilterFunction oauth2Client =
  210. new ServerOAuth2AuthorizedClientExchangeFilterFunction(authorizedClientManager);
  211. oauth2Client.setDefaultClientRegistrationId("okta");
  212. return WebClient.builder()
  213. .filter(oauth2Client)
  214. .build();
  215. }
  216. ----
  217. Kotlin::
  218. +
  219. [source,kotlin,role="secondary"]
  220. ----
  221. @Bean
  222. fun webClient(authorizedClientManager: ReactiveOAuth2AuthorizedClientManager): WebClient {
  223. val oauth2Client = ServerOAuth2AuthorizedClientExchangeFilterFunction(authorizedClientManager)
  224. oauth2Client.setDefaultClientRegistrationId("okta")
  225. return WebClient.builder()
  226. .filter(oauth2Client)
  227. .build()
  228. }
  229. ----
  230. ======
  231. [WARNING]
  232. ====
  233. It is recommended to be cautious with this feature since all HTTP requests will receive the access token.
  234. ====