oauth2-client.adoc 86 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080
  1. [[webflux-oauth2-client]]
  2. = OAuth 2.0 Client
  3. The OAuth 2.0 Client features provide support for the Client role as defined in the https://tools.ietf.org/html/rfc6749#section-1.1[OAuth 2.0 Authorization Framework].
  4. At a high-level, the core features available are:
  5. .Authorization Grant support
  6. * https://tools.ietf.org/html/rfc6749#section-1.3.1[Authorization Code]
  7. * https://tools.ietf.org/html/rfc6749#section-6[Refresh Token]
  8. * https://tools.ietf.org/html/rfc6749#section-1.3.4[Client Credentials]
  9. * https://tools.ietf.org/html/rfc6749#section-1.3.3[Resource Owner Password Credentials]
  10. * https://datatracker.ietf.org/doc/html/rfc7523#section-2.1[JWT Bearer]
  11. .Client Authentication support
  12. * https://datatracker.ietf.org/doc/html/rfc7523#section-2.2[JWT Bearer]
  13. .HTTP Client support
  14. * <<oauth2Client-webclient-webflux, `WebClient` integration for Reactive Environments>> (for requesting protected resources)
  15. The `ServerHttpSecurity.oauth2Client()` DSL provides a number of configuration options for customizing the core components used by OAuth 2.0 Client.
  16. The following code shows the complete configuration options provided by the `ServerHttpSecurity.oauth2Client()` DSL:
  17. .OAuth2 Client Configuration Options
  18. ====
  19. .Java
  20. [source,java,role="primary"]
  21. ----
  22. @EnableWebFluxSecurity
  23. public class OAuth2ClientSecurityConfig {
  24. @Bean
  25. public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {
  26. http
  27. .oauth2Client(oauth2 -> oauth2
  28. .clientRegistrationRepository(this.clientRegistrationRepository())
  29. .authorizedClientRepository(this.authorizedClientRepository())
  30. .authorizationRequestRepository(this.authorizationRequestRepository())
  31. .authenticationConverter(this.authenticationConverter())
  32. .authenticationManager(this.authenticationManager())
  33. );
  34. return http.build();
  35. }
  36. }
  37. ----
  38. .Kotlin
  39. [source,kotlin,role="secondary"]
  40. ----
  41. @EnableWebFluxSecurity
  42. class OAuth2ClientSecurityConfig {
  43. @Bean
  44. fun securityFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {
  45. http {
  46. oauth2Client {
  47. clientRegistrationRepository = clientRegistrationRepository()
  48. authorizedClientRepository = authorizedClientRepository()
  49. authorizationRequestRepository = authorizedRequestRepository()
  50. authenticationConverter = authenticationConverter()
  51. authenticationManager = authenticationManager()
  52. }
  53. }
  54. return http.build()
  55. }
  56. }
  57. ----
  58. ====
  59. The `ReactiveOAuth2AuthorizedClientManager` is responsible for managing the authorization (or re-authorization) of an OAuth 2.0 Client, in collaboration with one or more `ReactiveOAuth2AuthorizedClientProvider`(s).
  60. The following code shows an example of how to register a `ReactiveOAuth2AuthorizedClientManager` `@Bean` and associate it with a `ReactiveOAuth2AuthorizedClientProvider` composite that provides support for the `authorization_code`, `refresh_token`, `client_credentials` and `password` authorization grant types:
  61. ====
  62. .Java
  63. [source,java,role="primary"]
  64. ----
  65. @Bean
  66. public ReactiveOAuth2AuthorizedClientManager authorizedClientManager(
  67. ReactiveClientRegistrationRepository clientRegistrationRepository,
  68. ServerOAuth2AuthorizedClientRepository authorizedClientRepository) {
  69. ReactiveOAuth2AuthorizedClientProvider authorizedClientProvider =
  70. ReactiveOAuth2AuthorizedClientProviderBuilder.builder()
  71. .authorizationCode()
  72. .refreshToken()
  73. .clientCredentials()
  74. .password()
  75. .build();
  76. DefaultReactiveOAuth2AuthorizedClientManager authorizedClientManager =
  77. new DefaultReactiveOAuth2AuthorizedClientManager(
  78. clientRegistrationRepository, authorizedClientRepository);
  79. authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider);
  80. return authorizedClientManager;
  81. }
  82. ----
  83. .Kotlin
  84. [source,kotlin,role="secondary"]
  85. ----
  86. @Bean
  87. fun authorizedClientManager(
  88. clientRegistrationRepository: ReactiveClientRegistrationRepository,
  89. authorizedClientRepository: ServerOAuth2AuthorizedClientRepository): ReactiveOAuth2AuthorizedClientManager {
  90. val authorizedClientProvider: ReactiveOAuth2AuthorizedClientProvider = ReactiveOAuth2AuthorizedClientProviderBuilder.builder()
  91. .authorizationCode()
  92. .refreshToken()
  93. .clientCredentials()
  94. .password()
  95. .build()
  96. val authorizedClientManager = DefaultReactiveOAuth2AuthorizedClientManager(
  97. clientRegistrationRepository, authorizedClientRepository)
  98. authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider)
  99. return authorizedClientManager
  100. }
  101. ----
  102. ====
  103. The following sections will go into more detail on the core components used by OAuth 2.0 Client and the configuration options available:
  104. * <<oauth2Client-core-interface-class>>
  105. ** <<oauth2Client-client-registration, ClientRegistration>>
  106. ** <<oauth2Client-client-registration-repo, ReactiveClientRegistrationRepository>>
  107. ** <<oauth2Client-authorized-client, OAuth2AuthorizedClient>>
  108. ** <<oauth2Client-authorized-repo-service, ServerOAuth2AuthorizedClientRepository / ReactiveOAuth2AuthorizedClientService>>
  109. ** <<oauth2Client-authorized-manager-provider, ReactiveOAuth2AuthorizedClientManager / ReactiveOAuth2AuthorizedClientProvider>>
  110. * <<oauth2Client-auth-grant-support>>
  111. ** <<oauth2Client-auth-code-grant, Authorization Code>>
  112. ** <<oauth2Client-refresh-token-grant, Refresh Token>>
  113. ** <<oauth2Client-client-creds-grant, Client Credentials>>
  114. ** <<oauth2Client-password-grant, Resource Owner Password Credentials>>
  115. ** <<oauth2Client-jwt-bearer-grant, JWT Bearer>>
  116. * <<oauth2Client-client-auth-support>>
  117. ** <<oauth2Client-jwt-bearer-auth, JWT Bearer>>
  118. * <<oauth2Client-additional-features>>
  119. ** <<oauth2Client-registered-authorized-client, Resolving an Authorized Client>>
  120. * <<oauth2Client-webclient-webflux>>
  121. [[oauth2Client-core-interface-class]]
  122. == Core Interfaces / Classes
  123. [[oauth2Client-client-registration]]
  124. === ClientRegistration
  125. `ClientRegistration` is a representation of a client registered with an OAuth 2.0 or OpenID Connect 1.0 Provider.
  126. A client registration holds information, such as client id, client secret, authorization grant type, redirect URI, scope(s), authorization URI, token URI, and other details.
  127. `ClientRegistration` and its properties are defined as follows:
  128. [source,java]
  129. ----
  130. public final class ClientRegistration {
  131. private String registrationId; <1>
  132. private String clientId; <2>
  133. private String clientSecret; <3>
  134. private ClientAuthenticationMethod clientAuthenticationMethod; <4>
  135. private AuthorizationGrantType authorizationGrantType; <5>
  136. private String redirectUri; <6>
  137. private Set<String> scopes; <7>
  138. private ProviderDetails providerDetails;
  139. private String clientName; <8>
  140. public class ProviderDetails {
  141. private String authorizationUri; <9>
  142. private String tokenUri; <10>
  143. private UserInfoEndpoint userInfoEndpoint;
  144. private String jwkSetUri; <11>
  145. private String issuerUri; <12>
  146. private Map<String, Object> configurationMetadata; <13>
  147. public class UserInfoEndpoint {
  148. private String uri; <14>
  149. private AuthenticationMethod authenticationMethod; <15>
  150. private String userNameAttributeName; <16>
  151. }
  152. }
  153. }
  154. ----
  155. <1> `registrationId`: The ID that uniquely identifies the `ClientRegistration`.
  156. <2> `clientId`: The client identifier.
  157. <3> `clientSecret`: The client secret.
  158. <4> `clientAuthenticationMethod`: The method used to authenticate the Client with the Provider.
  159. The supported values are *client_secret_basic*, *client_secret_post*, *private_key_jwt*, *client_secret_jwt* and *none* https://tools.ietf.org/html/rfc6749#section-2.1[(public clients)].
  160. <5> `authorizationGrantType`: The OAuth 2.0 Authorization Framework defines four https://tools.ietf.org/html/rfc6749#section-1.3[Authorization Grant] types.
  161. The supported values are `authorization_code`, `client_credentials`, `password`, as well as, extension grant type `urn:ietf:params:oauth:grant-type:jwt-bearer`.
  162. <6> `redirectUri`: The client's registered redirect URI that the _Authorization Server_ redirects the end-user's user-agent
  163. to after the end-user has authenticated and authorized access to the client.
  164. <7> `scopes`: The scope(s) requested by the client during the Authorization Request flow, such as openid, email, or profile.
  165. <8> `clientName`: A descriptive name used for the client.
  166. The name may be used in certain scenarios, such as when displaying the name of the client in the auto-generated login page.
  167. <9> `authorizationUri`: The Authorization Endpoint URI for the Authorization Server.
  168. <10> `tokenUri`: The Token Endpoint URI for the Authorization Server.
  169. <11> `jwkSetUri`: The URI used to retrieve the https://tools.ietf.org/html/rfc7517[JSON Web Key (JWK)] Set from the Authorization Server,
  170. which contains the cryptographic key(s) used to verify the https://tools.ietf.org/html/rfc7515[JSON Web Signature (JWS)] of the ID Token and optionally the UserInfo Response.
  171. <12> `issuerUri`: Returns the issuer identifier uri for the OpenID Connect 1.0 provider or the OAuth 2.0 Authorization Server.
  172. <13> `configurationMetadata`: The https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderConfig[OpenID Provider Configuration Information].
  173. This information will only be available if the Spring Boot 2.x property `spring.security.oauth2.client.provider.[providerId].issuerUri` is configured.
  174. <14> `(userInfoEndpoint)uri`: The UserInfo Endpoint URI used to access the claims/attributes of the authenticated end-user.
  175. <15> `(userInfoEndpoint)authenticationMethod`: The authentication method used when sending the access token to the UserInfo Endpoint.
  176. The supported values are *header*, *form* and *query*.
  177. <16> `userNameAttributeName`: The name of the attribute returned in the UserInfo Response that references the Name or Identifier of the end-user.
  178. A `ClientRegistration` can be initially configured using discovery of an OpenID Connect Provider's https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderConfig[Configuration endpoint] or an Authorization Server's https://tools.ietf.org/html/rfc8414#section-3[Metadata endpoint].
  179. `ClientRegistrations` provides convenience methods for configuring a `ClientRegistration` in this way, as can be seen in the following example:
  180. ====
  181. .Java
  182. [source,java,role="primary"]
  183. ----
  184. ClientRegistration clientRegistration =
  185. ClientRegistrations.fromIssuerLocation("https://idp.example.com/issuer").build();
  186. ----
  187. .Kotlin
  188. [source,kotlin,role="secondary"]
  189. ----
  190. val clientRegistration = ClientRegistrations.fromIssuerLocation("https://idp.example.com/issuer").build()
  191. ----
  192. ====
  193. The above code will query in series `https://idp.example.com/issuer/.well-known/openid-configuration`, and then `https://idp.example.com/.well-known/openid-configuration/issuer`, and finally `https://idp.example.com/.well-known/oauth-authorization-server/issuer`, stopping at the first to return a 200 response.
  194. As an alternative, you can use `ClientRegistrations.fromOidcIssuerLocation()` to only query the OpenID Connect Provider's Configuration endpoint.
  195. [[oauth2Client-client-registration-repo]]
  196. === ReactiveClientRegistrationRepository
  197. The `ReactiveClientRegistrationRepository` serves as a repository for OAuth 2.0 / OpenID Connect 1.0 `ClientRegistration`(s).
  198. [NOTE]
  199. Client registration information is ultimately stored and owned by the associated Authorization Server.
  200. This repository provides the ability to retrieve a sub-set of the primary client registration information, which is stored with the Authorization Server.
  201. Spring Boot 2.x auto-configuration binds each of the properties under `spring.security.oauth2.client.registration._[registrationId]_` to an instance of `ClientRegistration` and then composes each of the `ClientRegistration` instance(s) within a `ReactiveClientRegistrationRepository`.
  202. [NOTE]
  203. The default implementation of `ReactiveClientRegistrationRepository` is `InMemoryReactiveClientRegistrationRepository`.
  204. The auto-configuration also registers the `ReactiveClientRegistrationRepository` as a `@Bean` in the `ApplicationContext` so that it is available for dependency-injection, if needed by the application.
  205. The following listing shows an example:
  206. ====
  207. .Java
  208. [source,java,role="primary"]
  209. ----
  210. @Controller
  211. public class OAuth2ClientController {
  212. @Autowired
  213. private ReactiveClientRegistrationRepository clientRegistrationRepository;
  214. @GetMapping("/")
  215. public Mono<String> index() {
  216. return this.clientRegistrationRepository.findByRegistrationId("okta")
  217. ...
  218. .thenReturn("index");
  219. }
  220. }
  221. ----
  222. .Kotlin
  223. [source,kotlin,role="secondary"]
  224. ----
  225. @Controller
  226. class OAuth2ClientController {
  227. @Autowired
  228. private lateinit var clientRegistrationRepository: ReactiveClientRegistrationRepository
  229. @GetMapping("/")
  230. fun index(): Mono<String> {
  231. return this.clientRegistrationRepository.findByRegistrationId("okta")
  232. ...
  233. .thenReturn("index")
  234. }
  235. }
  236. ----
  237. ====
  238. [[oauth2Client-authorized-client]]
  239. === OAuth2AuthorizedClient
  240. `OAuth2AuthorizedClient` is a representation of an Authorized Client.
  241. A client is considered to be authorized when the end-user (Resource Owner) has granted authorization to the client to access its protected resources.
  242. `OAuth2AuthorizedClient` serves the purpose of associating an `OAuth2AccessToken` (and optional `OAuth2RefreshToken`) to a `ClientRegistration` (client) and resource owner, who is the `Principal` end-user that granted the authorization.
  243. [[oauth2Client-authorized-repo-service]]
  244. === ServerOAuth2AuthorizedClientRepository / ReactiveOAuth2AuthorizedClientService
  245. `ServerOAuth2AuthorizedClientRepository` is responsible for persisting `OAuth2AuthorizedClient`(s) between web requests.
  246. Whereas, the primary role of `ReactiveOAuth2AuthorizedClientService` is to manage `OAuth2AuthorizedClient`(s) at the application-level.
  247. From a developer perspective, the `ServerOAuth2AuthorizedClientRepository` or `ReactiveOAuth2AuthorizedClientService` provides the capability to lookup an `OAuth2AccessToken` associated with a client so that it may be used to initiate a protected resource request.
  248. The following listing shows an example:
  249. ====
  250. .Java
  251. [source,java,role="primary"]
  252. ----
  253. @Controller
  254. public class OAuth2ClientController {
  255. @Autowired
  256. private ReactiveOAuth2AuthorizedClientService authorizedClientService;
  257. @GetMapping("/")
  258. public Mono<String> index(Authentication authentication) {
  259. return this.authorizedClientService.loadAuthorizedClient("okta", authentication.getName())
  260. .map(OAuth2AuthorizedClient::getAccessToken)
  261. ...
  262. .thenReturn("index");
  263. }
  264. }
  265. ----
  266. .Kotlin
  267. [source,kotlin,role="secondary"]
  268. ----
  269. @Controller
  270. class OAuth2ClientController {
  271. @Autowired
  272. private lateinit var authorizedClientService: ReactiveOAuth2AuthorizedClientService
  273. @GetMapping("/")
  274. fun index(authentication: Authentication): Mono<String> {
  275. return this.authorizedClientService.loadAuthorizedClient<OAuth2AuthorizedClient>("okta", authentication.name)
  276. .map { it.accessToken }
  277. ...
  278. .thenReturn("index")
  279. }
  280. }
  281. ----
  282. ====
  283. [NOTE]
  284. Spring Boot 2.x auto-configuration registers an `ServerOAuth2AuthorizedClientRepository` and/or `ReactiveOAuth2AuthorizedClientService` `@Bean` in the `ApplicationContext`.
  285. However, the application may choose to override and register a custom `ServerOAuth2AuthorizedClientRepository` or `ReactiveOAuth2AuthorizedClientService` `@Bean`.
  286. The default implementation of `ReactiveOAuth2AuthorizedClientService` is `InMemoryReactiveOAuth2AuthorizedClientService`, which stores `OAuth2AuthorizedClient`(s) in-memory.
  287. Alternatively, the R2DBC implementation `R2dbcReactiveOAuth2AuthorizedClientService` may be configured for persisting `OAuth2AuthorizedClient`(s) in a database.
  288. [NOTE]
  289. `R2dbcReactiveOAuth2AuthorizedClientService` depends on the table definition described in xref:servlet/appendix/database-schema.adoc#dbschema-oauth2-client[ OAuth 2.0 Client Schema].
  290. [[oauth2Client-authorized-manager-provider]]
  291. === ReactiveOAuth2AuthorizedClientManager / ReactiveOAuth2AuthorizedClientProvider
  292. The `ReactiveOAuth2AuthorizedClientManager` is responsible for the overall management of `OAuth2AuthorizedClient`(s).
  293. The primary responsibilities include:
  294. * Authorizing (or re-authorizing) an OAuth 2.0 Client, using a `ReactiveOAuth2AuthorizedClientProvider`.
  295. * Delegating the persistence of an `OAuth2AuthorizedClient`, typically using a `ReactiveOAuth2AuthorizedClientService` or `ServerOAuth2AuthorizedClientRepository`.
  296. * Delegating to a `ReactiveOAuth2AuthorizationSuccessHandler` when an OAuth 2.0 Client has been successfully authorized (or re-authorized).
  297. * Delegating to a `ReactiveOAuth2AuthorizationFailureHandler` when an OAuth 2.0 Client fails to authorize (or re-authorize).
  298. A `ReactiveOAuth2AuthorizedClientProvider` implements a strategy for authorizing (or re-authorizing) an OAuth 2.0 Client.
  299. Implementations will typically implement an authorization grant type, eg. `authorization_code`, `client_credentials`, etc.
  300. The default implementation of `ReactiveOAuth2AuthorizedClientManager` is `DefaultReactiveOAuth2AuthorizedClientManager`, which is associated with a `ReactiveOAuth2AuthorizedClientProvider` that may support multiple authorization grant types using a delegation-based composite.
  301. The `ReactiveOAuth2AuthorizedClientProviderBuilder` may be used to configure and build the delegation-based composite.
  302. The following code shows an example of how to configure and build a `ReactiveOAuth2AuthorizedClientProvider` composite that provides support for the `authorization_code`, `refresh_token`, `client_credentials` and `password` authorization grant types:
  303. ====
  304. .Java
  305. [source,java,role="primary"]
  306. ----
  307. @Bean
  308. public ReactiveOAuth2AuthorizedClientManager authorizedClientManager(
  309. ReactiveClientRegistrationRepository clientRegistrationRepository,
  310. ServerOAuth2AuthorizedClientRepository authorizedClientRepository) {
  311. ReactiveOAuth2AuthorizedClientProvider authorizedClientProvider =
  312. ReactiveOAuth2AuthorizedClientProviderBuilder.builder()
  313. .authorizationCode()
  314. .refreshToken()
  315. .clientCredentials()
  316. .password()
  317. .build();
  318. DefaultReactiveOAuth2AuthorizedClientManager authorizedClientManager =
  319. new DefaultReactiveOAuth2AuthorizedClientManager(
  320. clientRegistrationRepository, authorizedClientRepository);
  321. authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider);
  322. return authorizedClientManager;
  323. }
  324. ----
  325. .Kotlin
  326. [source,kotlin,role="secondary"]
  327. ----
  328. @Bean
  329. fun authorizedClientManager(
  330. clientRegistrationRepository: ReactiveClientRegistrationRepository,
  331. authorizedClientRepository: ServerOAuth2AuthorizedClientRepository): ReactiveOAuth2AuthorizedClientManager {
  332. val authorizedClientProvider: ReactiveOAuth2AuthorizedClientProvider = ReactiveOAuth2AuthorizedClientProviderBuilder.builder()
  333. .authorizationCode()
  334. .refreshToken()
  335. .clientCredentials()
  336. .password()
  337. .build()
  338. val authorizedClientManager = DefaultReactiveOAuth2AuthorizedClientManager(
  339. clientRegistrationRepository, authorizedClientRepository)
  340. authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider)
  341. return authorizedClientManager
  342. }
  343. ----
  344. ====
  345. When an authorization attempt succeeds, the `DefaultReactiveOAuth2AuthorizedClientManager` will delegate to the `ReactiveOAuth2AuthorizationSuccessHandler`, which (by default) will save the `OAuth2AuthorizedClient` via the `ReactiveOAuth2AuthorizedClientProvider`.
  346. In the case of a re-authorization failure, eg. a refresh token is no longer valid, the previously saved `OAuth2AuthorizedClient` will be removed from the `ServerOAuth2AuthorizedClientRepository` via the `RemoveAuthorizedClientReactiveOAuth2AuthorizationFailureHandler`.
  347. The default behaviour may be customized via `setAuthorizationSuccessHandler(ReactiveOAuth2AuthorizationSuccessHandler)` and `setAuthorizationFailureHandler(ReactiveOAuth2AuthorizationFailureHandler)`.
  348. The `DefaultReactiveOAuth2AuthorizedClientManager` is also associated with a `contextAttributesMapper` of type `Function<OAuth2AuthorizeRequest, Mono<Map<String, Object>>>`, which is responsible for mapping attribute(s) from the `OAuth2AuthorizeRequest` to a `Map` of attributes to be associated to the `OAuth2AuthorizationContext`.
  349. This can be useful when you need to supply a `ReactiveOAuth2AuthorizedClientProvider` with required (supported) attribute(s), eg. the `PasswordReactiveOAuth2AuthorizedClientProvider` requires the resource owner's `username` and `password` to be available in `OAuth2AuthorizationContext.getAttributes()`.
  350. The following code shows an example of the `contextAttributesMapper`:
  351. ====
  352. .Java
  353. [source,java,role="primary"]
  354. ----
  355. @Bean
  356. public ReactiveOAuth2AuthorizedClientManager authorizedClientManager(
  357. ReactiveClientRegistrationRepository clientRegistrationRepository,
  358. ServerOAuth2AuthorizedClientRepository authorizedClientRepository) {
  359. ReactiveOAuth2AuthorizedClientProvider authorizedClientProvider =
  360. ReactiveOAuth2AuthorizedClientProviderBuilder.builder()
  361. .password()
  362. .refreshToken()
  363. .build();
  364. DefaultReactiveOAuth2AuthorizedClientManager authorizedClientManager =
  365. new DefaultReactiveOAuth2AuthorizedClientManager(
  366. clientRegistrationRepository, authorizedClientRepository);
  367. authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider);
  368. // Assuming the `username` and `password` are supplied as `ServerHttpRequest` parameters,
  369. // map the `ServerHttpRequest` parameters to `OAuth2AuthorizationContext.getAttributes()`
  370. authorizedClientManager.setContextAttributesMapper(contextAttributesMapper());
  371. return authorizedClientManager;
  372. }
  373. private Function<OAuth2AuthorizeRequest, Mono<Map<String, Object>>> contextAttributesMapper() {
  374. return authorizeRequest -> {
  375. Map<String, Object> contextAttributes = Collections.emptyMap();
  376. ServerWebExchange exchange = authorizeRequest.getAttribute(ServerWebExchange.class.getName());
  377. ServerHttpRequest request = exchange.getRequest();
  378. String username = request.getQueryParams().getFirst(OAuth2ParameterNames.USERNAME);
  379. String password = request.getQueryParams().getFirst(OAuth2ParameterNames.PASSWORD);
  380. if (StringUtils.hasText(username) && StringUtils.hasText(password)) {
  381. contextAttributes = new HashMap<>();
  382. // `PasswordReactiveOAuth2AuthorizedClientProvider` requires both attributes
  383. contextAttributes.put(OAuth2AuthorizationContext.USERNAME_ATTRIBUTE_NAME, username);
  384. contextAttributes.put(OAuth2AuthorizationContext.PASSWORD_ATTRIBUTE_NAME, password);
  385. }
  386. return Mono.just(contextAttributes);
  387. };
  388. }
  389. ----
  390. .Kotlin
  391. [source,kotlin,role="secondary"]
  392. ----
  393. @Bean
  394. fun authorizedClientManager(
  395. clientRegistrationRepository: ReactiveClientRegistrationRepository,
  396. authorizedClientRepository: ServerOAuth2AuthorizedClientRepository): ReactiveOAuth2AuthorizedClientManager {
  397. val authorizedClientProvider: ReactiveOAuth2AuthorizedClientProvider = ReactiveOAuth2AuthorizedClientProviderBuilder.builder()
  398. .password()
  399. .refreshToken()
  400. .build()
  401. val authorizedClientManager = DefaultReactiveOAuth2AuthorizedClientManager(
  402. clientRegistrationRepository, authorizedClientRepository)
  403. authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider)
  404. // Assuming the `username` and `password` are supplied as `ServerHttpRequest` parameters,
  405. // map the `ServerHttpRequest` parameters to `OAuth2AuthorizationContext.getAttributes()`
  406. authorizedClientManager.setContextAttributesMapper(contextAttributesMapper())
  407. return authorizedClientManager
  408. }
  409. private fun contextAttributesMapper(): Function<OAuth2AuthorizeRequest, Mono<MutableMap<String, Any>>> {
  410. return Function { authorizeRequest ->
  411. var contextAttributes: MutableMap<String, Any> = mutableMapOf()
  412. val exchange: ServerWebExchange = authorizeRequest.getAttribute(ServerWebExchange::class.java.name)!!
  413. val request: ServerHttpRequest = exchange.request
  414. val username: String? = request.queryParams.getFirst(OAuth2ParameterNames.USERNAME)
  415. val password: String? = request.queryParams.getFirst(OAuth2ParameterNames.PASSWORD)
  416. if (StringUtils.hasText(username) && StringUtils.hasText(password)) {
  417. contextAttributes = hashMapOf()
  418. // `PasswordReactiveOAuth2AuthorizedClientProvider` requires both attributes
  419. contextAttributes[OAuth2AuthorizationContext.USERNAME_ATTRIBUTE_NAME] = username!!
  420. contextAttributes[OAuth2AuthorizationContext.PASSWORD_ATTRIBUTE_NAME] = password!!
  421. }
  422. Mono.just(contextAttributes)
  423. }
  424. }
  425. ----
  426. ====
  427. The `DefaultReactiveOAuth2AuthorizedClientManager` is designed to be used *_within_* the context of a `ServerWebExchange`.
  428. When operating *_outside_* of a `ServerWebExchange` context, use `AuthorizedClientServiceReactiveOAuth2AuthorizedClientManager` instead.
  429. A _service application_ is a common use case for when to use an `AuthorizedClientServiceReactiveOAuth2AuthorizedClientManager`.
  430. Service applications often run in the background, without any user interaction, and typically run under a system-level account instead of a user account.
  431. An OAuth 2.0 Client configured with the `client_credentials` grant type can be considered a type of service application.
  432. The following code shows an example of how to configure an `AuthorizedClientServiceReactiveOAuth2AuthorizedClientManager` that provides support for the `client_credentials` grant type:
  433. ====
  434. .Java
  435. [source,java,role="primary"]
  436. ----
  437. @Bean
  438. public ReactiveOAuth2AuthorizedClientManager authorizedClientManager(
  439. ReactiveClientRegistrationRepository clientRegistrationRepository,
  440. ReactiveOAuth2AuthorizedClientService authorizedClientService) {
  441. ReactiveOAuth2AuthorizedClientProvider authorizedClientProvider =
  442. ReactiveOAuth2AuthorizedClientProviderBuilder.builder()
  443. .clientCredentials()
  444. .build();
  445. AuthorizedClientServiceReactiveOAuth2AuthorizedClientManager authorizedClientManager =
  446. new AuthorizedClientServiceReactiveOAuth2AuthorizedClientManager(
  447. clientRegistrationRepository, authorizedClientService);
  448. authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider);
  449. return authorizedClientManager;
  450. }
  451. ----
  452. .Kotlin
  453. [source,kotlin,role="secondary"]
  454. ----
  455. @Bean
  456. fun authorizedClientManager(
  457. clientRegistrationRepository: ReactiveClientRegistrationRepository,
  458. authorizedClientService: ReactiveOAuth2AuthorizedClientService): ReactiveOAuth2AuthorizedClientManager {
  459. val authorizedClientProvider: ReactiveOAuth2AuthorizedClientProvider = ReactiveOAuth2AuthorizedClientProviderBuilder.builder()
  460. .clientCredentials()
  461. .build()
  462. val authorizedClientManager = AuthorizedClientServiceReactiveOAuth2AuthorizedClientManager(
  463. clientRegistrationRepository, authorizedClientService)
  464. authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider)
  465. return authorizedClientManager
  466. }
  467. ----
  468. ====
  469. [[oauth2Client-auth-grant-support]]
  470. == Authorization Grant Support
  471. [[oauth2Client-auth-code-grant]]
  472. === Authorization Code
  473. [NOTE]
  474. 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.
  475. ==== Obtaining Authorization
  476. [NOTE]
  477. Please refer to the https://tools.ietf.org/html/rfc6749#section-4.1.1[Authorization Request/Response] protocol flow for the Authorization Code grant.
  478. ==== Initiating the Authorization Request
  479. 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.
  480. The primary role of the `ServerOAuth2AuthorizationRequestResolver` is to resolve an `OAuth2AuthorizationRequest` from the provided web request.
  481. 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`.
  482. Given the following Spring Boot 2.x properties for an OAuth 2.0 Client registration:
  483. [source,yaml,attrs="-attributes"]
  484. ----
  485. spring:
  486. security:
  487. oauth2:
  488. client:
  489. registration:
  490. okta:
  491. client-id: okta-client-id
  492. client-secret: okta-client-secret
  493. authorization-grant-type: authorization_code
  494. redirect-uri: "{baseUrl}/authorized/okta"
  495. scope: read, write
  496. provider:
  497. okta:
  498. authorization-uri: https://dev-1234.oktapreview.com/oauth2/v1/authorize
  499. token-uri: https://dev-1234.oktapreview.com/oauth2/v1/token
  500. ----
  501. 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.
  502. [NOTE]
  503. The `AuthorizationCodeReactiveOAuth2AuthorizedClientProvider` is an implementation of `ReactiveOAuth2AuthorizedClientProvider` for the Authorization Code grant,
  504. which also initiates the Authorization Request redirect by the `OAuth2AuthorizationRequestRedirectWebFilter`.
  505. 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:
  506. [source,yaml,attrs="-attributes"]
  507. ----
  508. spring:
  509. security:
  510. oauth2:
  511. client:
  512. registration:
  513. okta:
  514. client-id: okta-client-id
  515. client-authentication-method: none
  516. authorization-grant-type: authorization_code
  517. redirect-uri: "{baseUrl}/authorized/okta"
  518. ...
  519. ----
  520. Public Clients are supported using https://tools.ietf.org/html/rfc7636[Proof Key for Code Exchange] (PKCE).
  521. 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:
  522. . `client-secret` is omitted (or empty)
  523. . `client-authentication-method` is set to "none" (`ClientAuthenticationMethod.NONE`)
  524. [[oauth2Client-auth-code-redirect-uri]]
  525. The `DefaultServerOAuth2AuthorizationRequestResolver` also supports `URI` template variables for the `redirect-uri` using `UriComponentsBuilder`.
  526. The following configuration uses all the supported `URI` template variables:
  527. [source,yaml,attrs="-attributes"]
  528. ----
  529. spring:
  530. security:
  531. oauth2:
  532. client:
  533. registration:
  534. okta:
  535. ...
  536. redirect-uri: "{baseScheme}://{baseHost}{basePort}{basePath}/authorized/{registrationId}"
  537. ...
  538. ----
  539. [NOTE]
  540. `+{baseUrl}+` resolves to `+{baseScheme}://{baseHost}{basePort}{basePath}+`
  541. 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].
  542. This ensures that the `X-Forwarded-*` headers are used when expanding the `redirect-uri`.
  543. ==== Customizing the Authorization Request
  544. 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.
  545. 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].
  546. One of those extended parameters is the `prompt` parameter.
  547. [NOTE]
  548. OPTIONAL. Space delimited, case sensitive list of ASCII string values that specifies whether the Authorization Server prompts the End-User for reauthentication and consent. The defined values are: none, login, consent, select_account
  549. 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`.
  550. ====
  551. .Java
  552. [source,java,role="primary"]
  553. ----
  554. @EnableWebFluxSecurity
  555. public class OAuth2LoginSecurityConfig {
  556. @Autowired
  557. private ReactiveClientRegistrationRepository clientRegistrationRepository;
  558. @Bean
  559. public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {
  560. http
  561. .authorizeExchange(authorize -> authorize
  562. .anyExchange().authenticated()
  563. )
  564. .oauth2Login(oauth2 -> oauth2
  565. .authorizationRequestResolver(
  566. authorizationRequestResolver(this.clientRegistrationRepository)
  567. )
  568. );
  569. return http.build();
  570. }
  571. private ServerOAuth2AuthorizationRequestResolver authorizationRequestResolver(
  572. ReactiveClientRegistrationRepository clientRegistrationRepository) {
  573. DefaultServerOAuth2AuthorizationRequestResolver authorizationRequestResolver =
  574. new DefaultServerOAuth2AuthorizationRequestResolver(
  575. clientRegistrationRepository);
  576. authorizationRequestResolver.setAuthorizationRequestCustomizer(
  577. authorizationRequestCustomizer());
  578. return authorizationRequestResolver;
  579. }
  580. private Consumer<OAuth2AuthorizationRequest.Builder> authorizationRequestCustomizer() {
  581. return customizer -> customizer
  582. .additionalParameters(params -> params.put("prompt", "consent"));
  583. }
  584. }
  585. ----
  586. .Kotlin
  587. [source,kotlin,role="secondary"]
  588. ----
  589. @EnableWebFluxSecurity
  590. class SecurityConfig {
  591. @Autowired
  592. private lateinit var customClientRegistrationRepository: ReactiveClientRegistrationRepository
  593. @Bean
  594. fun securityFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {
  595. http {
  596. authorizeExchange {
  597. authorize(anyExchange, authenticated)
  598. }
  599. oauth2Login {
  600. authorizationRequestResolver = authorizationRequestResolver(customClientRegistrationRepository)
  601. }
  602. }
  603. return http.build()
  604. }
  605. private fun authorizationRequestResolver(
  606. clientRegistrationRepository: ReactiveClientRegistrationRepository): ServerOAuth2AuthorizationRequestResolver {
  607. val authorizationRequestResolver = DefaultServerOAuth2AuthorizationRequestResolver(
  608. clientRegistrationRepository)
  609. authorizationRequestResolver.setAuthorizationRequestCustomizer(
  610. authorizationRequestCustomizer())
  611. return authorizationRequestResolver
  612. }
  613. private fun authorizationRequestCustomizer(): Consumer<OAuth2AuthorizationRequest.Builder> {
  614. return Consumer { customizer ->
  615. customizer
  616. .additionalParameters { params -> params["prompt"] = "consent" }
  617. }
  618. }
  619. }
  620. ----
  621. ====
  622. 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.
  623. For example, if the value for the request parameter `prompt` is always `consent` for the provider `okta`, than simply configure as follows:
  624. [source,yaml]
  625. ----
  626. spring:
  627. security:
  628. oauth2:
  629. client:
  630. provider:
  631. okta:
  632. authorization-uri: https://dev-1234.oktapreview.com/oauth2/v1/authorize?prompt=consent
  633. ----
  634. The preceding example shows the common use case of adding a custom parameter on top of the standard parameters.
  635. 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.
  636. [TIP]
  637. `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.
  638. The following example shows a variation of `authorizationRequestCustomizer()` from the preceding example, and instead overrides the `OAuth2AuthorizationRequest.authorizationRequestUri` property.
  639. ====
  640. .Java
  641. [source,java,role="primary"]
  642. ----
  643. private Consumer<OAuth2AuthorizationRequest.Builder> authorizationRequestCustomizer() {
  644. return customizer -> customizer
  645. .authorizationRequestUri(uriBuilder -> uriBuilder
  646. .queryParam("prompt", "consent").build());
  647. }
  648. ----
  649. .Kotlin
  650. [source,kotlin,role="secondary"]
  651. ----
  652. private fun authorizationRequestCustomizer(): Consumer<OAuth2AuthorizationRequest.Builder> {
  653. return Consumer { customizer: OAuth2AuthorizationRequest.Builder ->
  654. customizer
  655. .authorizationRequestUri { uriBuilder: UriBuilder ->
  656. uriBuilder
  657. .queryParam("prompt", "consent").build()
  658. }
  659. }
  660. }
  661. ----
  662. ====
  663. ==== Storing the Authorization Request
  664. 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).
  665. [TIP]
  666. The `OAuth2AuthorizationRequest` is used to correlate and validate the Authorization Response.
  667. The default implementation of `ServerAuthorizationRequestRepository` is `WebSessionOAuth2ServerAuthorizationRequestRepository`, which stores the `OAuth2AuthorizationRequest` in the `WebSession`.
  668. If you have a custom implementation of `ServerAuthorizationRequestRepository`, you may configure it as shown in the following example:
  669. .ServerAuthorizationRequestRepository Configuration
  670. ====
  671. .Java
  672. [source,java,role="primary"]
  673. ----
  674. @EnableWebFluxSecurity
  675. public class OAuth2ClientSecurityConfig {
  676. @Bean
  677. public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {
  678. http
  679. .oauth2Client(oauth2 -> oauth2
  680. .authorizationRequestRepository(this.authorizationRequestRepository())
  681. ...
  682. );
  683. return http.build();
  684. }
  685. }
  686. ----
  687. .Kotlin
  688. [source,kotlin,role="secondary"]
  689. ----
  690. @EnableWebFluxSecurity
  691. class OAuth2ClientSecurityConfig : WebSecurityConfigurerAdapter() {
  692. @Bean
  693. fun securityFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {
  694. http {
  695. oauth2Client {
  696. authorizationRequestRepository = authorizationRequestRepository()
  697. }
  698. }
  699. return http.build()
  700. }
  701. }
  702. ----
  703. ====
  704. ==== Requesting an Access Token
  705. [NOTE]
  706. 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.
  707. 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.
  708. The `WebClientReactiveAuthorizationCodeTokenResponseClient` is quite flexible as it allows you to customize the pre-processing of the Token Request and/or post-handling of the Token Response.
  709. ==== Customizing the Access Token Request
  710. If you need to customize the pre-processing of the Token Request, you can provide `WebClientReactiveAuthorizationCodeTokenResponseClient.setParametersConverter()` with a custom `Converter<OAuth2AuthorizationCodeGrantRequest, MultiValueMap<String, String>>`.
  711. The default implementation builds a `MultiValueMap<String, String>` containing only the `grant_type` parameter of a standard https://tools.ietf.org/html/rfc6749#section-4.1.3[OAuth 2.0 Access Token Request] which is used to construct the request. Other parameters required by the Authorization Code grant are added directly to the body of the request by the `WebClientReactiveAuthorizationCodeTokenResponseClient`.
  712. However, providing a custom `Converter`, would allow you to extend the standard Token Request and add custom parameter(s).
  713. [TIP]
  714. If you prefer to only add additional parameters, you can instead provide `WebClientReactiveAuthorizationCodeTokenResponseClient.addParametersConverter()` with a custom `Converter<OAuth2AuthorizationCodeGrantRequest, MultiValueMap<String, String>>` which constructs an aggregate `Converter`.
  715. IMPORTANT: The custom `Converter` must return valid parameters of an OAuth 2.0 Access Token Request that is understood by the intended OAuth 2.0 Provider.
  716. ==== Customizing the Access Token Response
  717. On the other end, if you need to customize the post-handling of the Token Response, you will need to provide `WebClientReactiveAuthorizationCodeTokenResponseClient.setBodyExtractor()` with a custom configured `BodyExtractor<Mono<OAuth2AccessTokenResponse>, ReactiveHttpInputMessage>` that is used for converting the OAuth 2.0 Access Token Response to an `OAuth2AccessTokenResponse`.
  718. The default implementation provided by `OAuth2BodyExtractors.oauth2AccessTokenResponse()` parses the response and handles errors accordingly.
  719. ==== Customizing the `WebClient`
  720. Alternatively, if your requirements are more advanced, you can take full control of the request/response by simply providing `WebClientReactiveAuthorizationCodeTokenResponseClient.setWebClient()` with a custom configured `WebClient`.
  721. Whether you customize `WebClientReactiveAuthorizationCodeTokenResponseClient` or provide your own implementation of `ReactiveOAuth2AccessTokenResponseClient`, you’ll need to configure it as shown in the following example:
  722. .Access Token Response Configuration
  723. ====
  724. .Java
  725. [source,java,role="primary"]
  726. ----
  727. @EnableWebFluxSecurity
  728. public class OAuth2ClientSecurityConfig {
  729. @Bean
  730. public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {
  731. http
  732. .oauth2Client(oauth2 -> oauth2
  733. .authenticationManager(this.authorizationCodeAuthenticationManager())
  734. ...
  735. );
  736. return http.build();
  737. }
  738. private ReactiveAuthenticationManager authorizationCodeAuthenticationManager() {
  739. WebClientReactiveAuthorizationCodeTokenResponseClient accessTokenResponseClient =
  740. new WebClientReactiveAuthorizationCodeTokenResponseClient();
  741. ...
  742. return new OAuth2AuthorizationCodeReactiveAuthenticationManager(accessTokenResponseClient);
  743. }
  744. }
  745. ----
  746. .Kotlin
  747. [source,kotlin,role="secondary"]
  748. ----
  749. @EnableWebFluxSecurity
  750. class OAuth2ClientSecurityConfig {
  751. @Bean
  752. fun securityFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {
  753. http {
  754. oauth2Client {
  755. authenticationManager = authorizationGrantAuthenticationManager()
  756. }
  757. }
  758. return http.build()
  759. }
  760. private fun authorizationGrantAuthenticationManager(): ReactiveAuthenticationManager {
  761. val accessTokenResponseClient = WebClientReactiveAuthorizationCodeTokenResponseClient()
  762. ...
  763. return OAuth2AuthorizationCodeReactiveAuthenticationManager(accessTokenResponseClient)
  764. }
  765. }
  766. ----
  767. ====
  768. [[oauth2Client-refresh-token-grant]]
  769. === Refresh Token
  770. [NOTE]
  771. 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].
  772. ==== Refreshing an Access Token
  773. [NOTE]
  774. Please refer to the https://tools.ietf.org/html/rfc6749#section-6[Access Token Request/Response] protocol flow for the Refresh Token grant.
  775. 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.
  776. The `WebClientReactiveRefreshTokenTokenResponseClient` is quite flexible as it allows you to customize the pre-processing of the Token Request and/or post-handling of the Token Response.
  777. ==== Customizing the Access Token Request
  778. If you need to customize the pre-processing of the Token Request, you can provide `WebClientReactiveRefreshTokenTokenResponseClient.setParametersConverter()` with a custom `Converter<OAuth2RefreshTokenGrantRequest, MultiValueMap<String, String>>`.
  779. The default implementation builds a `MultiValueMap<String, String>` containing only the `grant_type` parameter of a standard https://tools.ietf.org/html/rfc6749#section-6[OAuth 2.0 Access Token Request] which is used to construct the request. Other parameters required by the Refresh Token grant are added directly to the body of the request by the `WebClientReactiveRefreshTokenTokenResponseClient`.
  780. However, providing a custom `Converter`, would allow you to extend the standard Token Request and add custom parameter(s).
  781. [TIP]
  782. If you prefer to only add additional parameters, you can instead provide `WebClientReactiveRefreshTokenTokenResponseClient.addParametersConverter()` with a custom `Converter<OAuth2RefreshTokenGrantRequest, MultiValueMap<String, String>>` which constructs an aggregate `Converter`.
  783. IMPORTANT: The custom `Converter` must return valid parameters of an OAuth 2.0 Access Token Request that is understood by the intended OAuth 2.0 Provider.
  784. ==== Customizing the Access Token Response
  785. On the other end, if you need to customize the post-handling of the Token Response, you will need to provide `WebClientReactiveRefreshTokenTokenResponseClient.setBodyExtractor()` with a custom configured `BodyExtractor<Mono<OAuth2AccessTokenResponse>, ReactiveHttpInputMessage>` that is used for converting the OAuth 2.0 Access Token Response to an `OAuth2AccessTokenResponse`.
  786. The default implementation provided by `OAuth2BodyExtractors.oauth2AccessTokenResponse()` parses the response and handles errors accordingly.
  787. ==== Customizing the `WebClient`
  788. Alternatively, if your requirements are more advanced, you can take full control of the request/response by simply providing `WebClientReactiveRefreshTokenTokenResponseClient.setWebClient()` with a custom configured `WebClient`.
  789. Whether you customize `WebClientReactiveRefreshTokenTokenResponseClient` or provide your own implementation of `ReactiveOAuth2AccessTokenResponseClient`, you’ll need to configure it as shown in the following example:
  790. .Access Token Response Configuration
  791. ====
  792. .Java
  793. [source,java,role="primary"]
  794. ----
  795. // Customize
  796. ReactiveOAuth2AccessTokenResponseClient<OAuth2RefreshTokenGrantRequest> refreshTokenTokenResponseClient = ...
  797. ReactiveOAuth2AuthorizedClientProvider authorizedClientProvider =
  798. ReactiveOAuth2AuthorizedClientProviderBuilder.builder()
  799. .authorizationCode()
  800. .refreshToken(configurer -> configurer.accessTokenResponseClient(refreshTokenTokenResponseClient))
  801. .build();
  802. ...
  803. authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider);
  804. ----
  805. .Kotlin
  806. [source,kotlin,role="secondary"]
  807. ----
  808. // Customize
  809. val refreshTokenTokenResponseClient: ReactiveOAuth2AccessTokenResponseClient<OAuth2RefreshTokenGrantRequest> = ...
  810. val authorizedClientProvider: ReactiveOAuth2AuthorizedClientProvider = ReactiveOAuth2AuthorizedClientProviderBuilder.builder()
  811. .authorizationCode()
  812. .refreshToken { it.accessTokenResponseClient(refreshTokenTokenResponseClient) }
  813. .build()
  814. ...
  815. authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider)
  816. ----
  817. ====
  818. [NOTE]
  819. `ReactiveOAuth2AuthorizedClientProviderBuilder.builder().refreshToken()` configures a `RefreshTokenReactiveOAuth2AuthorizedClientProvider`,
  820. which is an implementation of a `ReactiveOAuth2AuthorizedClientProvider` for the Refresh Token grant.
  821. The `OAuth2RefreshToken` may optionally be returned in the Access Token Response for the `authorization_code` and `password` grant types.
  822. If the `OAuth2AuthorizedClient.getRefreshToken()` is available and the `OAuth2AuthorizedClient.getAccessToken()` is expired, it will automatically be refreshed by the `RefreshTokenReactiveOAuth2AuthorizedClientProvider`.
  823. [[oauth2Client-client-creds-grant]]
  824. === Client Credentials
  825. [NOTE]
  826. 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.
  827. ==== Requesting an Access Token
  828. [NOTE]
  829. 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.
  830. 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.
  831. The `WebClientReactiveClientCredentialsTokenResponseClient` is quite flexible as it allows you to customize the pre-processing of the Token Request and/or post-handling of the Token Response.
  832. ==== Customizing the Access Token Request
  833. If you need to customize the pre-processing of the Token Request, you can provide `WebClientReactiveClientCredentialsTokenResponseClient.setParametersConverter()` with a custom `Converter<OAuth2ClientCredentialsGrantRequest, MultiValueMap<String, String>>`.
  834. The default implementation builds a `MultiValueMap<String, String>` containing only the `grant_type` parameter of a standard https://tools.ietf.org/html/rfc6749#section-4.4.2[OAuth 2.0 Access Token Request] which is used to construct the request. Other parameters required by the Client Credentials grant are added directly to the body of the request by the `WebClientReactiveClientCredentialsTokenResponseClient`.
  835. However, providing a custom `Converter`, would allow you to extend the standard Token Request and add custom parameter(s).
  836. [TIP]
  837. If you prefer to only add additional parameters, you can instead provide `WebClientReactiveClientCredentialsTokenResponseClient.addParametersConverter()` with a custom `Converter<OAuth2ClientCredentialsGrantRequest, MultiValueMap<String, String>>` which constructs an aggregate `Converter`.
  838. IMPORTANT: The custom `Converter` must return valid parameters of an OAuth 2.0 Access Token Request that is understood by the intended OAuth 2.0 Provider.
  839. ==== Customizing the Access Token Response
  840. On the other end, if you need to customize the post-handling of the Token Response, you will need to provide `WebClientReactiveClientCredentialsTokenResponseClient.setBodyExtractor()` with a custom configured `BodyExtractor<Mono<OAuth2AccessTokenResponse>, ReactiveHttpInputMessage>` that is used for converting the OAuth 2.0 Access Token Response to an `OAuth2AccessTokenResponse`.
  841. The default implementation provided by `OAuth2BodyExtractors.oauth2AccessTokenResponse()` parses the response and handles errors accordingly.
  842. ==== Customizing the `WebClient`
  843. Alternatively, if your requirements are more advanced, you can take full control of the request/response by simply providing `WebClientReactiveClientCredentialsTokenResponseClient.setWebClient()` with a custom configured `WebClient`.
  844. Whether you customize `WebClientReactiveClientCredentialsTokenResponseClient` or provide your own implementation of `ReactiveOAuth2AccessTokenResponseClient`, you'll need to configure it as shown in the following example:
  845. ====
  846. .Java
  847. [source,java,role="primary"]
  848. ----
  849. // Customize
  850. ReactiveOAuth2AccessTokenResponseClient<OAuth2ClientCredentialsGrantRequest> clientCredentialsTokenResponseClient = ...
  851. ReactiveOAuth2AuthorizedClientProvider authorizedClientProvider =
  852. ReactiveOAuth2AuthorizedClientProviderBuilder.builder()
  853. .clientCredentials(configurer -> configurer.accessTokenResponseClient(clientCredentialsTokenResponseClient))
  854. .build();
  855. ...
  856. authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider);
  857. ----
  858. .Kotlin
  859. [source,kotlin,role="secondary"]
  860. ----
  861. // Customize
  862. val clientCredentialsTokenResponseClient: ReactiveOAuth2AccessTokenResponseClient<OAuth2ClientCredentialsGrantRequest> = ...
  863. val authorizedClientProvider: ReactiveOAuth2AuthorizedClientProvider = ReactiveOAuth2AuthorizedClientProviderBuilder.builder()
  864. .clientCredentials { it.accessTokenResponseClient(clientCredentialsTokenResponseClient) }
  865. .build()
  866. ...
  867. authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider)
  868. ----
  869. ====
  870. [NOTE]
  871. `ReactiveOAuth2AuthorizedClientProviderBuilder.builder().clientCredentials()` configures a `ClientCredentialsReactiveOAuth2AuthorizedClientProvider`,
  872. which is an implementation of a `ReactiveOAuth2AuthorizedClientProvider` for the Client Credentials grant.
  873. ==== Using the Access Token
  874. Given the following Spring Boot 2.x properties for an OAuth 2.0 Client registration:
  875. [source,yaml]
  876. ----
  877. spring:
  878. security:
  879. oauth2:
  880. client:
  881. registration:
  882. okta:
  883. client-id: okta-client-id
  884. client-secret: okta-client-secret
  885. authorization-grant-type: client_credentials
  886. scope: read, write
  887. provider:
  888. okta:
  889. token-uri: https://dev-1234.oktapreview.com/oauth2/v1/token
  890. ----
  891. ...and the `ReactiveOAuth2AuthorizedClientManager` `@Bean`:
  892. ====
  893. .Java
  894. [source,java,role="primary"]
  895. ----
  896. @Bean
  897. public ReactiveOAuth2AuthorizedClientManager authorizedClientManager(
  898. ReactiveClientRegistrationRepository clientRegistrationRepository,
  899. ServerOAuth2AuthorizedClientRepository authorizedClientRepository) {
  900. ReactiveOAuth2AuthorizedClientProvider authorizedClientProvider =
  901. ReactiveOAuth2AuthorizedClientProviderBuilder.builder()
  902. .clientCredentials()
  903. .build();
  904. DefaultReactiveOAuth2AuthorizedClientManager authorizedClientManager =
  905. new DefaultReactiveOAuth2AuthorizedClientManager(
  906. clientRegistrationRepository, authorizedClientRepository);
  907. authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider);
  908. return authorizedClientManager;
  909. }
  910. ----
  911. .Kotlin
  912. [source,kotlin,role="secondary"]
  913. ----
  914. @Bean
  915. fun authorizedClientManager(
  916. clientRegistrationRepository: ReactiveClientRegistrationRepository,
  917. authorizedClientRepository: ServerOAuth2AuthorizedClientRepository): ReactiveOAuth2AuthorizedClientManager {
  918. val authorizedClientProvider: ReactiveOAuth2AuthorizedClientProvider = ReactiveOAuth2AuthorizedClientProviderBuilder.builder()
  919. .clientCredentials()
  920. .build()
  921. val authorizedClientManager = DefaultReactiveOAuth2AuthorizedClientManager(
  922. clientRegistrationRepository, authorizedClientRepository)
  923. authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider)
  924. return authorizedClientManager
  925. }
  926. ----
  927. ====
  928. You may obtain the `OAuth2AccessToken` as follows:
  929. ====
  930. .Java
  931. [source,java,role="primary"]
  932. ----
  933. @Controller
  934. public class OAuth2ClientController {
  935. @Autowired
  936. private ReactiveOAuth2AuthorizedClientManager authorizedClientManager;
  937. @GetMapping("/")
  938. public Mono<String> index(Authentication authentication, ServerWebExchange exchange) {
  939. OAuth2AuthorizeRequest authorizeRequest = OAuth2AuthorizeRequest.withClientRegistrationId("okta")
  940. .principal(authentication)
  941. .attribute(ServerWebExchange.class.getName(), exchange)
  942. .build();
  943. return this.authorizedClientManager.authorize(authorizeRequest)
  944. .map(OAuth2AuthorizedClient::getAccessToken)
  945. ...
  946. .thenReturn("index");
  947. }
  948. }
  949. ----
  950. .Kotlin
  951. [source,kotlin,role="secondary"]
  952. ----
  953. class OAuth2ClientController {
  954. @Autowired
  955. private lateinit var authorizedClientManager: ReactiveOAuth2AuthorizedClientManager
  956. @GetMapping("/")
  957. fun index(authentication: Authentication, exchange: ServerWebExchange): Mono<String> {
  958. val authorizeRequest = OAuth2AuthorizeRequest.withClientRegistrationId("okta")
  959. .principal(authentication)
  960. .attribute(ServerWebExchange::class.java.name, exchange)
  961. .build()
  962. return authorizedClientManager.authorize(authorizeRequest)
  963. .map { it.accessToken }
  964. ...
  965. .thenReturn("index")
  966. }
  967. }
  968. ----
  969. ====
  970. [NOTE]
  971. `ServerWebExchange` is an OPTIONAL attribute.
  972. 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`.
  973. [[oauth2Client-password-grant]]
  974. === Resource Owner Password Credentials
  975. [NOTE]
  976. 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.
  977. ==== Requesting an Access Token
  978. [NOTE]
  979. 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.
  980. 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.
  981. The `WebClientReactivePasswordTokenResponseClient` is quite flexible as it allows you to customize the pre-processing of the Token Request and/or post-handling of the Token Response.
  982. ==== Customizing the Access Token Request
  983. If you need to customize the pre-processing of the Token Request, you can provide `WebClientReactivePasswordTokenResponseClient.setParametersConverter()` with a custom `Converter<OAuth2PasswordGrantRequest, MultiValueMap<String, String>>`.
  984. The default implementation builds a `MultiValueMap<String, String>` containing only the `grant_type` parameter of a standard https://tools.ietf.org/html/rfc6749#section-4.4.2[OAuth 2.0 Access Token Request] which is used to construct the request. Other parameters required by the Resource Owner Password Credentials grant are added directly to the body of the request by the `WebClientReactivePasswordTokenResponseClient`.
  985. However, providing a custom `Converter`, would allow you to extend the standard Token Request and add custom parameter(s).
  986. [TIP]
  987. If you prefer to only add additional parameters, you can instead provide `WebClientReactivePasswordTokenResponseClient.addParametersConverter()` with a custom `Converter<OAuth2PasswordGrantRequest, MultiValueMap<String, String>>` which constructs an aggregate `Converter`.
  988. IMPORTANT: The custom `Converter` must return valid parameters of an OAuth 2.0 Access Token Request that is understood by the intended OAuth 2.0 Provider.
  989. ==== Customizing the Access Token Response
  990. On the other end, if you need to customize the post-handling of the Token Response, you will need to provide `WebClientReactivePasswordTokenResponseClient.setBodyExtractor()` with a custom configured `BodyExtractor<Mono<OAuth2AccessTokenResponse>, ReactiveHttpInputMessage>` that is used for converting the OAuth 2.0 Access Token Response to an `OAuth2AccessTokenResponse`.
  991. The default implementation provided by `OAuth2BodyExtractors.oauth2AccessTokenResponse()` parses the response and handles errors accordingly.
  992. ==== Customizing the `WebClient`
  993. Alternatively, if your requirements are more advanced, you can take full control of the request/response by simply providing `WebClientReactivePasswordTokenResponseClient.setWebClient()` with a custom configured `WebClient`.
  994. Whether you customize `WebClientReactivePasswordTokenResponseClient` or provide your own implementation of `ReactiveOAuth2AccessTokenResponseClient`, you'll need to configure it as shown in the following example:
  995. ====
  996. .Java
  997. [source,java,role="primary"]
  998. ----
  999. // Customize
  1000. ReactiveOAuth2AccessTokenResponseClient<OAuth2PasswordGrantRequest> passwordTokenResponseClient = ...
  1001. ReactiveOAuth2AuthorizedClientProvider authorizedClientProvider =
  1002. ReactiveOAuth2AuthorizedClientProviderBuilder.builder()
  1003. .password(configurer -> configurer.accessTokenResponseClient(passwordTokenResponseClient))
  1004. .refreshToken()
  1005. .build();
  1006. ...
  1007. authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider);
  1008. ----
  1009. .Kotlin
  1010. [source,kotlin,role="secondary"]
  1011. ----
  1012. val passwordTokenResponseClient: ReactiveOAuth2AccessTokenResponseClient<OAuth2PasswordGrantRequest> = ...
  1013. val authorizedClientProvider = ReactiveOAuth2AuthorizedClientProviderBuilder.builder()
  1014. .password { it.accessTokenResponseClient(passwordTokenResponseClient) }
  1015. .refreshToken()
  1016. .build()
  1017. ...
  1018. authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider)
  1019. ----
  1020. ====
  1021. [NOTE]
  1022. `ReactiveOAuth2AuthorizedClientProviderBuilder.builder().password()` configures a `PasswordReactiveOAuth2AuthorizedClientProvider`,
  1023. which is an implementation of a `ReactiveOAuth2AuthorizedClientProvider` for the Resource Owner Password Credentials grant.
  1024. ==== Using the Access Token
  1025. Given the following Spring Boot 2.x properties for an OAuth 2.0 Client registration:
  1026. [source,yaml]
  1027. ----
  1028. spring:
  1029. security:
  1030. oauth2:
  1031. client:
  1032. registration:
  1033. okta:
  1034. client-id: okta-client-id
  1035. client-secret: okta-client-secret
  1036. authorization-grant-type: password
  1037. scope: read, write
  1038. provider:
  1039. okta:
  1040. token-uri: https://dev-1234.oktapreview.com/oauth2/v1/token
  1041. ----
  1042. ...and the `ReactiveOAuth2AuthorizedClientManager` `@Bean`:
  1043. ====
  1044. .Java
  1045. [source,java,role="primary"]
  1046. ----
  1047. @Bean
  1048. public ReactiveOAuth2AuthorizedClientManager authorizedClientManager(
  1049. ReactiveClientRegistrationRepository clientRegistrationRepository,
  1050. ServerOAuth2AuthorizedClientRepository authorizedClientRepository) {
  1051. ReactiveOAuth2AuthorizedClientProvider authorizedClientProvider =
  1052. ReactiveOAuth2AuthorizedClientProviderBuilder.builder()
  1053. .password()
  1054. .refreshToken()
  1055. .build();
  1056. DefaultReactiveOAuth2AuthorizedClientManager authorizedClientManager =
  1057. new DefaultReactiveOAuth2AuthorizedClientManager(
  1058. clientRegistrationRepository, authorizedClientRepository);
  1059. authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider);
  1060. // Assuming the `username` and `password` are supplied as `ServerHttpRequest` parameters,
  1061. // map the `ServerHttpRequest` parameters to `OAuth2AuthorizationContext.getAttributes()`
  1062. authorizedClientManager.setContextAttributesMapper(contextAttributesMapper());
  1063. return authorizedClientManager;
  1064. }
  1065. private Function<OAuth2AuthorizeRequest, Mono<Map<String, Object>>> contextAttributesMapper() {
  1066. return authorizeRequest -> {
  1067. Map<String, Object> contextAttributes = Collections.emptyMap();
  1068. ServerWebExchange exchange = authorizeRequest.getAttribute(ServerWebExchange.class.getName());
  1069. ServerHttpRequest request = exchange.getRequest();
  1070. String username = request.getQueryParams().getFirst(OAuth2ParameterNames.USERNAME);
  1071. String password = request.getQueryParams().getFirst(OAuth2ParameterNames.PASSWORD);
  1072. if (StringUtils.hasText(username) && StringUtils.hasText(password)) {
  1073. contextAttributes = new HashMap<>();
  1074. // `PasswordReactiveOAuth2AuthorizedClientProvider` requires both attributes
  1075. contextAttributes.put(OAuth2AuthorizationContext.USERNAME_ATTRIBUTE_NAME, username);
  1076. contextAttributes.put(OAuth2AuthorizationContext.PASSWORD_ATTRIBUTE_NAME, password);
  1077. }
  1078. return Mono.just(contextAttributes);
  1079. };
  1080. }
  1081. ----
  1082. .Kotlin
  1083. [source,kotlin,role="secondary"]
  1084. ----
  1085. @Bean
  1086. fun authorizedClientManager(
  1087. clientRegistrationRepository: ReactiveClientRegistrationRepository,
  1088. authorizedClientRepository: ServerOAuth2AuthorizedClientRepository): ReactiveOAuth2AuthorizedClientManager {
  1089. val authorizedClientProvider: ReactiveOAuth2AuthorizedClientProvider = ReactiveOAuth2AuthorizedClientProviderBuilder.builder()
  1090. .password()
  1091. .refreshToken()
  1092. .build()
  1093. val authorizedClientManager = DefaultReactiveOAuth2AuthorizedClientManager(
  1094. clientRegistrationRepository, authorizedClientRepository)
  1095. authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider)
  1096. // Assuming the `username` and `password` are supplied as `ServerHttpRequest` parameters,
  1097. // map the `ServerHttpRequest` parameters to `OAuth2AuthorizationContext.getAttributes()`
  1098. authorizedClientManager.setContextAttributesMapper(contextAttributesMapper())
  1099. return authorizedClientManager
  1100. }
  1101. private fun contextAttributesMapper(): Function<OAuth2AuthorizeRequest, Mono<MutableMap<String, Any>>> {
  1102. return Function { authorizeRequest ->
  1103. var contextAttributes: MutableMap<String, Any> = mutableMapOf()
  1104. val exchange: ServerWebExchange = authorizeRequest.getAttribute(ServerWebExchange::class.java.name)!!
  1105. val request: ServerHttpRequest = exchange.request
  1106. val username: String? = request.queryParams.getFirst(OAuth2ParameterNames.USERNAME)
  1107. val password: String? = request.queryParams.getFirst(OAuth2ParameterNames.PASSWORD)
  1108. if (StringUtils.hasText(username) && StringUtils.hasText(password)) {
  1109. contextAttributes = hashMapOf()
  1110. // `PasswordReactiveOAuth2AuthorizedClientProvider` requires both attributes
  1111. contextAttributes[OAuth2AuthorizationContext.USERNAME_ATTRIBUTE_NAME] = username!!
  1112. contextAttributes[OAuth2AuthorizationContext.PASSWORD_ATTRIBUTE_NAME] = password!!
  1113. }
  1114. Mono.just(contextAttributes)
  1115. }
  1116. }
  1117. ----
  1118. ====
  1119. You may obtain the `OAuth2AccessToken` as follows:
  1120. ====
  1121. .Java
  1122. [source,java,role="primary"]
  1123. ----
  1124. @Controller
  1125. public class OAuth2ClientController {
  1126. @Autowired
  1127. private ReactiveOAuth2AuthorizedClientManager authorizedClientManager;
  1128. @GetMapping("/")
  1129. public Mono<String> index(Authentication authentication, ServerWebExchange exchange) {
  1130. OAuth2AuthorizeRequest authorizeRequest = OAuth2AuthorizeRequest.withClientRegistrationId("okta")
  1131. .principal(authentication)
  1132. .attribute(ServerWebExchange.class.getName(), exchange)
  1133. .build();
  1134. return this.authorizedClientManager.authorize(authorizeRequest)
  1135. .map(OAuth2AuthorizedClient::getAccessToken)
  1136. ...
  1137. .thenReturn("index");
  1138. }
  1139. }
  1140. ----
  1141. .Kotlin
  1142. [source,kotlin,role="secondary"]
  1143. ----
  1144. @Controller
  1145. class OAuth2ClientController {
  1146. @Autowired
  1147. private lateinit var authorizedClientManager: ReactiveOAuth2AuthorizedClientManager
  1148. @GetMapping("/")
  1149. fun index(authentication: Authentication, exchange: ServerWebExchange): Mono<String> {
  1150. val authorizeRequest = OAuth2AuthorizeRequest.withClientRegistrationId("okta")
  1151. .principal(authentication)
  1152. .attribute(ServerWebExchange::class.java.name, exchange)
  1153. .build()
  1154. return authorizedClientManager.authorize(authorizeRequest)
  1155. .map { it.accessToken }
  1156. ...
  1157. .thenReturn("index")
  1158. }
  1159. }
  1160. ----
  1161. ====
  1162. [NOTE]
  1163. `ServerWebExchange` is an OPTIONAL attribute.
  1164. 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`.
  1165. [[oauth2Client-jwt-bearer-grant]]
  1166. === JWT Bearer
  1167. [NOTE]
  1168. 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.
  1169. ==== Requesting an Access Token
  1170. [NOTE]
  1171. 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.
  1172. 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.
  1173. The `WebClientReactiveJwtBearerTokenResponseClient` is quite flexible as it allows you to customize the pre-processing of the Token Request and/or post-handling of the Token Response.
  1174. ==== Customizing the Access Token Request
  1175. If you need to customize the pre-processing of the Token Request, you can provide `WebClientReactiveJwtBearerTokenResponseClient.setParametersConverter()` with a custom `Converter<JwtBearerGrantRequest, MultiValueMap<String, String>>`.
  1176. The default implementation builds a `MultiValueMap<String, String>` containing only the `grant_type` parameter of a standard https://tools.ietf.org/html/rfc6749#section-4.4.2[OAuth 2.0 Access Token Request] which is used to construct the request. Other parameters required by the JWT Bearer grant are added directly to the body of the request by the `WebClientReactiveJwtBearerTokenResponseClient`.
  1177. However, providing a custom `Converter`, would allow you to extend the standard Token Request and add custom parameter(s).
  1178. [TIP]
  1179. If you prefer to only add additional parameters, you can instead provide `WebClientReactiveJwtBearerTokenResponseClient.addParametersConverter()` with a custom `Converter<JwtBearerGrantRequest, MultiValueMap<String, String>>` which constructs an aggregate `Converter`.
  1180. IMPORTANT: The custom `Converter` must return valid parameters of an OAuth 2.0 Access Token Request that is understood by the intended OAuth 2.0 Provider.
  1181. ==== Customizing the Access Token Response
  1182. On the other end, if you need to customize the post-handling of the Token Response, you will need to provide `WebClientReactiveJwtBearerTokenResponseClient.setBodyExtractor()` with a custom configured `BodyExtractor<Mono<OAuth2AccessTokenResponse>, ReactiveHttpInputMessage>` that is used for converting the OAuth 2.0 Access Token Response to an `OAuth2AccessTokenResponse`.
  1183. The default implementation provided by `OAuth2BodyExtractors.oauth2AccessTokenResponse()` parses the response and handles errors accordingly.
  1184. ==== Customizing the `WebClient`
  1185. Alternatively, if your requirements are more advanced, you can take full control of the request/response by simply providing `WebClientReactiveJwtBearerTokenResponseClient.setWebClient()` with a custom configured `WebClient`.
  1186. Whether you customize `WebClientReactiveJwtBearerTokenResponseClient` or provide your own implementation of `ReactiveOAuth2AccessTokenResponseClient`, you'll need to configure it as shown in the following example:
  1187. ====
  1188. .Java
  1189. [source,java,role="primary"]
  1190. ----
  1191. // Customize
  1192. ReactiveOAuth2AccessTokenResponseClient<JwtBearerGrantRequest> jwtBearerTokenResponseClient = ...
  1193. JwtBearerReactiveOAuth2AuthorizedClientProvider jwtBearerAuthorizedClientProvider = new JwtBearerReactiveOAuth2AuthorizedClientProvider();
  1194. jwtBearerAuthorizedClientProvider.setAccessTokenResponseClient(jwtBearerTokenResponseClient);
  1195. ReactiveOAuth2AuthorizedClientProvider authorizedClientProvider =
  1196. ReactiveOAuth2AuthorizedClientProviderBuilder.builder()
  1197. .provider(jwtBearerAuthorizedClientProvider)
  1198. .build();
  1199. ...
  1200. authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider);
  1201. ----
  1202. .Kotlin
  1203. [source,kotlin,role="secondary"]
  1204. ----
  1205. // Customize
  1206. val jwtBearerTokenResponseClient: ReactiveOAuth2AccessTokenResponseClient<JwtBearerGrantRequest> = ...
  1207. val jwtBearerAuthorizedClientProvider = JwtBearerReactiveOAuth2AuthorizedClientProvider()
  1208. jwtBearerAuthorizedClientProvider.setAccessTokenResponseClient(jwtBearerTokenResponseClient)
  1209. val authorizedClientProvider = ReactiveOAuth2AuthorizedClientProviderBuilder.builder()
  1210. .provider(jwtBearerAuthorizedClientProvider)
  1211. .build()
  1212. ...
  1213. authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider)
  1214. ----
  1215. ====
  1216. ==== Using the Access Token
  1217. Given the following Spring Boot 2.x properties for an OAuth 2.0 Client registration:
  1218. [source,yaml]
  1219. ----
  1220. spring:
  1221. security:
  1222. oauth2:
  1223. client:
  1224. registration:
  1225. okta:
  1226. client-id: okta-client-id
  1227. client-secret: okta-client-secret
  1228. authorization-grant-type: urn:ietf:params:oauth:grant-type:jwt-bearer
  1229. scope: read
  1230. provider:
  1231. okta:
  1232. token-uri: https://dev-1234.oktapreview.com/oauth2/v1/token
  1233. ----
  1234. ...and the `OAuth2AuthorizedClientManager` `@Bean`:
  1235. ====
  1236. .Java
  1237. [source,java,role="primary"]
  1238. ----
  1239. @Bean
  1240. public ReactiveOAuth2AuthorizedClientManager authorizedClientManager(
  1241. ReactiveClientRegistrationRepository clientRegistrationRepository,
  1242. ServerOAuth2AuthorizedClientRepository authorizedClientRepository) {
  1243. JwtBearerReactiveOAuth2AuthorizedClientProvider jwtBearerAuthorizedClientProvider =
  1244. new JwtBearerReactiveOAuth2AuthorizedClientProvider();
  1245. ReactiveOAuth2AuthorizedClientProvider authorizedClientProvider =
  1246. ReactiveOAuth2AuthorizedClientProviderBuilder.builder()
  1247. .provider(jwtBearerAuthorizedClientProvider)
  1248. .build();
  1249. DefaultReactiveOAuth2AuthorizedClientManager authorizedClientManager =
  1250. new DefaultReactiveOAuth2AuthorizedClientManager(
  1251. clientRegistrationRepository, authorizedClientRepository);
  1252. authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider);
  1253. return authorizedClientManager;
  1254. }
  1255. ----
  1256. .Kotlin
  1257. [source,kotlin,role="secondary"]
  1258. ----
  1259. @Bean
  1260. fun authorizedClientManager(
  1261. clientRegistrationRepository: ReactiveClientRegistrationRepository,
  1262. authorizedClientRepository: ServerOAuth2AuthorizedClientRepository): ReactiveOAuth2AuthorizedClientManager {
  1263. val jwtBearerAuthorizedClientProvider = JwtBearerReactiveOAuth2AuthorizedClientProvider()
  1264. val authorizedClientProvider = ReactiveOAuth2AuthorizedClientProviderBuilder.builder()
  1265. .provider(jwtBearerAuthorizedClientProvider)
  1266. .build()
  1267. val authorizedClientManager = DefaultReactiveOAuth2AuthorizedClientManager(
  1268. clientRegistrationRepository, authorizedClientRepository)
  1269. authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider)
  1270. return authorizedClientManager
  1271. }
  1272. ----
  1273. ====
  1274. You may obtain the `OAuth2AccessToken` as follows:
  1275. ====
  1276. .Java
  1277. [source,java,role="primary"]
  1278. ----
  1279. @RestController
  1280. public class OAuth2ResourceServerController {
  1281. @Autowired
  1282. private ReactiveOAuth2AuthorizedClientManager authorizedClientManager;
  1283. @GetMapping("/resource")
  1284. public Mono<String> resource(JwtAuthenticationToken jwtAuthentication, ServerWebExchange exchange) {
  1285. OAuth2AuthorizeRequest authorizeRequest = OAuth2AuthorizeRequest.withClientRegistrationId("okta")
  1286. .principal(jwtAuthentication)
  1287. .build();
  1288. return this.authorizedClientManager.authorize(authorizeRequest)
  1289. .map(OAuth2AuthorizedClient::getAccessToken)
  1290. ...
  1291. }
  1292. }
  1293. ----
  1294. .Kotlin
  1295. [source,kotlin,role="secondary"]
  1296. ----
  1297. class OAuth2ResourceServerController {
  1298. @Autowired
  1299. private lateinit var authorizedClientManager: ReactiveOAuth2AuthorizedClientManager
  1300. @GetMapping("/resource")
  1301. fun resource(jwtAuthentication: JwtAuthenticationToken, exchange: ServerWebExchange): Mono<String> {
  1302. val authorizeRequest = OAuth2AuthorizeRequest.withClientRegistrationId("okta")
  1303. .principal(jwtAuthentication)
  1304. .build()
  1305. return authorizedClientManager.authorize(authorizeRequest)
  1306. .map { it.accessToken }
  1307. ...
  1308. }
  1309. }
  1310. ----
  1311. ====
  1312. [[oauth2Client-client-auth-support]]
  1313. == Client Authentication Support
  1314. [[oauth2Client-jwt-bearer-auth]]
  1315. === JWT Bearer
  1316. [NOTE]
  1317. Please refer to JSON Web Token (JWT) Profile for OAuth 2.0 Client Authentication and Authorization Grants for further details on https://datatracker.ietf.org/doc/html/rfc7523#section-2.2[JWT Bearer] Client Authentication.
  1318. The default implementation for JWT Bearer Client Authentication is `NimbusJwtClientAuthenticationParametersConverter`,
  1319. which is a `Converter` that customizes the Token Request parameters by adding
  1320. a signed JSON Web Token (JWS) in the `client_assertion` parameter.
  1321. The `java.security.PrivateKey` or `javax.crypto.SecretKey` used for signing the JWS
  1322. is supplied by the `com.nimbusds.jose.jwk.JWK` resolver associated with `NimbusJwtClientAuthenticationParametersConverter`.
  1323. ==== Authenticate using `private_key_jwt`
  1324. Given the following Spring Boot 2.x properties for an OAuth 2.0 Client registration:
  1325. [source,yaml]
  1326. ----
  1327. spring:
  1328. security:
  1329. oauth2:
  1330. client:
  1331. registration:
  1332. okta:
  1333. client-id: okta-client-id
  1334. client-authentication-method: private_key_jwt
  1335. authorization-grant-type: authorization_code
  1336. ...
  1337. ----
  1338. The following example shows how to configure `WebClientReactiveAuthorizationCodeTokenResponseClient`:
  1339. ====
  1340. .Java
  1341. [source,java,role="primary"]
  1342. ----
  1343. Function<ClientRegistration, JWK> jwkResolver = (clientRegistration) -> {
  1344. if (clientRegistration.getClientAuthenticationMethod().equals(ClientAuthenticationMethod.PRIVATE_KEY_JWT)) {
  1345. // Assuming RSA key type
  1346. RSAPublicKey publicKey = ...
  1347. RSAPrivateKey privateKey = ...
  1348. return new RSAKey.Builder(publicKey)
  1349. .privateKey(privateKey)
  1350. .keyID(UUID.randomUUID().toString())
  1351. .build();
  1352. }
  1353. return null;
  1354. };
  1355. WebClientReactiveAuthorizationCodeTokenResponseClient tokenResponseClient =
  1356. new WebClientReactiveAuthorizationCodeTokenResponseClient();
  1357. tokenResponseClient.addParametersConverter(
  1358. new NimbusJwtClientAuthenticationParametersConverter<>(jwkResolver));
  1359. ----
  1360. .Kotlin
  1361. [source,kotlin,role="secondary"]
  1362. ----
  1363. val jwkResolver: Function<ClientRegistration, JWK> =
  1364. Function<ClientRegistration, JWK> { clientRegistration ->
  1365. if (clientRegistration.clientAuthenticationMethod.equals(ClientAuthenticationMethod.PRIVATE_KEY_JWT)) {
  1366. // Assuming RSA key type
  1367. var publicKey: RSAPublicKey = ...
  1368. var privateKey: RSAPrivateKey = ...
  1369. RSAKey.Builder(publicKey)
  1370. .privateKey(privateKey)
  1371. .keyID(UUID.randomUUID().toString())
  1372. .build()
  1373. }
  1374. null
  1375. }
  1376. val tokenResponseClient = WebClientReactiveAuthorizationCodeTokenResponseClient()
  1377. tokenResponseClient.addParametersConverter(
  1378. NimbusJwtClientAuthenticationParametersConverter(jwkResolver)
  1379. )
  1380. ----
  1381. ====
  1382. ==== Authenticate using `client_secret_jwt`
  1383. Given the following Spring Boot 2.x properties for an OAuth 2.0 Client registration:
  1384. [source,yaml]
  1385. ----
  1386. spring:
  1387. security:
  1388. oauth2:
  1389. client:
  1390. registration:
  1391. okta:
  1392. client-id: okta-client-id
  1393. client-secret: okta-client-secret
  1394. client-authentication-method: client_secret_jwt
  1395. authorization-grant-type: client_credentials
  1396. ...
  1397. ----
  1398. The following example shows how to configure `DefaultClientCredentialsTokenResponseClient`:
  1399. ====
  1400. .Java
  1401. [source,java,role="primary"]
  1402. ----
  1403. Function<ClientRegistration, JWK> jwkResolver = (clientRegistration) -> {
  1404. if (clientRegistration.getClientAuthenticationMethod().equals(ClientAuthenticationMethod.CLIENT_SECRET_JWT)) {
  1405. SecretKeySpec secretKey = new SecretKeySpec(
  1406. clientRegistration.getClientSecret().getBytes(StandardCharsets.UTF_8),
  1407. "HmacSHA256");
  1408. return new OctetSequenceKey.Builder(secretKey)
  1409. .keyID(UUID.randomUUID().toString())
  1410. .build();
  1411. }
  1412. return null;
  1413. };
  1414. WebClientReactiveAuthorizationCodeTokenResponseClient tokenResponseClient =
  1415. new WebClientReactiveAuthorizationCodeTokenResponseClient();
  1416. tokenResponseClient.addParametersConverter(
  1417. new NimbusJwtClientAuthenticationParametersConverter<>(jwkResolver));
  1418. ----
  1419. .Kotlin
  1420. [source,kotlin,role="secondary"]
  1421. ----
  1422. val jwkResolver = Function<ClientRegistration, JWK?> { clientRegistration: ClientRegistration ->
  1423. if (clientRegistration.clientAuthenticationMethod == ClientAuthenticationMethod.CLIENT_SECRET_JWT) {
  1424. val secretKey = SecretKeySpec(
  1425. clientRegistration.clientSecret.toByteArray(StandardCharsets.UTF_8),
  1426. "HmacSHA256"
  1427. )
  1428. OctetSequenceKey.Builder(secretKey)
  1429. .keyID(UUID.randomUUID().toString())
  1430. .build()
  1431. }
  1432. null
  1433. }
  1434. val tokenResponseClient = WebClientReactiveAuthorizationCodeTokenResponseClient()
  1435. tokenResponseClient.addParametersConverter(
  1436. NimbusJwtClientAuthenticationParametersConverter(jwkResolver)
  1437. )
  1438. ----
  1439. ====
  1440. [[oauth2Client-additional-features]]
  1441. == Additional Features
  1442. [[oauth2Client-registered-authorized-client]]
  1443. === Resolving an Authorized Client
  1444. The `@RegisteredOAuth2AuthorizedClient` annotation provides the capability of resolving a method parameter to an argument value of type `OAuth2AuthorizedClient`.
  1445. This is a convenient alternative compared to accessing the `OAuth2AuthorizedClient` using the `ReactiveOAuth2AuthorizedClientManager` or `ReactiveOAuth2AuthorizedClientService`.
  1446. ====
  1447. .Java
  1448. [source,java,role="primary"]
  1449. ----
  1450. @Controller
  1451. public class OAuth2ClientController {
  1452. @GetMapping("/")
  1453. public Mono<String> index(@RegisteredOAuth2AuthorizedClient("okta") OAuth2AuthorizedClient authorizedClient) {
  1454. return Mono.just(authorizedClient.getAccessToken())
  1455. ...
  1456. .thenReturn("index");
  1457. }
  1458. }
  1459. ----
  1460. .Kotlin
  1461. [source,kotlin,role="secondary"]
  1462. ----
  1463. @Controller
  1464. class OAuth2ClientController {
  1465. @GetMapping("/")
  1466. fun index(@RegisteredOAuth2AuthorizedClient("okta") authorizedClient: OAuth2AuthorizedClient): Mono<String> {
  1467. return Mono.just(authorizedClient.accessToken)
  1468. ...
  1469. .thenReturn("index")
  1470. }
  1471. }
  1472. ----
  1473. ====
  1474. The `@RegisteredOAuth2AuthorizedClient` annotation is handled by `OAuth2AuthorizedClientArgumentResolver`, which directly uses an <<oauth2Client-authorized-manager-provider, ReactiveOAuth2AuthorizedClientManager>> and therefore inherits it's capabilities.
  1475. [[oauth2Client-webclient-webflux]]
  1476. == WebClient integration for Reactive Environments
  1477. The OAuth 2.0 Client support integrates with `WebClient` using an `ExchangeFilterFunction`.
  1478. The `ServerOAuth2AuthorizedClientExchangeFilterFunction` provides a simple mechanism for requesting protected resources by using an `OAuth2AuthorizedClient` and including the associated `OAuth2AccessToken` as a Bearer Token.
  1479. It directly uses an <<oauth2Client-authorized-manager-provider, ReactiveOAuth2AuthorizedClientManager>> and therefore inherits the following capabilities:
  1480. * An `OAuth2AccessToken` will be requested if the client has not yet been authorized.
  1481. ** `authorization_code` - triggers the Authorization Request redirect to initiate the flow
  1482. ** `client_credentials` - the access token is obtained directly from the Token Endpoint
  1483. ** `password` - the access token is obtained directly from the Token Endpoint
  1484. * If the `OAuth2AccessToken` is expired, it will be refreshed (or renewed) if a `ReactiveOAuth2AuthorizedClientProvider` is available to perform the authorization
  1485. The following code shows an example of how to configure `WebClient` with OAuth 2.0 Client support:
  1486. ====
  1487. .Java
  1488. [source,java,role="primary"]
  1489. ----
  1490. @Bean
  1491. WebClient webClient(ReactiveOAuth2AuthorizedClientManager authorizedClientManager) {
  1492. ServerOAuth2AuthorizedClientExchangeFilterFunction oauth2Client =
  1493. new ServerOAuth2AuthorizedClientExchangeFilterFunction(authorizedClientManager);
  1494. return WebClient.builder()
  1495. .filter(oauth2Client)
  1496. .build();
  1497. }
  1498. ----
  1499. .Kotlin
  1500. [source,kotlin,role="secondary"]
  1501. ----
  1502. @Bean
  1503. fun webClient(authorizedClientManager: ReactiveOAuth2AuthorizedClientManager): WebClient {
  1504. val oauth2Client = ServerOAuth2AuthorizedClientExchangeFilterFunction(authorizedClientManager)
  1505. return WebClient.builder()
  1506. .filter(oauth2Client)
  1507. .build()
  1508. }
  1509. ----
  1510. ====
  1511. === Providing the Authorized Client
  1512. The `ServerOAuth2AuthorizedClientExchangeFilterFunction` determines the client to use (for a request) by resolving the `OAuth2AuthorizedClient` from the `ClientRequest.attributes()` (request attributes).
  1513. The following code shows how to set an `OAuth2AuthorizedClient` as a request attribute:
  1514. ====
  1515. .Java
  1516. [source,java,role="primary"]
  1517. ----
  1518. @GetMapping("/")
  1519. public Mono<String> index(@RegisteredOAuth2AuthorizedClient("test-client") OAuth2AuthorizedClient authorizedClient) {
  1520. String resourceUri = ...
  1521. return webClient
  1522. .get()
  1523. .uri(resourceUri)
  1524. .attributes(oauth2AuthorizedClient(authorizedClient)) <1>
  1525. .retrieve()
  1526. .bodyToMono(String.class)
  1527. ...
  1528. .thenReturn("index");
  1529. }
  1530. ----
  1531. .Kotlin
  1532. [source,kotlin,role="secondary"]
  1533. ----
  1534. @GetMapping("/")
  1535. fun index(@RegisteredOAuth2AuthorizedClient("okta") authorizedClient: OAuth2AuthorizedClient): Mono<String> {
  1536. val resourceUri: String = ...
  1537. return webClient
  1538. .get()
  1539. .uri(resourceUri)
  1540. .attributes(oauth2AuthorizedClient(authorizedClient)) <1>
  1541. .retrieve()
  1542. .bodyToMono<String>()
  1543. ...
  1544. .thenReturn("index")
  1545. }
  1546. ----
  1547. ====
  1548. <1> `oauth2AuthorizedClient()` is a `static` method in `ServerOAuth2AuthorizedClientExchangeFilterFunction`.
  1549. The following code shows how to set the `ClientRegistration.getRegistrationId()` as a request attribute:
  1550. ====
  1551. .Java
  1552. [source,java,role="primary"]
  1553. ----
  1554. @GetMapping("/")
  1555. public Mono<String> index() {
  1556. String resourceUri = ...
  1557. return webClient
  1558. .get()
  1559. .uri(resourceUri)
  1560. .attributes(clientRegistrationId("okta")) <1>
  1561. .retrieve()
  1562. .bodyToMono(String.class)
  1563. ...
  1564. .thenReturn("index");
  1565. }
  1566. ----
  1567. .Kotlin
  1568. [source,kotlin,role="secondary"]
  1569. ----
  1570. @GetMapping("/")
  1571. fun index(): Mono<String> {
  1572. val resourceUri: String = ...
  1573. return webClient
  1574. .get()
  1575. .uri(resourceUri)
  1576. .attributes(clientRegistrationId("okta")) <1>
  1577. .retrieve()
  1578. .bodyToMono<String>()
  1579. ...
  1580. .thenReturn("index")
  1581. }
  1582. ----
  1583. ====
  1584. <1> `clientRegistrationId()` is a `static` method in `ServerOAuth2AuthorizedClientExchangeFilterFunction`.
  1585. === Defaulting the Authorized Client
  1586. If neither `OAuth2AuthorizedClient` or `ClientRegistration.getRegistrationId()` is provided as a request attribute, the `ServerOAuth2AuthorizedClientExchangeFilterFunction` can determine the _default_ client to use depending on it's configuration.
  1587. If `setDefaultOAuth2AuthorizedClient(true)` is configured and the user has authenticated using `ServerHttpSecurity.oauth2Login()`, the `OAuth2AccessToken` associated with the current `OAuth2AuthenticationToken` is used.
  1588. The following code shows the specific configuration:
  1589. ====
  1590. .Java
  1591. [source,java,role="primary"]
  1592. ----
  1593. @Bean
  1594. WebClient webClient(ReactiveOAuth2AuthorizedClientManager authorizedClientManager) {
  1595. ServerOAuth2AuthorizedClientExchangeFilterFunction oauth2Client =
  1596. new ServerOAuth2AuthorizedClientExchangeFilterFunction(authorizedClientManager);
  1597. oauth2Client.setDefaultOAuth2AuthorizedClient(true);
  1598. return WebClient.builder()
  1599. .filter(oauth2Client)
  1600. .build();
  1601. }
  1602. ----
  1603. .Kotlin
  1604. [source,kotlin,role="secondary"]
  1605. ----
  1606. @Bean
  1607. fun webClient(authorizedClientManager: ReactiveOAuth2AuthorizedClientManager): WebClient {
  1608. val oauth2Client = ServerOAuth2AuthorizedClientExchangeFilterFunction(authorizedClientManager)
  1609. oauth2Client.setDefaultOAuth2AuthorizedClient(true)
  1610. return WebClient.builder()
  1611. .filter(oauth2Client)
  1612. .build()
  1613. }
  1614. ----
  1615. ====
  1616. [WARNING]
  1617. It is recommended to be cautious with this feature since all HTTP requests will receive the access token.
  1618. Alternatively, if `setDefaultClientRegistrationId("okta")` is configured with a valid `ClientRegistration`, the `OAuth2AccessToken` associated with the `OAuth2AuthorizedClient` is used.
  1619. The following code shows the specific configuration:
  1620. ====
  1621. .Java
  1622. [source,java,role="primary"]
  1623. ----
  1624. @Bean
  1625. WebClient webClient(ReactiveOAuth2AuthorizedClientManager authorizedClientManager) {
  1626. ServerOAuth2AuthorizedClientExchangeFilterFunction oauth2Client =
  1627. new ServerOAuth2AuthorizedClientExchangeFilterFunction(authorizedClientManager);
  1628. oauth2Client.setDefaultClientRegistrationId("okta");
  1629. return WebClient.builder()
  1630. .filter(oauth2Client)
  1631. .build();
  1632. }
  1633. ----
  1634. .Kotlin
  1635. [source,kotlin,role="secondary"]
  1636. ----
  1637. @Bean
  1638. fun webClient(authorizedClientManager: ReactiveOAuth2AuthorizedClientManager): WebClient {
  1639. val oauth2Client = ServerOAuth2AuthorizedClientExchangeFilterFunction(authorizedClientManager)
  1640. oauth2Client.setDefaultClientRegistrationId("okta")
  1641. return WebClient.builder()
  1642. .filter(oauth2Client)
  1643. .build()
  1644. }
  1645. ----
  1646. ====
  1647. [WARNING]
  1648. It is recommended to be cautious with this feature since all HTTP requests will receive the access token.