2
0

authorized-clients.adoc 8.1 KB

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