authorized-clients.adoc 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264
  1. [[oauth2Client-additional-features]]
  2. = Authorized Client Features
  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 `OAuth2AuthorizedClientManager` or `OAuth2AuthorizedClientService`.
  7. ====
  8. .Java
  9. [source,java,role="primary"]
  10. ----
  11. @Controller
  12. public class OAuth2ClientController {
  13. @GetMapping("/")
  14. public String index(@RegisteredOAuth2AuthorizedClient("okta") OAuth2AuthorizedClient authorizedClient) {
  15. OAuth2AccessToken accessToken = authorizedClient.getAccessToken();
  16. ...
  17. return "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): String {
  28. val accessToken = authorizedClient.accessToken
  29. ...
  30. return "index"
  31. }
  32. }
  33. ----
  34. ====
  35. 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 it's capabilities.
  36. [[oauth2Client-webclient-servlet]]
  37. == WebClient integration for Servlet Environments
  38. The OAuth 2.0 Client support integrates with `WebClient` using an `ExchangeFilterFunction`.
  39. The `ServletOAuth2AuthorizedClientExchangeFilterFunction` 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 xref:servlet/oauth2/client/core.adoc#oauth2Client-authorized-manager-provider[`OAuth2AuthorizedClientManager`] 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 an `OAuth2AuthorizedClientProvider` 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(OAuth2AuthorizedClientManager authorizedClientManager) {
  53. ServletOAuth2AuthorizedClientExchangeFilterFunction oauth2Client =
  54. new ServletOAuth2AuthorizedClientExchangeFilterFunction(authorizedClientManager);
  55. return WebClient.builder()
  56. .apply(oauth2Client.oauth2Configuration())
  57. .build();
  58. }
  59. ----
  60. .Kotlin
  61. [source,kotlin,role="secondary"]
  62. ----
  63. @Bean
  64. fun webClient(authorizedClientManager: OAuth2AuthorizedClientManager?): WebClient {
  65. val oauth2Client = ServletOAuth2AuthorizedClientExchangeFilterFunction(authorizedClientManager)
  66. return WebClient.builder()
  67. .apply(oauth2Client.oauth2Configuration())
  68. .build()
  69. }
  70. ----
  71. ====
  72. === Providing the Authorized Client
  73. The `ServletOAuth2AuthorizedClientExchangeFilterFunction` 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 String index(@RegisteredOAuth2AuthorizedClient("okta") OAuth2AuthorizedClient authorizedClient) {
  81. String resourceUri = ...
  82. String body = webClient
  83. .get()
  84. .uri(resourceUri)
  85. .attributes(oauth2AuthorizedClient(authorizedClient)) <1>
  86. .retrieve()
  87. .bodyToMono(String.class)
  88. .block();
  89. ...
  90. return "index";
  91. }
  92. ----
  93. .Kotlin
  94. [source,kotlin,role="secondary"]
  95. ----
  96. @GetMapping("/")
  97. fun index(@RegisteredOAuth2AuthorizedClient("okta") authorizedClient: OAuth2AuthorizedClient): String {
  98. val resourceUri: String = ...
  99. val body: String = webClient
  100. .get()
  101. .uri(resourceUri)
  102. .attributes(oauth2AuthorizedClient(authorizedClient)) <1>
  103. .retrieve()
  104. .bodyToMono()
  105. .block()
  106. ...
  107. return "index"
  108. }
  109. ----
  110. ====
  111. <1> `oauth2AuthorizedClient()` is a `static` method in `ServletOAuth2AuthorizedClientExchangeFilterFunction`.
  112. The following code shows how to set the `ClientRegistration.getRegistrationId()` as a request attribute:
  113. ====
  114. .Java
  115. [source,java,role="primary"]
  116. ----
  117. @GetMapping("/")
  118. public String index() {
  119. String resourceUri = ...
  120. String body = webClient
  121. .get()
  122. .uri(resourceUri)
  123. .attributes(clientRegistrationId("okta")) <1>
  124. .retrieve()
  125. .bodyToMono(String.class)
  126. .block();
  127. ...
  128. return "index";
  129. }
  130. ----
  131. .Kotlin
  132. [source,kotlin,role="secondary"]
  133. ----
  134. @GetMapping("/")
  135. fun index(): String {
  136. val resourceUri: String = ...
  137. val body: String = webClient
  138. .get()
  139. .uri(resourceUri)
  140. .attributes(clientRegistrationId("okta")) <1>
  141. .retrieve()
  142. .bodyToMono()
  143. .block()
  144. ...
  145. return "index"
  146. }
  147. ----
  148. ====
  149. <1> `clientRegistrationId()` is a `static` method in `ServletOAuth2AuthorizedClientExchangeFilterFunction`.
  150. === Defaulting the Authorized Client
  151. If neither `OAuth2AuthorizedClient` or `ClientRegistration.getRegistrationId()` is provided as a request attribute, the `ServletOAuth2AuthorizedClientExchangeFilterFunction` can determine the _default_ client to use depending on it's configuration.
  152. If `setDefaultOAuth2AuthorizedClient(true)` is configured and the user has authenticated using `HttpSecurity.oauth2Login()`, the `OAuth2AccessToken` associated with the current `OAuth2AuthenticationToken` is used.
  153. The following code shows the specific configuration:
  154. ====
  155. .Java
  156. [source,java,role="primary"]
  157. ----
  158. @Bean
  159. WebClient webClient(OAuth2AuthorizedClientManager authorizedClientManager) {
  160. ServletOAuth2AuthorizedClientExchangeFilterFunction oauth2Client =
  161. new ServletOAuth2AuthorizedClientExchangeFilterFunction(authorizedClientManager);
  162. oauth2Client.setDefaultOAuth2AuthorizedClient(true);
  163. return WebClient.builder()
  164. .apply(oauth2Client.oauth2Configuration())
  165. .build();
  166. }
  167. ----
  168. .Kotlin
  169. [source,kotlin,role="secondary"]
  170. ----
  171. @Bean
  172. fun webClient(authorizedClientManager: OAuth2AuthorizedClientManager?): WebClient {
  173. val oauth2Client = ServletOAuth2AuthorizedClientExchangeFilterFunction(authorizedClientManager)
  174. oauth2Client.setDefaultOAuth2AuthorizedClient(true)
  175. return WebClient.builder()
  176. .apply(oauth2Client.oauth2Configuration())
  177. .build()
  178. }
  179. ----
  180. ====
  181. [WARNING]
  182. It is recommended to be cautious with this feature since all HTTP requests will receive the access token.
  183. Alternatively, if `setDefaultClientRegistrationId("okta")` is configured with a valid `ClientRegistration`, the `OAuth2AccessToken` associated with the `OAuth2AuthorizedClient` is used.
  184. The following code shows the specific configuration:
  185. ====
  186. .Java
  187. [source,java,role="primary"]
  188. ----
  189. @Bean
  190. WebClient webClient(OAuth2AuthorizedClientManager authorizedClientManager) {
  191. ServletOAuth2AuthorizedClientExchangeFilterFunction oauth2Client =
  192. new ServletOAuth2AuthorizedClientExchangeFilterFunction(authorizedClientManager);
  193. oauth2Client.setDefaultClientRegistrationId("okta");
  194. return WebClient.builder()
  195. .apply(oauth2Client.oauth2Configuration())
  196. .build();
  197. }
  198. ----
  199. .Kotlin
  200. [source,kotlin,role="secondary"]
  201. ----
  202. @Bean
  203. fun webClient(authorizedClientManager: OAuth2AuthorizedClientManager?): WebClient {
  204. val oauth2Client = ServletOAuth2AuthorizedClientExchangeFilterFunction(authorizedClientManager)
  205. oauth2Client.setDefaultClientRegistrationId("okta")
  206. return WebClient.builder()
  207. .apply(oauth2Client.oauth2Configuration())
  208. .build()
  209. }
  210. ----
  211. ====
  212. [WARNING]
  213. It is recommended to be cautious with this feature since all HTTP requests will receive the access token.