authorized-clients.adoc 8.0 KB

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