authorized-clients.adoc 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346
  1. [[oauth2Client-additional-features]]
  2. = Authorized Client Features
  3. This section covers additional features provided by Spring Security for the OAuth2 client.
  4. [[oauth2Client-registered-authorized-client]]
  5. == Resolving an Authorized Client
  6. The `@RegisteredOAuth2AuthorizedClient` annotation provides the ability to resolve a method parameter to an argument value of type `OAuth2AuthorizedClient`.
  7. This is a convenient alternative compared to accessing the `OAuth2AuthorizedClient` by using the `OAuth2AuthorizedClientManager` or `OAuth2AuthorizedClientService`.
  8. The following example shows how to use `@RegisteredOAuth2AuthorizedClient`:
  9. [tabs]
  10. ======
  11. Java::
  12. +
  13. [source,java,role="primary"]
  14. ----
  15. @Controller
  16. public class OAuth2ClientController {
  17. @GetMapping("/")
  18. public String index(@RegisteredOAuth2AuthorizedClient("okta") OAuth2AuthorizedClient authorizedClient) {
  19. OAuth2AccessToken accessToken = authorizedClient.getAccessToken();
  20. ...
  21. return "index";
  22. }
  23. }
  24. ----
  25. Kotlin::
  26. +
  27. [source,kotlin,role="secondary"]
  28. ----
  29. @Controller
  30. class OAuth2ClientController {
  31. @GetMapping("/")
  32. fun index(@RegisteredOAuth2AuthorizedClient("okta") authorizedClient: OAuth2AuthorizedClient): String {
  33. val accessToken = authorizedClient.accessToken
  34. ...
  35. return "index"
  36. }
  37. }
  38. ----
  39. ======
  40. 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 its capabilities.
  41. [[oauth2Client-webclient-servlet]]
  42. == WebClient Integration for Servlet Environments
  43. The OAuth 2.0 Client support integrates with `WebClient` by using an `ExchangeFilterFunction`.
  44. The `ServletOAuth2AuthorizedClientExchangeFilterFunction` provides a mechanism for requesting protected resources by using an `OAuth2AuthorizedClient` and including the associated `OAuth2AccessToken` as a Bearer Token.
  45. It directly uses an xref:servlet/oauth2/client/core.adoc#oauth2Client-authorized-manager-provider[`OAuth2AuthorizedClientManager`] and, therefore, inherits the following capabilities:
  46. * An `OAuth2AccessToken` is requested if the client has not yet been authorized.
  47. ** `authorization_code`: Triggers the Authorization Request redirect to initiate the flow.
  48. ** `client_credentials`: The access token is obtained directly from the Token Endpoint.
  49. ** `password`: The access token is obtained directly from the Token Endpoint.
  50. * If the `OAuth2AccessToken` is expired, it is refreshed (or renewed) if an `OAuth2AuthorizedClientProvider` is available to perform the authorization
  51. The following code shows an example of how to configure `WebClient` with OAuth 2.0 Client support:
  52. [tabs]
  53. ======
  54. Java::
  55. +
  56. [source,java,role="primary"]
  57. ----
  58. @Bean
  59. WebClient webClient(OAuth2AuthorizedClientManager authorizedClientManager) {
  60. ServletOAuth2AuthorizedClientExchangeFilterFunction oauth2Client =
  61. new ServletOAuth2AuthorizedClientExchangeFilterFunction(authorizedClientManager);
  62. return WebClient.builder()
  63. .apply(oauth2Client.oauth2Configuration())
  64. .build();
  65. }
  66. ----
  67. Kotlin::
  68. +
  69. [source,kotlin,role="secondary"]
  70. ----
  71. @Bean
  72. fun webClient(authorizedClientManager: OAuth2AuthorizedClientManager?): WebClient {
  73. val oauth2Client = ServletOAuth2AuthorizedClientExchangeFilterFunction(authorizedClientManager)
  74. return WebClient.builder()
  75. .apply(oauth2Client.oauth2Configuration())
  76. .build()
  77. }
  78. ----
  79. ======
  80. === Providing the Authorized Client
  81. The `ServletOAuth2AuthorizedClientExchangeFilterFunction` 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 String index(@RegisteredOAuth2AuthorizedClient("okta") OAuth2AuthorizedClient authorizedClient) {
  91. String resourceUri = ...
  92. String body = webClient
  93. .get()
  94. .uri(resourceUri)
  95. .attributes(oauth2AuthorizedClient(authorizedClient)) <1>
  96. .retrieve()
  97. .bodyToMono(String.class)
  98. .block();
  99. ...
  100. return "index";
  101. }
  102. ----
  103. Kotlin::
  104. +
  105. [source,kotlin,role="secondary"]
  106. ----
  107. @GetMapping("/")
  108. fun index(@RegisteredOAuth2AuthorizedClient("okta") authorizedClient: OAuth2AuthorizedClient): String {
  109. val resourceUri: String = ...
  110. val body: String = webClient
  111. .get()
  112. .uri(resourceUri)
  113. .attributes(oauth2AuthorizedClient(authorizedClient)) <1>
  114. .retrieve()
  115. .bodyToMono()
  116. .block()
  117. ...
  118. return "index"
  119. }
  120. ----
  121. ======
  122. <1> `oauth2AuthorizedClient()` is a `static` method in `ServletOAuth2AuthorizedClientExchangeFilterFunction`.
  123. The following code shows how to set the `ClientRegistration.getRegistrationId()` as a request attribute:
  124. [tabs]
  125. ======
  126. Java::
  127. +
  128. [source,java,role="primary"]
  129. ----
  130. @GetMapping("/")
  131. public String index() {
  132. String resourceUri = ...
  133. String body = webClient
  134. .get()
  135. .uri(resourceUri)
  136. .attributes(clientRegistrationId("okta")) <1>
  137. .retrieve()
  138. .bodyToMono(String.class)
  139. .block();
  140. ...
  141. return "index";
  142. }
  143. ----
  144. Kotlin::
  145. +
  146. [source,kotlin,role="secondary"]
  147. ----
  148. @GetMapping("/")
  149. fun index(): String {
  150. val resourceUri: String = ...
  151. val body: String = webClient
  152. .get()
  153. .uri(resourceUri)
  154. .attributes(clientRegistrationId("okta")) <1>
  155. .retrieve()
  156. .bodyToMono()
  157. .block()
  158. ...
  159. return "index"
  160. }
  161. ----
  162. ======
  163. <1> `clientRegistrationId()` is a `static` method in `ServletOAuth2AuthorizedClientExchangeFilterFunction`.
  164. The following code shows how to set an `Authentication` as a request attribute:
  165. [tabs]
  166. ======
  167. Java::
  168. +
  169. [source,java,role="primary"]
  170. ----
  171. @GetMapping("/")
  172. public String index() {
  173. String resourceUri = ...
  174. Authentication anonymousAuthentication = new AnonymousAuthenticationToken(
  175. "anonymous", "anonymousUser", AuthorityUtils.createAuthorityList("ROLE_ANONYMOUS"));
  176. String body = webClient
  177. .get()
  178. .uri(resourceUri)
  179. .attributes(authentication(anonymousAuthentication)) <1>
  180. .retrieve()
  181. .bodyToMono(String.class)
  182. .block();
  183. ...
  184. return "index";
  185. }
  186. ----
  187. Kotlin::
  188. +
  189. [source,kotlin,role="secondary"]
  190. ----
  191. @GetMapping("/")
  192. fun index(): String {
  193. val resourceUri: String = ...
  194. val anonymousAuthentication: Authentication = AnonymousAuthenticationToken(
  195. "anonymous", "anonymousUser", AuthorityUtils.createAuthorityList("ROLE_ANONYMOUS"))
  196. val body: String = webClient
  197. .get()
  198. .uri(resourceUri)
  199. .attributes(authentication(anonymousAuthentication)) <1>
  200. .retrieve()
  201. .bodyToMono()
  202. .block()
  203. ...
  204. return "index"
  205. }
  206. ----
  207. ======
  208. <1> `authentication()` is a `static` method in `ServletOAuth2AuthorizedClientExchangeFilterFunction`.
  209. [WARNING]
  210. It is recommended to be cautious with this feature since all HTTP requests will receive an access token bound to the provided principal.
  211. === Defaulting the Authorized Client
  212. If neither `OAuth2AuthorizedClient` or `ClientRegistration.getRegistrationId()` is provided as a request attribute, the `ServletOAuth2AuthorizedClientExchangeFilterFunction` can determine the _default_ client to use, depending on its configuration.
  213. If `setDefaultOAuth2AuthorizedClient(true)` is configured and the user has authenticated by using `HttpSecurity.oauth2Login()`, the `OAuth2AccessToken` associated with the current `OAuth2AuthenticationToken` is used.
  214. The following code shows the specific configuration:
  215. [tabs]
  216. ======
  217. Java::
  218. +
  219. [source,java,role="primary"]
  220. ----
  221. @Bean
  222. WebClient webClient(OAuth2AuthorizedClientManager authorizedClientManager) {
  223. ServletOAuth2AuthorizedClientExchangeFilterFunction oauth2Client =
  224. new ServletOAuth2AuthorizedClientExchangeFilterFunction(authorizedClientManager);
  225. oauth2Client.setDefaultOAuth2AuthorizedClient(true);
  226. return WebClient.builder()
  227. .apply(oauth2Client.oauth2Configuration())
  228. .build();
  229. }
  230. ----
  231. Kotlin::
  232. +
  233. [source,kotlin,role="secondary"]
  234. ----
  235. @Bean
  236. fun webClient(authorizedClientManager: OAuth2AuthorizedClientManager?): WebClient {
  237. val oauth2Client = ServletOAuth2AuthorizedClientExchangeFilterFunction(authorizedClientManager)
  238. oauth2Client.setDefaultOAuth2AuthorizedClient(true)
  239. return WebClient.builder()
  240. .apply(oauth2Client.oauth2Configuration())
  241. .build()
  242. }
  243. ----
  244. ======
  245. [WARNING]
  246. ====
  247. Be cautious with this feature, since all HTTP requests receive the access token.
  248. ====
  249. Alternatively, if `setDefaultClientRegistrationId("okta")` is configured with a valid `ClientRegistration`, the `OAuth2AccessToken` associated with the `OAuth2AuthorizedClient` is used.
  250. The following code shows the specific configuration:
  251. [tabs]
  252. ======
  253. Java::
  254. +
  255. [source,java,role="primary"]
  256. ----
  257. @Bean
  258. WebClient webClient(OAuth2AuthorizedClientManager authorizedClientManager) {
  259. ServletOAuth2AuthorizedClientExchangeFilterFunction oauth2Client =
  260. new ServletOAuth2AuthorizedClientExchangeFilterFunction(authorizedClientManager);
  261. oauth2Client.setDefaultClientRegistrationId("okta");
  262. return WebClient.builder()
  263. .apply(oauth2Client.oauth2Configuration())
  264. .build();
  265. }
  266. ----
  267. Kotlin::
  268. +
  269. [source,kotlin,role="secondary"]
  270. ----
  271. @Bean
  272. fun webClient(authorizedClientManager: OAuth2AuthorizedClientManager?): WebClient {
  273. val oauth2Client = ServletOAuth2AuthorizedClientExchangeFilterFunction(authorizedClientManager)
  274. oauth2Client.setDefaultClientRegistrationId("okta")
  275. return WebClient.builder()
  276. .apply(oauth2Client.oauth2Configuration())
  277. .build()
  278. }
  279. ----
  280. ======
  281. [WARNING]
  282. ====
  283. Be cautious with this feature, since all HTTP requests receive the access token.
  284. ====