authorization-grants.adoc 48 KB

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