authorization-grants.adoc 48 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318
  1. [[oauth2-client-authorization-grants]]
  2. = [[oauth2Client-auth-grant-support]]Authorization Grant Support
  3. This section describes Spring Security's support for authorization grants.
  4. [[oauth2-client-authorization-code]]
  5. == [[oauth2Client-auth-code-grant]]Authorization Code
  6. [NOTE]
  7. Please refer to the OAuth 2.0 Authorization Framework for further details on the https://tools.ietf.org/html/rfc6749#section-1.3.1[Authorization Code] grant.
  8. [[oauth2-client-authorization-code-authorization]]
  9. === Obtaining Authorization
  10. [NOTE]
  11. Please refer to the https://tools.ietf.org/html/rfc6749#section-4.1.1[Authorization Request/Response] protocol flow for the Authorization Code grant.
  12. [[oauth2-client-authorization-code-authorization-request]]
  13. === Initiating the Authorization Request
  14. The `OAuth2AuthorizationRequestRedirectWebFilter` uses a `ServerOAuth2AuthorizationRequestResolver` to resolve an `OAuth2AuthorizationRequest` and initiate the Authorization Code grant flow by redirecting the end-user's user-agent to the Authorization Server's Authorization Endpoint.
  15. The primary role of the `ServerOAuth2AuthorizationRequestResolver` is to resolve an `OAuth2AuthorizationRequest` from the provided web request.
  16. The default implementation `DefaultServerOAuth2AuthorizationRequestResolver` matches on the (default) path `+/oauth2/authorization/{registrationId}+` extracting the `registrationId` and using it to build the `OAuth2AuthorizationRequest` for the associated `ClientRegistration`.
  17. Given the following Spring Boot properties for an OAuth 2.0 Client registration:
  18. [source,yaml,attrs="-attributes"]
  19. ----
  20. spring:
  21. security:
  22. oauth2:
  23. client:
  24. registration:
  25. okta:
  26. client-id: okta-client-id
  27. client-secret: okta-client-secret
  28. authorization-grant-type: authorization_code
  29. redirect-uri: "{baseUrl}/authorized/okta"
  30. scope: read, write
  31. provider:
  32. okta:
  33. authorization-uri: https://dev-1234.oktapreview.com/oauth2/v1/authorize
  34. token-uri: https://dev-1234.oktapreview.com/oauth2/v1/token
  35. ----
  36. A request with the base path `/oauth2/authorization/okta` will initiate the Authorization Request redirect by the `OAuth2AuthorizationRequestRedirectWebFilter` and ultimately start the Authorization Code grant flow.
  37. [NOTE]
  38. The `AuthorizationCodeReactiveOAuth2AuthorizedClientProvider` is an implementation of `ReactiveOAuth2AuthorizedClientProvider` for the Authorization Code grant,
  39. which also initiates the Authorization Request redirect by the `OAuth2AuthorizationRequestRedirectWebFilter`.
  40. If the OAuth 2.0 Client is a https://tools.ietf.org/html/rfc6749#section-2.1[Public Client], then configure the OAuth 2.0 Client registration as follows:
  41. [source,yaml,attrs="-attributes"]
  42. ----
  43. spring:
  44. security:
  45. oauth2:
  46. client:
  47. registration:
  48. okta:
  49. client-id: okta-client-id
  50. client-authentication-method: none
  51. authorization-grant-type: authorization_code
  52. redirect-uri: "{baseUrl}/authorized/okta"
  53. # ...
  54. ----
  55. Public Clients are supported using https://tools.ietf.org/html/rfc7636[Proof Key for Code Exchange] (PKCE).
  56. If the client is running in an untrusted environment (eg. native application or web browser-based application) and therefore incapable of maintaining the confidentiality of it's credentials, PKCE will automatically be used when the following conditions are true:
  57. . `client-secret` is omitted (or empty)
  58. . `client-authentication-method` is set to "none" (`ClientAuthenticationMethod.NONE`)
  59. [TIP]
  60. If the OAuth 2.0 Provider supports PKCE for https://tools.ietf.org/html/rfc6749#section-2.1[Confidential Clients], you may (optionally) configure it using `DefaultServerOAuth2AuthorizationRequestResolver.setAuthorizationRequestCustomizer(OAuth2AuthorizationRequestCustomizers.withPkce())`.
  61. [[oauth2-client-authorization-code-redirect-uri]]
  62. [[oauth2Client-auth-code-redirect-uri]]The `DefaultServerOAuth2AuthorizationRequestResolver` also supports `URI` template variables for the `redirect-uri` using `UriComponentsBuilder`.
  63. The following configuration uses all the supported `URI` template variables:
  64. [source,yaml,attrs="-attributes"]
  65. ----
  66. spring:
  67. security:
  68. oauth2:
  69. client:
  70. registration:
  71. okta:
  72. # ...
  73. redirect-uri: "{baseScheme}://{baseHost}{basePort}{basePath}/authorized/{registrationId}"
  74. # ...
  75. ----
  76. [NOTE]
  77. `+{baseUrl}+` resolves to `+{baseScheme}://{baseHost}{basePort}{basePath}+`
  78. Configuring the `redirect-uri` with `URI` template variables is especially useful when the OAuth 2.0 Client is running behind a xref:features/exploits/http.adoc#http-proxy-server[Proxy Server].
  79. This ensures that the `X-Forwarded-*` headers are used when expanding the `redirect-uri`.
  80. [[oauth2-client-authorization-code-authorization-request-resolver]]
  81. === Customizing the Authorization Request
  82. One of the primary use cases a `ServerOAuth2AuthorizationRequestResolver` can realize is the ability to customize the Authorization Request with additional parameters above the standard parameters defined in the OAuth 2.0 Authorization Framework.
  83. For example, OpenID Connect defines additional OAuth 2.0 request parameters for the https://openid.net/specs/openid-connect-core-1_0.html#AuthRequest[Authorization Code Flow] extending from the standard parameters defined in the https://tools.ietf.org/html/rfc6749#section-4.1.1[OAuth 2.0 Authorization Framework].
  84. One of those extended parameters is the `prompt` parameter.
  85. [NOTE]
  86. ====
  87. The `prompt` parameter is optional. Space delimited, case sensitive list of ASCII string values that specifies whether the Authorization Server prompts the End-User for re-authentication and consent. The defined values are: `none`, `login`, `consent`, and `select_account`.
  88. ====
  89. The following example shows how to configure the `DefaultServerOAuth2AuthorizationRequestResolver` with a `Consumer<OAuth2AuthorizationRequest.Builder>` that customizes the Authorization Request for `oauth2Login()`, by including the request parameter `prompt=consent`.
  90. [tabs]
  91. ======
  92. Java::
  93. +
  94. [source,java,role="primary"]
  95. ----
  96. @Configuration
  97. @EnableWebFluxSecurity
  98. public class OAuth2LoginSecurityConfig {
  99. @Autowired
  100. private ReactiveClientRegistrationRepository clientRegistrationRepository;
  101. @Bean
  102. public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {
  103. http
  104. .authorizeExchange(authorize -> authorize
  105. .anyExchange().authenticated()
  106. )
  107. .oauth2Login(oauth2 -> oauth2
  108. .authorizationRequestResolver(
  109. authorizationRequestResolver(this.clientRegistrationRepository)
  110. )
  111. );
  112. return http.build();
  113. }
  114. private ServerOAuth2AuthorizationRequestResolver authorizationRequestResolver(
  115. ReactiveClientRegistrationRepository clientRegistrationRepository) {
  116. DefaultServerOAuth2AuthorizationRequestResolver authorizationRequestResolver =
  117. new DefaultServerOAuth2AuthorizationRequestResolver(
  118. clientRegistrationRepository);
  119. authorizationRequestResolver.setAuthorizationRequestCustomizer(
  120. authorizationRequestCustomizer());
  121. return authorizationRequestResolver;
  122. }
  123. private Consumer<OAuth2AuthorizationRequest.Builder> authorizationRequestCustomizer() {
  124. return customizer -> customizer
  125. .additionalParameters(params -> params.put("prompt", "consent"));
  126. }
  127. }
  128. ----
  129. Kotlin::
  130. +
  131. [source,kotlin,role="secondary"]
  132. ----
  133. @Configuration
  134. @EnableWebFluxSecurity
  135. class SecurityConfig {
  136. @Autowired
  137. private lateinit var customClientRegistrationRepository: ReactiveClientRegistrationRepository
  138. @Bean
  139. fun securityFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {
  140. http {
  141. authorizeExchange {
  142. authorize(anyExchange, authenticated)
  143. }
  144. oauth2Login {
  145. authorizationRequestResolver = authorizationRequestResolver(customClientRegistrationRepository)
  146. }
  147. }
  148. return http.build()
  149. }
  150. private fun authorizationRequestResolver(
  151. clientRegistrationRepository: ReactiveClientRegistrationRepository): ServerOAuth2AuthorizationRequestResolver {
  152. val authorizationRequestResolver = DefaultServerOAuth2AuthorizationRequestResolver(
  153. clientRegistrationRepository)
  154. authorizationRequestResolver.setAuthorizationRequestCustomizer(
  155. authorizationRequestCustomizer())
  156. return authorizationRequestResolver
  157. }
  158. private fun authorizationRequestCustomizer(): Consumer<OAuth2AuthorizationRequest.Builder> {
  159. return Consumer { customizer ->
  160. customizer
  161. .additionalParameters { params -> params["prompt"] = "consent" }
  162. }
  163. }
  164. }
  165. ----
  166. ======
  167. For the simple use case, where the additional request parameter is always the same for a specific provider, it may be added directly in the `authorization-uri` property.
  168. For example, if the value for the request parameter `prompt` is always `consent` for the provider `okta`, than simply configure as follows:
  169. [source,yaml]
  170. ----
  171. spring:
  172. security:
  173. oauth2:
  174. client:
  175. provider:
  176. okta:
  177. authorization-uri: https://dev-1234.oktapreview.com/oauth2/v1/authorize?prompt=consent
  178. ----
  179. The preceding example shows the common use case of adding a custom parameter on top of the standard parameters.
  180. Alternatively, if your requirements are more advanced, you can take full control in building the Authorization Request URI by simply overriding the `OAuth2AuthorizationRequest.authorizationRequestUri` property.
  181. [TIP]
  182. `OAuth2AuthorizationRequest.Builder.build()` constructs the `OAuth2AuthorizationRequest.authorizationRequestUri`, which represents the Authorization Request URI including all query parameters using the `application/x-www-form-urlencoded` format.
  183. The following example shows a variation of `authorizationRequestCustomizer()` from the preceding example, and instead overrides the `OAuth2AuthorizationRequest.authorizationRequestUri` property.
  184. [tabs]
  185. ======
  186. Java::
  187. +
  188. [source,java,role="primary"]
  189. ----
  190. private Consumer<OAuth2AuthorizationRequest.Builder> authorizationRequestCustomizer() {
  191. return customizer -> customizer
  192. .authorizationRequestUri(uriBuilder -> uriBuilder
  193. .queryParam("prompt", "consent").build());
  194. }
  195. ----
  196. Kotlin::
  197. +
  198. [source,kotlin,role="secondary"]
  199. ----
  200. private fun authorizationRequestCustomizer(): Consumer<OAuth2AuthorizationRequest.Builder> {
  201. return Consumer { customizer: OAuth2AuthorizationRequest.Builder ->
  202. customizer
  203. .authorizationRequestUri { uriBuilder: UriBuilder ->
  204. uriBuilder
  205. .queryParam("prompt", "consent").build()
  206. }
  207. }
  208. }
  209. ----
  210. ======
  211. [[oauth2-client-authorization-code-authorization-request-repository]]
  212. === Storing the Authorization Request
  213. The `ServerAuthorizationRequestRepository` is responsible for the persistence of the `OAuth2AuthorizationRequest` from the time the Authorization Request is initiated to the time the Authorization Response is received (the callback).
  214. [TIP]
  215. The `OAuth2AuthorizationRequest` is used to correlate and validate the Authorization Response.
  216. The default implementation of `ServerAuthorizationRequestRepository` is `WebSessionOAuth2ServerAuthorizationRequestRepository`, which stores the `OAuth2AuthorizationRequest` in the `WebSession`.
  217. If you have a custom implementation of `ServerAuthorizationRequestRepository`, you may configure it as shown in the following example:
  218. .ServerAuthorizationRequestRepository Configuration
  219. [tabs]
  220. ======
  221. Java::
  222. +
  223. [source,java,role="primary"]
  224. ----
  225. @Configuration
  226. @EnableWebFluxSecurity
  227. public class OAuth2ClientSecurityConfig {
  228. @Bean
  229. public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {
  230. http
  231. .oauth2Client(oauth2 -> oauth2
  232. .authorizationRequestRepository(this.authorizationRequestRepository())
  233. // ...
  234. );
  235. return http.build();
  236. }
  237. }
  238. ----
  239. Kotlin::
  240. +
  241. [source,kotlin,role="secondary"]
  242. ----
  243. @Configuration
  244. @EnableWebFluxSecurity
  245. class OAuth2ClientSecurityConfig {
  246. @Bean
  247. fun securityFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {
  248. http {
  249. oauth2Client {
  250. authorizationRequestRepository = authorizationRequestRepository()
  251. }
  252. }
  253. return http.build()
  254. }
  255. }
  256. ----
  257. ======
  258. [[oauth2-client-authorization-code-access-token]]
  259. === Requesting an Access Token
  260. [NOTE]
  261. Please refer to the https://tools.ietf.org/html/rfc6749#section-4.1.3[Access Token Request/Response] protocol flow for the Authorization Code grant.
  262. The default implementation of `ReactiveOAuth2AccessTokenResponseClient` for the Authorization Code grant is `WebClientReactiveAuthorizationCodeTokenResponseClient`, which uses a `WebClient` for exchanging an authorization code for an access token at the Authorization Server’s Token Endpoint.
  263. :section-id: authorization-code
  264. :grant-type: Authorization Code
  265. :class-name: WebClientReactiveAuthorizationCodeTokenResponseClient
  266. :grant-request: OAuth2AuthorizationCodeGrantRequest
  267. :leveloffset: +1
  268. include::partial$reactive/oauth2/client/web-client-access-token-response-client.adoc[]
  269. :leveloffset: -1
  270. [[oauth2-client-authorization-code-access-token-response-client-dsl]]
  271. === Customize using the DSL
  272. Whether you customize `{class-name}` or provide your own implementation of `ReactiveOAuth2AccessTokenResponseClient`, you can configure it using the DSL (as an alternative to <<oauth2-client-authorization-code-access-token-response-client-bean,publishing a bean>>) as shown in the following example:
  273. .Access Token Response Configuration via DSL
  274. [tabs]
  275. ======
  276. Java::
  277. +
  278. [source,java,role="primary"]
  279. ----
  280. @Configuration
  281. @EnableWebFluxSecurity
  282. public class OAuth2ClientSecurityConfig {
  283. @Bean
  284. public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {
  285. http
  286. .oauth2Client(oauth2 -> oauth2
  287. .authenticationManager(this.authorizationCodeAuthenticationManager())
  288. // ...
  289. );
  290. return http.build();
  291. }
  292. private ReactiveAuthenticationManager authorizationCodeAuthenticationManager() {
  293. WebClientReactiveAuthorizationCodeTokenResponseClient accessTokenResponseClient =
  294. new WebClientReactiveAuthorizationCodeTokenResponseClient();
  295. // ...
  296. return new OAuth2AuthorizationCodeReactiveAuthenticationManager(accessTokenResponseClient);
  297. }
  298. }
  299. ----
  300. Kotlin::
  301. +
  302. [source,kotlin,role="secondary"]
  303. ----
  304. @Configuration
  305. @EnableWebFluxSecurity
  306. class OAuth2ClientSecurityConfig {
  307. @Bean
  308. fun securityFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {
  309. http {
  310. oauth2Client {
  311. authenticationManager = authorizationCodeAuthenticationManager()
  312. }
  313. }
  314. return http.build()
  315. }
  316. private fun authorizationCodeAuthenticationManager(): ReactiveAuthenticationManager {
  317. val accessTokenResponseClient = WebClientReactiveAuthorizationCodeTokenResponseClient()
  318. // ...
  319. return OAuth2AuthorizationCodeReactiveAuthenticationManager(accessTokenResponseClient)
  320. }
  321. }
  322. ----
  323. ======
  324. [[oauth2-client-refresh-token]]
  325. == [[oauth2Client-refresh-token-grant]]Refresh Token
  326. [NOTE]
  327. Please refer to the OAuth 2.0 Authorization Framework for further details on the https://tools.ietf.org/html/rfc6749#section-1.5[Refresh Token].
  328. [[oauth2-client-refresh-token-access-token]]
  329. === Refreshing an Access Token
  330. [NOTE]
  331. Please refer to the https://tools.ietf.org/html/rfc6749#section-6[Access Token Request/Response] protocol flow for the Refresh Token grant.
  332. The default implementation of `ReactiveOAuth2AccessTokenResponseClient` for the Refresh Token grant is `WebClientReactiveRefreshTokenTokenResponseClient`, which uses a `WebClient` when refreshing an access token at the Authorization Server’s Token Endpoint.
  333. :section-id: refresh-token
  334. :grant-type: Refresh Token
  335. :class-name: WebClientReactiveRefreshTokenTokenResponseClient
  336. :grant-request: OAuth2RefreshTokenGrantRequest
  337. :leveloffset: +1
  338. include::partial$reactive/oauth2/client/web-client-access-token-response-client.adoc[]
  339. :leveloffset: -1
  340. [[oauth2-client-refresh-token-authorized-client-provider-builder]]
  341. === Customize using the Builder
  342. Whether you customize `WebClientReactiveRefreshTokenTokenResponseClient` or provide your own implementation of `ReactiveOAuth2AccessTokenResponseClient`, you can configure it using the `ReactiveOAuth2AuthorizedClientProviderBuilder` (as an alternative to <<oauth2-client-refresh-token-access-token-response-client-bean,publishing a bean>>) as follows:
  343. .Access Token Response Configuration via Builder
  344. [tabs]
  345. ======
  346. Java::
  347. +
  348. [source,java,role="primary"]
  349. ----
  350. // Customize
  351. ReactiveOAuth2AccessTokenResponseClient<OAuth2RefreshTokenGrantRequest> refreshTokenTokenResponseClient = ...
  352. ReactiveOAuth2AuthorizedClientProvider authorizedClientProvider =
  353. ReactiveOAuth2AuthorizedClientProviderBuilder.builder()
  354. .authorizationCode()
  355. .refreshToken(configurer -> configurer.accessTokenResponseClient(refreshTokenTokenResponseClient))
  356. .build();
  357. // ...
  358. authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider);
  359. ----
  360. Kotlin::
  361. +
  362. [source,kotlin,role="secondary"]
  363. ----
  364. // Customize
  365. val refreshTokenTokenResponseClient: ReactiveOAuth2AccessTokenResponseClient<OAuth2RefreshTokenGrantRequest> = ...
  366. val authorizedClientProvider: ReactiveOAuth2AuthorizedClientProvider = ReactiveOAuth2AuthorizedClientProviderBuilder.builder()
  367. .authorizationCode()
  368. .refreshToken { it.accessTokenResponseClient(refreshTokenTokenResponseClient) }
  369. .build()
  370. // ...
  371. authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider)
  372. ----
  373. ======
  374. [NOTE]
  375. `ReactiveOAuth2AuthorizedClientProviderBuilder.builder().refreshToken()` configures a `RefreshTokenReactiveOAuth2AuthorizedClientProvider`,
  376. which is an implementation of a `ReactiveOAuth2AuthorizedClientProvider` for the Refresh Token grant.
  377. The `OAuth2RefreshToken` may optionally be returned in the Access Token Response for the `authorization_code` and `password` grant types.
  378. If the `OAuth2AuthorizedClient.getRefreshToken()` is available and the `OAuth2AuthorizedClient.getAccessToken()` is expired, it will automatically be refreshed by the `RefreshTokenReactiveOAuth2AuthorizedClientProvider`.
  379. [[oauth2-client-client-credentials]]
  380. == [[oauth2Client-client-creds-grant]]Client Credentials
  381. [NOTE]
  382. Please refer to the OAuth 2.0 Authorization Framework for further details on the https://tools.ietf.org/html/rfc6749#section-1.3.4[Client Credentials] grant.
  383. [[oauth2-client-client-credentials-access-token]]
  384. === Requesting an Access Token
  385. [NOTE]
  386. Please refer to the https://tools.ietf.org/html/rfc6749#section-4.4.2[Access Token Request/Response] protocol flow for the Client Credentials grant.
  387. The default implementation of `ReactiveOAuth2AccessTokenResponseClient` for the Client Credentials grant is `WebClientReactiveClientCredentialsTokenResponseClient`, which uses a `WebClient` when requesting an access token at the Authorization Server’s Token Endpoint.
  388. :section-id: client-credentials
  389. :grant-type: Client Credentials
  390. :class-name: WebClientReactiveClientCredentialsTokenResponseClient
  391. :grant-request: OAuth2ClientCredentialsGrantRequest
  392. :leveloffset: +1
  393. include::partial$reactive/oauth2/client/web-client-access-token-response-client.adoc[]
  394. :leveloffset: -1
  395. [[oauth2-client-client-credentials-authorized-client-provider-builder]]
  396. === Customize using the Builder
  397. Whether you customize `WebClientReactiveClientCredentialsTokenResponseClient` or provide your own implementation of `ReactiveOAuth2AccessTokenResponseClient`, you can configure it using the `ReactiveOAuth2AuthorizedClientProviderBuilder` (as an alternative to <<oauth2-client-client-credentials-access-token-response-client-bean,publishing a bean>>) as follows:
  398. .Access Token Response Configuration via Builder
  399. [tabs]
  400. ======
  401. Java::
  402. +
  403. [source,java,role="primary"]
  404. ----
  405. // Customize
  406. ReactiveOAuth2AccessTokenResponseClient<OAuth2ClientCredentialsGrantRequest> clientCredentialsTokenResponseClient = ...
  407. ReactiveOAuth2AuthorizedClientProvider authorizedClientProvider =
  408. ReactiveOAuth2AuthorizedClientProviderBuilder.builder()
  409. .clientCredentials(configurer -> configurer.accessTokenResponseClient(clientCredentialsTokenResponseClient))
  410. .build();
  411. // ...
  412. authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider);
  413. ----
  414. Kotlin::
  415. +
  416. [source,kotlin,role="secondary"]
  417. ----
  418. // Customize
  419. val clientCredentialsTokenResponseClient: ReactiveOAuth2AccessTokenResponseClient<OAuth2ClientCredentialsGrantRequest> = ...
  420. val authorizedClientProvider: ReactiveOAuth2AuthorizedClientProvider = ReactiveOAuth2AuthorizedClientProviderBuilder.builder()
  421. .clientCredentials { it.accessTokenResponseClient(clientCredentialsTokenResponseClient) }
  422. .build()
  423. // ...
  424. authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider)
  425. ----
  426. ======
  427. [NOTE]
  428. `ReactiveOAuth2AuthorizedClientProviderBuilder.builder().clientCredentials()` configures a `ClientCredentialsReactiveOAuth2AuthorizedClientProvider`,
  429. which is an implementation of a `ReactiveOAuth2AuthorizedClientProvider` for the Client Credentials grant.
  430. [[oauth2-client-client-credentials-authorized-client-manager]]
  431. === Using the Access Token
  432. Given the following Spring Boot properties for an OAuth 2.0 Client registration:
  433. [source,yaml]
  434. ----
  435. spring:
  436. security:
  437. oauth2:
  438. client:
  439. registration:
  440. okta:
  441. client-id: okta-client-id
  442. client-secret: okta-client-secret
  443. authorization-grant-type: client_credentials
  444. scope: read, write
  445. provider:
  446. okta:
  447. token-uri: https://dev-1234.oktapreview.com/oauth2/v1/token
  448. ----
  449. ...and the `ReactiveOAuth2AuthorizedClientManager` `@Bean`:
  450. [tabs]
  451. ======
  452. Java::
  453. +
  454. [source,java,role="primary"]
  455. ----
  456. @Bean
  457. public ReactiveOAuth2AuthorizedClientManager authorizedClientManager(
  458. ReactiveClientRegistrationRepository clientRegistrationRepository,
  459. ServerOAuth2AuthorizedClientRepository authorizedClientRepository) {
  460. ReactiveOAuth2AuthorizedClientProvider authorizedClientProvider =
  461. ReactiveOAuth2AuthorizedClientProviderBuilder.builder()
  462. .clientCredentials()
  463. .build();
  464. DefaultReactiveOAuth2AuthorizedClientManager authorizedClientManager =
  465. new DefaultReactiveOAuth2AuthorizedClientManager(
  466. clientRegistrationRepository, authorizedClientRepository);
  467. authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider);
  468. return authorizedClientManager;
  469. }
  470. ----
  471. Kotlin::
  472. +
  473. [source,kotlin,role="secondary"]
  474. ----
  475. @Bean
  476. fun authorizedClientManager(
  477. clientRegistrationRepository: ReactiveClientRegistrationRepository,
  478. authorizedClientRepository: ServerOAuth2AuthorizedClientRepository): ReactiveOAuth2AuthorizedClientManager {
  479. val authorizedClientProvider: ReactiveOAuth2AuthorizedClientProvider = ReactiveOAuth2AuthorizedClientProviderBuilder.builder()
  480. .clientCredentials()
  481. .build()
  482. val authorizedClientManager = DefaultReactiveOAuth2AuthorizedClientManager(
  483. clientRegistrationRepository, authorizedClientRepository)
  484. authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider)
  485. return authorizedClientManager
  486. }
  487. ----
  488. ======
  489. You may obtain the `OAuth2AccessToken` as follows:
  490. [tabs]
  491. ======
  492. Java::
  493. +
  494. [source,java,role="primary"]
  495. ----
  496. @Controller
  497. public class OAuth2ClientController {
  498. @Autowired
  499. private ReactiveOAuth2AuthorizedClientManager authorizedClientManager;
  500. @GetMapping("/")
  501. public Mono<String> index(Authentication authentication, ServerWebExchange exchange) {
  502. OAuth2AuthorizeRequest authorizeRequest = OAuth2AuthorizeRequest.withClientRegistrationId("okta")
  503. .principal(authentication)
  504. .attribute(ServerWebExchange.class.getName(), exchange)
  505. .build();
  506. return this.authorizedClientManager.authorize(authorizeRequest)
  507. .map(OAuth2AuthorizedClient::getAccessToken)
  508. // ...
  509. .thenReturn("index");
  510. }
  511. }
  512. ----
  513. Kotlin::
  514. +
  515. [source,kotlin,role="secondary"]
  516. ----
  517. class OAuth2ClientController {
  518. @Autowired
  519. private lateinit var authorizedClientManager: ReactiveOAuth2AuthorizedClientManager
  520. @GetMapping("/")
  521. fun index(authentication: Authentication, exchange: ServerWebExchange): Mono<String> {
  522. val authorizeRequest = OAuth2AuthorizeRequest.withClientRegistrationId("okta")
  523. .principal(authentication)
  524. .attribute(ServerWebExchange::class.java.name, exchange)
  525. .build()
  526. return authorizedClientManager.authorize(authorizeRequest)
  527. .map { it.accessToken }
  528. // ...
  529. .thenReturn("index")
  530. }
  531. }
  532. ----
  533. ======
  534. [NOTE]
  535. `ServerWebExchange` is an OPTIONAL attribute.
  536. If not provided, it will be obtained from the https://projectreactor.io/docs/core/release/reference/#context[Reactor's Context] via the key `ServerWebExchange.class`.
  537. [[oauth2-client-password]]
  538. == [[oauth2Client-password-grant]]Resource Owner Password Credentials
  539. [NOTE]
  540. Please refer to the OAuth 2.0 Authorization Framework for further details on the https://tools.ietf.org/html/rfc6749#section-1.3.3[Resource Owner Password Credentials] grant.
  541. [[oauth2-client-password-access-token]]
  542. === Requesting an Access Token
  543. [NOTE]
  544. Please refer to the https://tools.ietf.org/html/rfc6749#section-4.3.2[Access Token Request/Response] protocol flow for the Resource Owner Password Credentials grant.
  545. The default implementation of `ReactiveOAuth2AccessTokenResponseClient` for the Resource Owner Password Credentials grant is `WebClientReactivePasswordTokenResponseClient`, which uses a `WebClient` when requesting an access token at the Authorization Server’s Token Endpoint.
  546. [CAUTION]
  547. ====
  548. The `WebClientReactivePasswordTokenResponseClient` class and support for the Resource Owner Password Credentials grant are deprecated.
  549. This section will be removed in Spring Security 7.
  550. ====
  551. :section-id: password
  552. :grant-type: Password
  553. :class-name: WebClientReactivePasswordTokenResponseClient
  554. :grant-request: OAuth2PasswordGrantRequest
  555. :leveloffset: +1
  556. include::partial$reactive/oauth2/client/web-client-access-token-response-client.adoc[]
  557. :leveloffset: -1
  558. [[oauth2-client-password-authorized-client-provider-builder]]
  559. === Customize using the Builder
  560. Whether you customize `WebClientReactivePasswordTokenResponseClient` or provide your own implementation of `ReactiveOAuth2AccessTokenResponseClient`, you can configure it using the `ReactiveOAuth2AuthorizedClientProviderBuilder` (as an alternative to <<oauth2-client-password-access-token-response-client-bean,publishing a bean>>) as follows:
  561. .Access Token Response Configuration via Builder
  562. [tabs]
  563. ======
  564. Java::
  565. +
  566. [source,java,role="primary"]
  567. ----
  568. // Customize
  569. ReactiveOAuth2AccessTokenResponseClient<OAuth2PasswordGrantRequest> passwordTokenResponseClient = ...
  570. ReactiveOAuth2AuthorizedClientProvider authorizedClientProvider =
  571. ReactiveOAuth2AuthorizedClientProviderBuilder.builder()
  572. .password(configurer -> configurer.accessTokenResponseClient(passwordTokenResponseClient))
  573. .refreshToken()
  574. .build();
  575. // ...
  576. authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider);
  577. ----
  578. Kotlin::
  579. +
  580. [source,kotlin,role="secondary"]
  581. ----
  582. val passwordTokenResponseClient: ReactiveOAuth2AccessTokenResponseClient<OAuth2PasswordGrantRequest> = ...
  583. val authorizedClientProvider = ReactiveOAuth2AuthorizedClientProviderBuilder.builder()
  584. .password { it.accessTokenResponseClient(passwordTokenResponseClient) }
  585. .refreshToken()
  586. .build()
  587. // ...
  588. authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider)
  589. ----
  590. ======
  591. [NOTE]
  592. `ReactiveOAuth2AuthorizedClientProviderBuilder.builder().password()` configures a `PasswordReactiveOAuth2AuthorizedClientProvider`,
  593. which is an implementation of a `ReactiveOAuth2AuthorizedClientProvider` for the Resource Owner Password Credentials grant.
  594. [[oauth2-client-password-authorized-client-manager]]
  595. === Using the Access Token
  596. Given the following Spring Boot properties for an OAuth 2.0 Client registration:
  597. [source,yaml]
  598. ----
  599. spring:
  600. security:
  601. oauth2:
  602. client:
  603. registration:
  604. okta:
  605. client-id: okta-client-id
  606. client-secret: okta-client-secret
  607. authorization-grant-type: password
  608. scope: read, write
  609. provider:
  610. okta:
  611. token-uri: https://dev-1234.oktapreview.com/oauth2/v1/token
  612. ----
  613. ...and the `ReactiveOAuth2AuthorizedClientManager` `@Bean`:
  614. [tabs]
  615. ======
  616. Java::
  617. +
  618. [source,java,role="primary"]
  619. ----
  620. @Bean
  621. public ReactiveOAuth2AuthorizedClientManager authorizedClientManager(
  622. ReactiveClientRegistrationRepository clientRegistrationRepository,
  623. ServerOAuth2AuthorizedClientRepository authorizedClientRepository) {
  624. ReactiveOAuth2AuthorizedClientProvider authorizedClientProvider =
  625. ReactiveOAuth2AuthorizedClientProviderBuilder.builder()
  626. .password()
  627. .refreshToken()
  628. .build();
  629. DefaultReactiveOAuth2AuthorizedClientManager authorizedClientManager =
  630. new DefaultReactiveOAuth2AuthorizedClientManager(
  631. clientRegistrationRepository, authorizedClientRepository);
  632. authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider);
  633. // Assuming the `username` and `password` are supplied as `ServerHttpRequest` parameters,
  634. // map the `ServerHttpRequest` parameters to `OAuth2AuthorizationContext.getAttributes()`
  635. authorizedClientManager.setContextAttributesMapper(contextAttributesMapper());
  636. return authorizedClientManager;
  637. }
  638. private Function<OAuth2AuthorizeRequest, Mono<Map<String, Object>>> contextAttributesMapper() {
  639. return authorizeRequest -> {
  640. Map<String, Object> contextAttributes = Collections.emptyMap();
  641. ServerWebExchange exchange = authorizeRequest.getAttribute(ServerWebExchange.class.getName());
  642. ServerHttpRequest request = exchange.getRequest();
  643. String username = request.getQueryParams().getFirst(OAuth2ParameterNames.USERNAME);
  644. String password = request.getQueryParams().getFirst(OAuth2ParameterNames.PASSWORD);
  645. if (StringUtils.hasText(username) && StringUtils.hasText(password)) {
  646. contextAttributes = new HashMap<>();
  647. // `PasswordReactiveOAuth2AuthorizedClientProvider` requires both attributes
  648. contextAttributes.put(OAuth2AuthorizationContext.USERNAME_ATTRIBUTE_NAME, username);
  649. contextAttributes.put(OAuth2AuthorizationContext.PASSWORD_ATTRIBUTE_NAME, password);
  650. }
  651. return Mono.just(contextAttributes);
  652. };
  653. }
  654. ----
  655. Kotlin::
  656. +
  657. [source,kotlin,role="secondary"]
  658. ----
  659. @Bean
  660. fun authorizedClientManager(
  661. clientRegistrationRepository: ReactiveClientRegistrationRepository,
  662. authorizedClientRepository: ServerOAuth2AuthorizedClientRepository): ReactiveOAuth2AuthorizedClientManager {
  663. val authorizedClientProvider: ReactiveOAuth2AuthorizedClientProvider = ReactiveOAuth2AuthorizedClientProviderBuilder.builder()
  664. .password()
  665. .refreshToken()
  666. .build()
  667. val authorizedClientManager = DefaultReactiveOAuth2AuthorizedClientManager(
  668. clientRegistrationRepository, authorizedClientRepository)
  669. authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider)
  670. // Assuming the `username` and `password` are supplied as `ServerHttpRequest` parameters,
  671. // map the `ServerHttpRequest` parameters to `OAuth2AuthorizationContext.getAttributes()`
  672. authorizedClientManager.setContextAttributesMapper(contextAttributesMapper())
  673. return authorizedClientManager
  674. }
  675. private fun contextAttributesMapper(): Function<OAuth2AuthorizeRequest, Mono<MutableMap<String, Any>>> {
  676. return Function { authorizeRequest ->
  677. var contextAttributes: MutableMap<String, Any> = mutableMapOf()
  678. val exchange: ServerWebExchange = authorizeRequest.getAttribute(ServerWebExchange::class.java.name)!!
  679. val request: ServerHttpRequest = exchange.request
  680. val username: String? = request.queryParams.getFirst(OAuth2ParameterNames.USERNAME)
  681. val password: String? = request.queryParams.getFirst(OAuth2ParameterNames.PASSWORD)
  682. if (StringUtils.hasText(username) && StringUtils.hasText(password)) {
  683. contextAttributes = hashMapOf()
  684. // `PasswordReactiveOAuth2AuthorizedClientProvider` requires both attributes
  685. contextAttributes[OAuth2AuthorizationContext.USERNAME_ATTRIBUTE_NAME] = username!!
  686. contextAttributes[OAuth2AuthorizationContext.PASSWORD_ATTRIBUTE_NAME] = password!!
  687. }
  688. Mono.just(contextAttributes)
  689. }
  690. }
  691. ----
  692. ======
  693. You may obtain the `OAuth2AccessToken` as follows:
  694. [tabs]
  695. ======
  696. Java::
  697. +
  698. [source,java,role="primary"]
  699. ----
  700. @Controller
  701. public class OAuth2ClientController {
  702. @Autowired
  703. private ReactiveOAuth2AuthorizedClientManager authorizedClientManager;
  704. @GetMapping("/")
  705. public Mono<String> index(Authentication authentication, ServerWebExchange exchange) {
  706. OAuth2AuthorizeRequest authorizeRequest = OAuth2AuthorizeRequest.withClientRegistrationId("okta")
  707. .principal(authentication)
  708. .attribute(ServerWebExchange.class.getName(), exchange)
  709. .build();
  710. return this.authorizedClientManager.authorize(authorizeRequest)
  711. .map(OAuth2AuthorizedClient::getAccessToken)
  712. // ...
  713. .thenReturn("index");
  714. }
  715. }
  716. ----
  717. Kotlin::
  718. +
  719. [source,kotlin,role="secondary"]
  720. ----
  721. @Controller
  722. class OAuth2ClientController {
  723. @Autowired
  724. private lateinit var authorizedClientManager: ReactiveOAuth2AuthorizedClientManager
  725. @GetMapping("/")
  726. fun index(authentication: Authentication, exchange: ServerWebExchange): Mono<String> {
  727. val authorizeRequest = OAuth2AuthorizeRequest.withClientRegistrationId("okta")
  728. .principal(authentication)
  729. .attribute(ServerWebExchange::class.java.name, exchange)
  730. .build()
  731. return authorizedClientManager.authorize(authorizeRequest)
  732. .map { it.accessToken }
  733. // ...
  734. .thenReturn("index")
  735. }
  736. }
  737. ----
  738. ======
  739. [NOTE]
  740. `ServerWebExchange` is an OPTIONAL attribute.
  741. If not provided, it will be obtained from the https://projectreactor.io/docs/core/release/reference/#context[Reactor's Context] via the key `ServerWebExchange.class`.
  742. [[oauth2-client-jwt-bearer]]
  743. == [[oauth2Client-jwt-bearer-grant]]JWT Bearer
  744. [NOTE]
  745. Please refer to JSON Web Token (JWT) Profile for OAuth 2.0 Client Authentication and Authorization Grants for further details on the https://datatracker.ietf.org/doc/html/rfc7523[JWT Bearer] grant.
  746. [[oauth2-client-jwt-bearer-access-token]]
  747. === Requesting an Access Token
  748. [NOTE]
  749. Please refer to the https://datatracker.ietf.org/doc/html/rfc7523#section-2.1[Access Token Request/Response] protocol flow for the JWT Bearer grant.
  750. The default implementation of `ReactiveOAuth2AccessTokenResponseClient` for the JWT Bearer grant is `WebClientReactiveJwtBearerTokenResponseClient`, which uses a `WebClient` when requesting an access token at the Authorization Server’s Token Endpoint.
  751. :section-id: jwt-bearer
  752. :grant-type: JWT Bearer
  753. :class-name: WebClientReactiveJwtBearerTokenResponseClient
  754. :grant-request: JwtBearerGrantRequest
  755. :leveloffset: +1
  756. include::partial$reactive/oauth2/client/web-client-access-token-response-client.adoc[]
  757. :leveloffset: -1
  758. [[oauth2-client-jwt-bearer-authorized-client-provider-builder]]
  759. === Customize using the Builder
  760. Whether you customize `WebClientReactiveJwtBearerTokenResponseClient` or provide your own implementation of `ReactiveOAuth2AccessTokenResponseClient`, you can configure it using the `ReactiveOAuth2AuthorizedClientProviderBuilder` (as an alternative to <<oauth2-client-jwt-bearer-access-token-response-client-bean,publishing a bean>>) as follows:
  761. .Access Token Response Configuration via Builder
  762. [tabs]
  763. ======
  764. Java::
  765. +
  766. [source,java,role="primary"]
  767. ----
  768. // Customize
  769. ReactiveOAuth2AccessTokenResponseClient<JwtBearerGrantRequest> jwtBearerTokenResponseClient = ...
  770. JwtBearerReactiveOAuth2AuthorizedClientProvider jwtBearerAuthorizedClientProvider = new JwtBearerReactiveOAuth2AuthorizedClientProvider();
  771. jwtBearerAuthorizedClientProvider.setAccessTokenResponseClient(jwtBearerTokenResponseClient);
  772. ReactiveOAuth2AuthorizedClientProvider authorizedClientProvider =
  773. ReactiveOAuth2AuthorizedClientProviderBuilder.builder()
  774. .provider(jwtBearerAuthorizedClientProvider)
  775. .build();
  776. // ...
  777. authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider);
  778. ----
  779. Kotlin::
  780. +
  781. [source,kotlin,role="secondary"]
  782. ----
  783. // Customize
  784. val jwtBearerTokenResponseClient: ReactiveOAuth2AccessTokenResponseClient<JwtBearerGrantRequest> = ...
  785. val jwtBearerAuthorizedClientProvider = JwtBearerReactiveOAuth2AuthorizedClientProvider()
  786. jwtBearerAuthorizedClientProvider.setAccessTokenResponseClient(jwtBearerTokenResponseClient)
  787. val authorizedClientProvider = ReactiveOAuth2AuthorizedClientProviderBuilder.builder()
  788. .provider(jwtBearerAuthorizedClientProvider)
  789. .build()
  790. // ...
  791. authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider)
  792. ----
  793. ======
  794. [[oauth2-client-jwt-bearer-authorized-client-manager]]
  795. === Using the Access Token
  796. Given the following Spring Boot properties for an OAuth 2.0 Client registration:
  797. [source,yaml]
  798. ----
  799. spring:
  800. security:
  801. oauth2:
  802. client:
  803. registration:
  804. okta:
  805. client-id: okta-client-id
  806. client-secret: okta-client-secret
  807. authorization-grant-type: urn:ietf:params:oauth:grant-type:jwt-bearer
  808. scope: read
  809. provider:
  810. okta:
  811. token-uri: https://dev-1234.oktapreview.com/oauth2/v1/token
  812. ----
  813. ...and the `OAuth2AuthorizedClientManager` `@Bean`:
  814. [tabs]
  815. ======
  816. Java::
  817. +
  818. [source,java,role="primary"]
  819. ----
  820. @Bean
  821. public ReactiveOAuth2AuthorizedClientManager authorizedClientManager(
  822. ReactiveClientRegistrationRepository clientRegistrationRepository,
  823. ServerOAuth2AuthorizedClientRepository authorizedClientRepository) {
  824. JwtBearerReactiveOAuth2AuthorizedClientProvider jwtBearerAuthorizedClientProvider =
  825. new JwtBearerReactiveOAuth2AuthorizedClientProvider();
  826. ReactiveOAuth2AuthorizedClientProvider authorizedClientProvider =
  827. ReactiveOAuth2AuthorizedClientProviderBuilder.builder()
  828. .provider(jwtBearerAuthorizedClientProvider)
  829. .build();
  830. DefaultReactiveOAuth2AuthorizedClientManager authorizedClientManager =
  831. new DefaultReactiveOAuth2AuthorizedClientManager(
  832. clientRegistrationRepository, authorizedClientRepository);
  833. authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider);
  834. return authorizedClientManager;
  835. }
  836. ----
  837. Kotlin::
  838. +
  839. [source,kotlin,role="secondary"]
  840. ----
  841. @Bean
  842. fun authorizedClientManager(
  843. clientRegistrationRepository: ReactiveClientRegistrationRepository,
  844. authorizedClientRepository: ServerOAuth2AuthorizedClientRepository): ReactiveOAuth2AuthorizedClientManager {
  845. val jwtBearerAuthorizedClientProvider = JwtBearerReactiveOAuth2AuthorizedClientProvider()
  846. val authorizedClientProvider = ReactiveOAuth2AuthorizedClientProviderBuilder.builder()
  847. .provider(jwtBearerAuthorizedClientProvider)
  848. .build()
  849. val authorizedClientManager = DefaultReactiveOAuth2AuthorizedClientManager(
  850. clientRegistrationRepository, authorizedClientRepository)
  851. authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider)
  852. return authorizedClientManager
  853. }
  854. ----
  855. ======
  856. You may obtain the `OAuth2AccessToken` as follows:
  857. [tabs]
  858. ======
  859. Java::
  860. +
  861. [source,java,role="primary"]
  862. ----
  863. @RestController
  864. public class OAuth2ResourceServerController {
  865. @Autowired
  866. private ReactiveOAuth2AuthorizedClientManager authorizedClientManager;
  867. @GetMapping("/resource")
  868. public Mono<String> resource(JwtAuthenticationToken jwtAuthentication, ServerWebExchange exchange) {
  869. OAuth2AuthorizeRequest authorizeRequest = OAuth2AuthorizeRequest.withClientRegistrationId("okta")
  870. .principal(jwtAuthentication)
  871. .build();
  872. return this.authorizedClientManager.authorize(authorizeRequest)
  873. .map(OAuth2AuthorizedClient::getAccessToken)
  874. // ...
  875. .thenReturn("index");
  876. }
  877. }
  878. ----
  879. Kotlin::
  880. +
  881. [source,kotlin,role="secondary"]
  882. ----
  883. class OAuth2ResourceServerController {
  884. @Autowired
  885. private lateinit var authorizedClientManager: ReactiveOAuth2AuthorizedClientManager
  886. @GetMapping("/resource")
  887. fun resource(jwtAuthentication: JwtAuthenticationToken, exchange: ServerWebExchange): Mono<String> {
  888. val authorizeRequest = OAuth2AuthorizeRequest.withClientRegistrationId("okta")
  889. .principal(jwtAuthentication)
  890. .build()
  891. return authorizedClientManager.authorize(authorizeRequest)
  892. .map { it.accessToken }
  893. // ...
  894. .thenReturn("index")
  895. }
  896. }
  897. ----
  898. ======
  899. [NOTE]
  900. `JwtBearerReactiveOAuth2AuthorizedClientProvider` resolves the `Jwt` assertion via `OAuth2AuthorizationContext.getPrincipal().getPrincipal()` by default, hence the use of `JwtAuthenticationToken` in the preceding example.
  901. [TIP]
  902. If you need to resolve the `Jwt` assertion from a different source, you can provide `JwtBearerReactiveOAuth2AuthorizedClientProvider.setJwtAssertionResolver()` with a custom `Function<OAuth2AuthorizationContext, Mono<Jwt>>`.
  903. [[oauth2-client-token-exchange]]
  904. == [[oauth2Client-token-exchange-grant]]Token Exchange
  905. [NOTE]
  906. Please refer to OAuth 2.0 Token Exchange for further details on the https://datatracker.ietf.org/doc/html/rfc8693[Token Exchange] grant.
  907. [[oauth2-client-token-exchange-access-token]]
  908. === Requesting an Access Token
  909. [NOTE]
  910. Please refer to the https://datatracker.ietf.org/doc/html/rfc8693#section-2[Token Exchange Request and Response] protocol flow for the Token Exchange grant.
  911. The default implementation of `ReactiveOAuth2AccessTokenResponseClient` for the Token Exchange grant is `WebClientReactiveTokenExchangeTokenResponseClient`, which uses a `WebClient` when requesting an access token at the Authorization Server’s Token Endpoint.
  912. :section-id: token-exchange
  913. :grant-type: Token Exchange
  914. :class-name: WebClientReactiveTokenExchangeTokenResponseClient
  915. :grant-request: TokenExchangeGrantRequest
  916. :leveloffset: +1
  917. include::partial$reactive/oauth2/client/web-client-access-token-response-client.adoc[]
  918. :leveloffset: -1
  919. [[oauth2-client-token-exchange-authorized-client-provider-builder]]
  920. === Customize using the Builder
  921. Whether you customize `WebClientReactiveTokenExchangeTokenResponseClient` or provide your own implementation of `ReactiveOAuth2AccessTokenResponseClient`, you can configure it using the `ReactiveOAuth2AuthorizedClientProviderBuilder` (as an alternative to <<oauth2-client-token-exchange-access-token-response-client-bean,publishing a bean>>) as follows:
  922. .Access Token Response Configuration via Builder
  923. [tabs]
  924. ======
  925. Java::
  926. +
  927. [source,java,role="primary"]
  928. ----
  929. // Customize
  930. ReactiveOAuth2AccessTokenResponseClient<TokenExchangeGrantRequest> tokenExchangeTokenResponseClient = ...
  931. TokenExchangeReactiveOAuth2AuthorizedClientProvider tokenExchangeAuthorizedClientProvider = new TokenExchangeReactiveOAuth2AuthorizedClientProvider();
  932. tokenExchangeAuthorizedClientProvider.setAccessTokenResponseClient(tokenExchangeTokenResponseClient);
  933. ReactiveOAuth2AuthorizedClientProvider authorizedClientProvider =
  934. ReactiveOAuth2AuthorizedClientProviderBuilder.builder()
  935. .provider(tokenExchangeAuthorizedClientProvider)
  936. .build();
  937. // ...
  938. authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider);
  939. ----
  940. Kotlin::
  941. +
  942. [source,kotlin,role="secondary"]
  943. ----
  944. // Customize
  945. val tokenExchangeTokenResponseClient: ReactiveOAuth2AccessTokenResponseClient<TokenExchangeGrantRequest> = ...
  946. val tokenExchangeAuthorizedClientProvider = TokenExchangeReactiveOAuth2AuthorizedClientProvider()
  947. tokenExchangeAuthorizedClientProvider.setAccessTokenResponseClient(tokenExchangeTokenResponseClient)
  948. val authorizedClientProvider = ReactiveOAuth2AuthorizedClientProviderBuilder.builder()
  949. .provider(tokenExchangeAuthorizedClientProvider)
  950. .build()
  951. // ...
  952. authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider)
  953. ----
  954. ======
  955. [[oauth2-client-token-exchange-authorized-client-manager]]
  956. === Using the Access Token
  957. Given the following Spring Boot properties for an OAuth 2.0 Client registration:
  958. [source,yaml]
  959. ----
  960. spring:
  961. security:
  962. oauth2:
  963. client:
  964. registration:
  965. okta:
  966. client-id: okta-client-id
  967. client-secret: okta-client-secret
  968. authorization-grant-type: urn:ietf:params:oauth:grant-type:token-exchange
  969. scope: read
  970. provider:
  971. okta:
  972. token-uri: https://dev-1234.oktapreview.com/oauth2/v1/token
  973. ----
  974. ...and the `OAuth2AuthorizedClientManager` `@Bean`:
  975. [tabs]
  976. ======
  977. Java::
  978. +
  979. [source,java,role="primary"]
  980. ----
  981. @Bean
  982. public ReactiveOAuth2AuthorizedClientManager authorizedClientManager(
  983. ReactiveClientRegistrationRepository clientRegistrationRepository,
  984. ServerOAuth2AuthorizedClientRepository authorizedClientRepository) {
  985. TokenExchangeReactiveOAuth2AuthorizedClientProvider tokenExchangeAuthorizedClientProvider =
  986. new TokenExchangeReactiveOAuth2AuthorizedClientProvider();
  987. ReactiveOAuth2AuthorizedClientProvider authorizedClientProvider =
  988. ReactiveOAuth2AuthorizedClientProviderBuilder.builder()
  989. .provider(tokenExchangeAuthorizedClientProvider)
  990. .build();
  991. DefaultReactiveOAuth2AuthorizedClientManager authorizedClientManager =
  992. new DefaultReactiveOAuth2AuthorizedClientManager(
  993. clientRegistrationRepository, authorizedClientRepository);
  994. authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider);
  995. return authorizedClientManager;
  996. }
  997. ----
  998. Kotlin::
  999. +
  1000. [source,kotlin,role="secondary"]
  1001. ----
  1002. @Bean
  1003. fun authorizedClientManager(
  1004. clientRegistrationRepository: ReactiveClientRegistrationRepository,
  1005. authorizedClientRepository: ServerOAuth2AuthorizedClientRepository): ReactiveOAuth2AuthorizedClientManager {
  1006. val tokenExchangeAuthorizedClientProvider = TokenExchangeReactiveOAuth2AuthorizedClientProvider()
  1007. val authorizedClientProvider = ReactiveOAuth2AuthorizedClientProviderBuilder.builder()
  1008. .provider(tokenExchangeAuthorizedClientProvider)
  1009. .build()
  1010. val authorizedClientManager = DefaultReactiveOAuth2AuthorizedClientManager(
  1011. clientRegistrationRepository, authorizedClientRepository)
  1012. authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider)
  1013. return authorizedClientManager
  1014. }
  1015. ----
  1016. ======
  1017. You may obtain the `OAuth2AccessToken` as follows:
  1018. [tabs]
  1019. ======
  1020. Java::
  1021. +
  1022. [source,java,role="primary"]
  1023. ----
  1024. @RestController
  1025. public class OAuth2ResourceServerController {
  1026. @Autowired
  1027. private ReactiveOAuth2AuthorizedClientManager authorizedClientManager;
  1028. @GetMapping("/resource")
  1029. public Mono<String> resource(JwtAuthenticationToken jwtAuthentication) {
  1030. OAuth2AuthorizeRequest authorizeRequest = OAuth2AuthorizeRequest.withClientRegistrationId("okta")
  1031. .principal(jwtAuthentication)
  1032. .build();
  1033. return this.authorizedClientManager.authorize(authorizeRequest)
  1034. .map(OAuth2AuthorizedClient::getAccessToken)
  1035. // ...
  1036. .thenReturn("index");
  1037. }
  1038. }
  1039. ----
  1040. Kotlin::
  1041. +
  1042. [source,kotlin,role="secondary"]
  1043. ----
  1044. class OAuth2ResourceServerController {
  1045. @Autowired
  1046. private lateinit var authorizedClientManager: ReactiveOAuth2AuthorizedClientManager
  1047. @GetMapping("/resource")
  1048. fun resource(jwtAuthentication: JwtAuthenticationToken): Mono<String> {
  1049. val authorizeRequest = OAuth2AuthorizeRequest.withClientRegistrationId("okta")
  1050. .principal(jwtAuthentication)
  1051. .build()
  1052. return authorizedClientManager.authorize(authorizeRequest)
  1053. .map { it.accessToken }
  1054. // ...
  1055. .thenReturn("index")
  1056. }
  1057. }
  1058. ----
  1059. ======
  1060. [NOTE]
  1061. `TokenExchangeReactiveOAuth2AuthorizedClientProvider` resolves the subject token (as an `OAuth2Token`) via `OAuth2AuthorizationContext.getPrincipal().getPrincipal()` by default, hence the use of `JwtAuthenticationToken` in the preceding example.
  1062. An actor token is not resolved by default.
  1063. [TIP]
  1064. If you need to resolve the subject token from a different source, you can provide `TokenExchangeReactiveOAuth2AuthorizedClientProvider.setSubjectTokenResolver()` with a custom `Function<OAuth2AuthorizationContext, Mono<OAuth2Token>>`.
  1065. [TIP]
  1066. If you need to resolve an actor token, you can provide `TokenExchangeReactiveOAuth2AuthorizedClientProvider.setActorTokenResolver()` with a custom `Function<OAuth2AuthorizationContext, Mono<OAuth2Token>>`.