2
0

oauth2-client.adoc 86 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077
  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. ==== Requesting an Access Token
  704. [NOTE]
  705. 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.
  706. 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.
  707. 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.
  708. ==== Customizing the Access Token Request
  709. 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>>`.
  710. 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`.
  711. However, providing a custom `Converter`, would allow you to extend the standard Token Request and add custom parameter(s).
  712. [TIP]
  713. 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`.
  714. 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.
  715. ==== Customizing the Access Token Response
  716. 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`.
  717. The default implementation provided by `OAuth2BodyExtractors.oauth2AccessTokenResponse()` parses the response and handles errors accordingly.
  718. ==== Customizing the `WebClient`
  719. 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`.
  720. Whether you customize `WebClientReactiveAuthorizationCodeTokenResponseClient` or provide your own implementation of `ReactiveOAuth2AccessTokenResponseClient`, you’ll need to configure it as shown in the following example:
  721. .Access Token Response Configuration
  722. ====
  723. .Java
  724. [source,java,role="primary"]
  725. ----
  726. @EnableWebFluxSecurity
  727. public class OAuth2ClientSecurityConfig {
  728. @Bean
  729. public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {
  730. http
  731. .oauth2Client(oauth2 -> oauth2
  732. .authenticationManager(this.authorizationCodeAuthenticationManager())
  733. ...
  734. );
  735. return http.build();
  736. }
  737. private ReactiveAuthenticationManager authorizationCodeAuthenticationManager() {
  738. WebClientReactiveAuthorizationCodeTokenResponseClient accessTokenResponseClient =
  739. new WebClientReactiveAuthorizationCodeTokenResponseClient();
  740. ...
  741. return new OAuth2AuthorizationCodeReactiveAuthenticationManager(accessTokenResponseClient);
  742. }
  743. }
  744. ----
  745. .Kotlin
  746. [source,kotlin,role="secondary"]
  747. ----
  748. @EnableWebFluxSecurity
  749. class OAuth2ClientSecurityConfig {
  750. @Bean
  751. fun securityFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {
  752. http {
  753. oauth2Client {
  754. authenticationManager = authorizationGrantAuthenticationManager()
  755. }
  756. }
  757. return http.build()
  758. }
  759. private fun authorizationGrantAuthenticationManager(): ReactiveAuthenticationManager {
  760. val accessTokenResponseClient = WebClientReactiveAuthorizationCodeTokenResponseClient()
  761. ...
  762. return OAuth2AuthorizationCodeReactiveAuthenticationManager(accessTokenResponseClient)
  763. }
  764. }
  765. ====
  766. [[oauth2Client-refresh-token-grant]]
  767. === Refresh Token
  768. [NOTE]
  769. 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].
  770. ==== Refreshing an Access Token
  771. [NOTE]
  772. Please refer to the https://tools.ietf.org/html/rfc6749#section-6[Access Token Request/Response] protocol flow for the Refresh Token grant.
  773. 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.
  774. 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.
  775. ==== Customizing the Access Token Request
  776. 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>>`.
  777. 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`.
  778. However, providing a custom `Converter`, would allow you to extend the standard Token Request and add custom parameter(s).
  779. [TIP]
  780. 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`.
  781. 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.
  782. ==== Customizing the Access Token Response
  783. 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`.
  784. The default implementation provided by `OAuth2BodyExtractors.oauth2AccessTokenResponse()` parses the response and handles errors accordingly.
  785. ==== Customizing the `WebClient`
  786. 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`.
  787. Whether you customize `WebClientReactiveRefreshTokenTokenResponseClient` or provide your own implementation of `ReactiveOAuth2AccessTokenResponseClient`, you’ll need to configure it as shown in the following example:
  788. .Access Token Response Configuration
  789. ====
  790. .Java
  791. [source,java,role="primary"]
  792. ----
  793. // Customize
  794. ReactiveOAuth2AccessTokenResponseClient<OAuth2RefreshTokenGrantRequest> refreshTokenTokenResponseClient = ...
  795. ReactiveOAuth2AuthorizedClientProvider authorizedClientProvider =
  796. ReactiveOAuth2AuthorizedClientProviderBuilder.builder()
  797. .authorizationCode()
  798. .refreshToken(configurer -> configurer.accessTokenResponseClient(refreshTokenTokenResponseClient))
  799. .build();
  800. ...
  801. authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider);
  802. ----
  803. .Kotlin
  804. [source,kotlin,role="secondary"]
  805. ----
  806. // Customize
  807. val refreshTokenTokenResponseClient: ReactiveOAuth2AccessTokenResponseClient<OAuth2RefreshTokenGrantRequest> = ...
  808. val authorizedClientProvider: ReactiveOAuth2AuthorizedClientProvider = ReactiveOAuth2AuthorizedClientProviderBuilder.builder()
  809. .authorizationCode()
  810. .refreshToken { it.accessTokenResponseClient(refreshTokenTokenResponseClient) }
  811. .build()
  812. ...
  813. authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider)
  814. ====
  815. [NOTE]
  816. `ReactiveOAuth2AuthorizedClientProviderBuilder.builder().refreshToken()` configures a `RefreshTokenReactiveOAuth2AuthorizedClientProvider`,
  817. which is an implementation of a `ReactiveOAuth2AuthorizedClientProvider` for the Refresh Token grant.
  818. The `OAuth2RefreshToken` may optionally be returned in the Access Token Response for the `authorization_code` and `password` grant types.
  819. If the `OAuth2AuthorizedClient.getRefreshToken()` is available and the `OAuth2AuthorizedClient.getAccessToken()` is expired, it will automatically be refreshed by the `RefreshTokenReactiveOAuth2AuthorizedClientProvider`.
  820. [[oauth2Client-client-creds-grant]]
  821. === Client Credentials
  822. [NOTE]
  823. 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.
  824. ==== Requesting an Access Token
  825. [NOTE]
  826. 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.
  827. 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.
  828. 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.
  829. ==== Customizing the Access Token Request
  830. 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>>`.
  831. 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`.
  832. However, providing a custom `Converter`, would allow you to extend the standard Token Request and add custom parameter(s).
  833. [TIP]
  834. 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`.
  835. 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.
  836. ==== Customizing the Access Token Response
  837. 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`.
  838. The default implementation provided by `OAuth2BodyExtractors.oauth2AccessTokenResponse()` parses the response and handles errors accordingly.
  839. ==== Customizing the `WebClient`
  840. 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`.
  841. Whether you customize `WebClientReactiveClientCredentialsTokenResponseClient` or provide your own implementation of `ReactiveOAuth2AccessTokenResponseClient`, you'll need to configure it as shown in the following example:
  842. ====
  843. .Java
  844. [source,java,role="primary"]
  845. ----
  846. // Customize
  847. ReactiveOAuth2AccessTokenResponseClient<OAuth2ClientCredentialsGrantRequest> clientCredentialsTokenResponseClient = ...
  848. ReactiveOAuth2AuthorizedClientProvider authorizedClientProvider =
  849. ReactiveOAuth2AuthorizedClientProviderBuilder.builder()
  850. .clientCredentials(configurer -> configurer.accessTokenResponseClient(clientCredentialsTokenResponseClient))
  851. .build();
  852. ...
  853. authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider);
  854. ----
  855. .Kotlin
  856. [source,kotlin,role="secondary"]
  857. ----
  858. // Customize
  859. val clientCredentialsTokenResponseClient: ReactiveOAuth2AccessTokenResponseClient<OAuth2ClientCredentialsGrantRequest> = ...
  860. val authorizedClientProvider: ReactiveOAuth2AuthorizedClientProvider = ReactiveOAuth2AuthorizedClientProviderBuilder.builder()
  861. .clientCredentials { it.accessTokenResponseClient(clientCredentialsTokenResponseClient) }
  862. .build()
  863. ...
  864. authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider)
  865. ----
  866. ====
  867. [NOTE]
  868. `ReactiveOAuth2AuthorizedClientProviderBuilder.builder().clientCredentials()` configures a `ClientCredentialsReactiveOAuth2AuthorizedClientProvider`,
  869. which is an implementation of a `ReactiveOAuth2AuthorizedClientProvider` for the Client Credentials grant.
  870. ==== Using the Access Token
  871. Given the following Spring Boot 2.x properties for an OAuth 2.0 Client registration:
  872. [source,yaml]
  873. ----
  874. spring:
  875. security:
  876. oauth2:
  877. client:
  878. registration:
  879. okta:
  880. client-id: okta-client-id
  881. client-secret: okta-client-secret
  882. authorization-grant-type: client_credentials
  883. scope: read, write
  884. provider:
  885. okta:
  886. token-uri: https://dev-1234.oktapreview.com/oauth2/v1/token
  887. ----
  888. ...and the `ReactiveOAuth2AuthorizedClientManager` `@Bean`:
  889. ====
  890. .Java
  891. [source,java,role="primary"]
  892. ----
  893. @Bean
  894. public ReactiveOAuth2AuthorizedClientManager authorizedClientManager(
  895. ReactiveClientRegistrationRepository clientRegistrationRepository,
  896. ServerOAuth2AuthorizedClientRepository authorizedClientRepository) {
  897. ReactiveOAuth2AuthorizedClientProvider authorizedClientProvider =
  898. ReactiveOAuth2AuthorizedClientProviderBuilder.builder()
  899. .clientCredentials()
  900. .build();
  901. DefaultReactiveOAuth2AuthorizedClientManager authorizedClientManager =
  902. new DefaultReactiveOAuth2AuthorizedClientManager(
  903. clientRegistrationRepository, authorizedClientRepository);
  904. authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider);
  905. return authorizedClientManager;
  906. }
  907. ----
  908. .Kotlin
  909. [source,kotlin,role="secondary"]
  910. ----
  911. @Bean
  912. fun authorizedClientManager(
  913. clientRegistrationRepository: ReactiveClientRegistrationRepository,
  914. authorizedClientRepository: ServerOAuth2AuthorizedClientRepository): ReactiveOAuth2AuthorizedClientManager {
  915. val authorizedClientProvider: ReactiveOAuth2AuthorizedClientProvider = ReactiveOAuth2AuthorizedClientProviderBuilder.builder()
  916. .clientCredentials()
  917. .build()
  918. val authorizedClientManager = DefaultReactiveOAuth2AuthorizedClientManager(
  919. clientRegistrationRepository, authorizedClientRepository)
  920. authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider)
  921. return authorizedClientManager
  922. }
  923. ----
  924. ====
  925. You may obtain the `OAuth2AccessToken` as follows:
  926. ====
  927. .Java
  928. [source,java,role="primary"]
  929. ----
  930. @Controller
  931. public class OAuth2ClientController {
  932. @Autowired
  933. private ReactiveOAuth2AuthorizedClientManager authorizedClientManager;
  934. @GetMapping("/")
  935. public Mono<String> index(Authentication authentication, ServerWebExchange exchange) {
  936. OAuth2AuthorizeRequest authorizeRequest = OAuth2AuthorizeRequest.withClientRegistrationId("okta")
  937. .principal(authentication)
  938. .attribute(ServerWebExchange.class.getName(), exchange)
  939. .build();
  940. return this.authorizedClientManager.authorize(authorizeRequest)
  941. .map(OAuth2AuthorizedClient::getAccessToken)
  942. ...
  943. .thenReturn("index");
  944. }
  945. }
  946. ----
  947. .Kotlin
  948. [source,kotlin,role="secondary"]
  949. ----
  950. class OAuth2ClientController {
  951. @Autowired
  952. private lateinit var authorizedClientManager: ReactiveOAuth2AuthorizedClientManager
  953. @GetMapping("/")
  954. fun index(authentication: Authentication, exchange: ServerWebExchange): Mono<String> {
  955. val authorizeRequest = OAuth2AuthorizeRequest.withClientRegistrationId("okta")
  956. .principal(authentication)
  957. .attribute(ServerWebExchange::class.java.name, exchange)
  958. .build()
  959. return authorizedClientManager.authorize(authorizeRequest)
  960. .map { it.accessToken }
  961. ...
  962. .thenReturn("index")
  963. }
  964. }
  965. ----
  966. ====
  967. [NOTE]
  968. `ServerWebExchange` is an OPTIONAL attribute.
  969. 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`.
  970. [[oauth2Client-password-grant]]
  971. === Resource Owner Password Credentials
  972. [NOTE]
  973. 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.
  974. ==== Requesting an Access Token
  975. [NOTE]
  976. 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.
  977. 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.
  978. 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.
  979. ==== Customizing the Access Token Request
  980. 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>>`.
  981. 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`.
  982. However, providing a custom `Converter`, would allow you to extend the standard Token Request and add custom parameter(s).
  983. [TIP]
  984. 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`.
  985. 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.
  986. ==== Customizing the Access Token Response
  987. 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`.
  988. The default implementation provided by `OAuth2BodyExtractors.oauth2AccessTokenResponse()` parses the response and handles errors accordingly.
  989. ==== Customizing the `WebClient`
  990. 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`.
  991. Whether you customize `WebClientReactivePasswordTokenResponseClient` or provide your own implementation of `ReactiveOAuth2AccessTokenResponseClient`, you'll need to configure it as shown in the following example:
  992. ====
  993. .Java
  994. [source,java,role="primary"]
  995. ----
  996. // Customize
  997. ReactiveOAuth2AccessTokenResponseClient<OAuth2PasswordGrantRequest> passwordTokenResponseClient = ...
  998. ReactiveOAuth2AuthorizedClientProvider authorizedClientProvider =
  999. ReactiveOAuth2AuthorizedClientProviderBuilder.builder()
  1000. .password(configurer -> configurer.accessTokenResponseClient(passwordTokenResponseClient))
  1001. .refreshToken()
  1002. .build();
  1003. ...
  1004. authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider);
  1005. ----
  1006. .Kotlin
  1007. [source,kotlin,role="secondary"]
  1008. ----
  1009. val passwordTokenResponseClient: ReactiveOAuth2AccessTokenResponseClient<OAuth2PasswordGrantRequest> = ...
  1010. val authorizedClientProvider = ReactiveOAuth2AuthorizedClientProviderBuilder.builder()
  1011. .password { it.accessTokenResponseClient(passwordTokenResponseClient) }
  1012. .refreshToken()
  1013. .build()
  1014. ...
  1015. authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider)
  1016. ----
  1017. ====
  1018. [NOTE]
  1019. `ReactiveOAuth2AuthorizedClientProviderBuilder.builder().password()` configures a `PasswordReactiveOAuth2AuthorizedClientProvider`,
  1020. which is an implementation of a `ReactiveOAuth2AuthorizedClientProvider` for the Resource Owner Password Credentials grant.
  1021. ==== Using the Access Token
  1022. Given the following Spring Boot 2.x properties for an OAuth 2.0 Client registration:
  1023. [source,yaml]
  1024. ----
  1025. spring:
  1026. security:
  1027. oauth2:
  1028. client:
  1029. registration:
  1030. okta:
  1031. client-id: okta-client-id
  1032. client-secret: okta-client-secret
  1033. authorization-grant-type: password
  1034. scope: read, write
  1035. provider:
  1036. okta:
  1037. token-uri: https://dev-1234.oktapreview.com/oauth2/v1/token
  1038. ----
  1039. ...and the `ReactiveOAuth2AuthorizedClientManager` `@Bean`:
  1040. ====
  1041. .Java
  1042. [source,java,role="primary"]
  1043. ----
  1044. @Bean
  1045. public ReactiveOAuth2AuthorizedClientManager authorizedClientManager(
  1046. ReactiveClientRegistrationRepository clientRegistrationRepository,
  1047. ServerOAuth2AuthorizedClientRepository authorizedClientRepository) {
  1048. ReactiveOAuth2AuthorizedClientProvider authorizedClientProvider =
  1049. ReactiveOAuth2AuthorizedClientProviderBuilder.builder()
  1050. .password()
  1051. .refreshToken()
  1052. .build();
  1053. DefaultReactiveOAuth2AuthorizedClientManager authorizedClientManager =
  1054. new DefaultReactiveOAuth2AuthorizedClientManager(
  1055. clientRegistrationRepository, authorizedClientRepository);
  1056. authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider);
  1057. // Assuming the `username` and `password` are supplied as `ServerHttpRequest` parameters,
  1058. // map the `ServerHttpRequest` parameters to `OAuth2AuthorizationContext.getAttributes()`
  1059. authorizedClientManager.setContextAttributesMapper(contextAttributesMapper());
  1060. return authorizedClientManager;
  1061. }
  1062. private Function<OAuth2AuthorizeRequest, Mono<Map<String, Object>>> contextAttributesMapper() {
  1063. return authorizeRequest -> {
  1064. Map<String, Object> contextAttributes = Collections.emptyMap();
  1065. ServerWebExchange exchange = authorizeRequest.getAttribute(ServerWebExchange.class.getName());
  1066. ServerHttpRequest request = exchange.getRequest();
  1067. String username = request.getQueryParams().getFirst(OAuth2ParameterNames.USERNAME);
  1068. String password = request.getQueryParams().getFirst(OAuth2ParameterNames.PASSWORD);
  1069. if (StringUtils.hasText(username) && StringUtils.hasText(password)) {
  1070. contextAttributes = new HashMap<>();
  1071. // `PasswordReactiveOAuth2AuthorizedClientProvider` requires both attributes
  1072. contextAttributes.put(OAuth2AuthorizationContext.USERNAME_ATTRIBUTE_NAME, username);
  1073. contextAttributes.put(OAuth2AuthorizationContext.PASSWORD_ATTRIBUTE_NAME, password);
  1074. }
  1075. return Mono.just(contextAttributes);
  1076. };
  1077. }
  1078. ----
  1079. .Kotlin
  1080. [source,kotlin,role="secondary"]
  1081. ----
  1082. @Bean
  1083. fun authorizedClientManager(
  1084. clientRegistrationRepository: ReactiveClientRegistrationRepository,
  1085. authorizedClientRepository: ServerOAuth2AuthorizedClientRepository): ReactiveOAuth2AuthorizedClientManager {
  1086. val authorizedClientProvider: ReactiveOAuth2AuthorizedClientProvider = ReactiveOAuth2AuthorizedClientProviderBuilder.builder()
  1087. .password()
  1088. .refreshToken()
  1089. .build()
  1090. val authorizedClientManager = DefaultReactiveOAuth2AuthorizedClientManager(
  1091. clientRegistrationRepository, authorizedClientRepository)
  1092. authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider)
  1093. // Assuming the `username` and `password` are supplied as `ServerHttpRequest` parameters,
  1094. // map the `ServerHttpRequest` parameters to `OAuth2AuthorizationContext.getAttributes()`
  1095. authorizedClientManager.setContextAttributesMapper(contextAttributesMapper())
  1096. return authorizedClientManager
  1097. }
  1098. private fun contextAttributesMapper(): Function<OAuth2AuthorizeRequest, Mono<MutableMap<String, Any>>> {
  1099. return Function { authorizeRequest ->
  1100. var contextAttributes: MutableMap<String, Any> = mutableMapOf()
  1101. val exchange: ServerWebExchange = authorizeRequest.getAttribute(ServerWebExchange::class.java.name)!!
  1102. val request: ServerHttpRequest = exchange.request
  1103. val username: String? = request.queryParams.getFirst(OAuth2ParameterNames.USERNAME)
  1104. val password: String? = request.queryParams.getFirst(OAuth2ParameterNames.PASSWORD)
  1105. if (StringUtils.hasText(username) && StringUtils.hasText(password)) {
  1106. contextAttributes = hashMapOf()
  1107. // `PasswordReactiveOAuth2AuthorizedClientProvider` requires both attributes
  1108. contextAttributes[OAuth2AuthorizationContext.USERNAME_ATTRIBUTE_NAME] = username!!
  1109. contextAttributes[OAuth2AuthorizationContext.PASSWORD_ATTRIBUTE_NAME] = password!!
  1110. }
  1111. Mono.just(contextAttributes)
  1112. }
  1113. }
  1114. ----
  1115. ====
  1116. You may obtain the `OAuth2AccessToken` as follows:
  1117. ====
  1118. .Java
  1119. [source,java,role="primary"]
  1120. ----
  1121. @Controller
  1122. public class OAuth2ClientController {
  1123. @Autowired
  1124. private ReactiveOAuth2AuthorizedClientManager authorizedClientManager;
  1125. @GetMapping("/")
  1126. public Mono<String> index(Authentication authentication, ServerWebExchange exchange) {
  1127. OAuth2AuthorizeRequest authorizeRequest = OAuth2AuthorizeRequest.withClientRegistrationId("okta")
  1128. .principal(authentication)
  1129. .attribute(ServerWebExchange.class.getName(), exchange)
  1130. .build();
  1131. return this.authorizedClientManager.authorize(authorizeRequest)
  1132. .map(OAuth2AuthorizedClient::getAccessToken)
  1133. ...
  1134. .thenReturn("index");
  1135. }
  1136. }
  1137. ----
  1138. .Kotlin
  1139. [source,kotlin,role="secondary"]
  1140. ----
  1141. @Controller
  1142. class OAuth2ClientController {
  1143. @Autowired
  1144. private lateinit var authorizedClientManager: ReactiveOAuth2AuthorizedClientManager
  1145. @GetMapping("/")
  1146. fun index(authentication: Authentication, exchange: ServerWebExchange): Mono<String> {
  1147. val authorizeRequest = OAuth2AuthorizeRequest.withClientRegistrationId("okta")
  1148. .principal(authentication)
  1149. .attribute(ServerWebExchange::class.java.name, exchange)
  1150. .build()
  1151. return authorizedClientManager.authorize(authorizeRequest)
  1152. .map { it.accessToken }
  1153. ...
  1154. .thenReturn("index")
  1155. }
  1156. }
  1157. ----
  1158. ====
  1159. [NOTE]
  1160. `ServerWebExchange` is an OPTIONAL attribute.
  1161. 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`.
  1162. [[oauth2Client-jwt-bearer-grant]]
  1163. === JWT Bearer
  1164. [NOTE]
  1165. 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.
  1166. ==== Requesting an Access Token
  1167. [NOTE]
  1168. 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.
  1169. 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.
  1170. 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.
  1171. ==== Customizing the Access Token Request
  1172. 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>>`.
  1173. 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`.
  1174. However, providing a custom `Converter`, would allow you to extend the standard Token Request and add custom parameter(s).
  1175. [TIP]
  1176. 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`.
  1177. 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.
  1178. ==== Customizing the Access Token Response
  1179. 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`.
  1180. The default implementation provided by `OAuth2BodyExtractors.oauth2AccessTokenResponse()` parses the response and handles errors accordingly.
  1181. ==== Customizing the `WebClient`
  1182. 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`.
  1183. Whether you customize `WebClientReactiveJwtBearerTokenResponseClient` or provide your own implementation of `ReactiveOAuth2AccessTokenResponseClient`, you'll need to configure it as shown in the following example:
  1184. ====
  1185. .Java
  1186. [source,java,role="primary"]
  1187. ----
  1188. // Customize
  1189. ReactiveOAuth2AccessTokenResponseClient<JwtBearerGrantRequest> jwtBearerTokenResponseClient = ...
  1190. JwtBearerReactiveOAuth2AuthorizedClientProvider jwtBearerAuthorizedClientProvider = new JwtBearerReactiveOAuth2AuthorizedClientProvider();
  1191. jwtBearerAuthorizedClientProvider.setAccessTokenResponseClient(jwtBearerTokenResponseClient);
  1192. ReactiveOAuth2AuthorizedClientProvider authorizedClientProvider =
  1193. ReactiveOAuth2AuthorizedClientProviderBuilder.builder()
  1194. .provider(jwtBearerAuthorizedClientProvider)
  1195. .build();
  1196. ...
  1197. authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider);
  1198. ----
  1199. .Kotlin
  1200. [source,kotlin,role="secondary"]
  1201. ----
  1202. // Customize
  1203. val jwtBearerTokenResponseClient: ReactiveOAuth2AccessTokenResponseClient<JwtBearerGrantRequest> = ...
  1204. val jwtBearerAuthorizedClientProvider = JwtBearerReactiveOAuth2AuthorizedClientProvider()
  1205. jwtBearerAuthorizedClientProvider.setAccessTokenResponseClient(jwtBearerTokenResponseClient)
  1206. val authorizedClientProvider = ReactiveOAuth2AuthorizedClientProviderBuilder.builder()
  1207. .provider(jwtBearerAuthorizedClientProvider)
  1208. .build()
  1209. ...
  1210. authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider)
  1211. ----
  1212. ====
  1213. ==== Using the Access Token
  1214. Given the following Spring Boot 2.x properties for an OAuth 2.0 Client registration:
  1215. [source,yaml]
  1216. ----
  1217. spring:
  1218. security:
  1219. oauth2:
  1220. client:
  1221. registration:
  1222. okta:
  1223. client-id: okta-client-id
  1224. client-secret: okta-client-secret
  1225. authorization-grant-type: urn:ietf:params:oauth:grant-type:jwt-bearer
  1226. scope: read
  1227. provider:
  1228. okta:
  1229. token-uri: https://dev-1234.oktapreview.com/oauth2/v1/token
  1230. ----
  1231. ...and the `OAuth2AuthorizedClientManager` `@Bean`:
  1232. ====
  1233. .Java
  1234. [source,java,role="primary"]
  1235. ----
  1236. @Bean
  1237. public ReactiveOAuth2AuthorizedClientManager authorizedClientManager(
  1238. ReactiveClientRegistrationRepository clientRegistrationRepository,
  1239. ServerOAuth2AuthorizedClientRepository authorizedClientRepository) {
  1240. JwtBearerReactiveOAuth2AuthorizedClientProvider jwtBearerAuthorizedClientProvider =
  1241. new JwtBearerReactiveOAuth2AuthorizedClientProvider();
  1242. ReactiveOAuth2AuthorizedClientProvider authorizedClientProvider =
  1243. ReactiveOAuth2AuthorizedClientProviderBuilder.builder()
  1244. .provider(jwtBearerAuthorizedClientProvider)
  1245. .build();
  1246. DefaultReactiveOAuth2AuthorizedClientManager authorizedClientManager =
  1247. new DefaultReactiveOAuth2AuthorizedClientManager(
  1248. clientRegistrationRepository, authorizedClientRepository);
  1249. authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider);
  1250. return authorizedClientManager;
  1251. }
  1252. ----
  1253. .Kotlin
  1254. [source,kotlin,role="secondary"]
  1255. ----
  1256. @Bean
  1257. fun authorizedClientManager(
  1258. clientRegistrationRepository: ReactiveClientRegistrationRepository,
  1259. authorizedClientRepository: ServerOAuth2AuthorizedClientRepository): ReactiveOAuth2AuthorizedClientManager {
  1260. val jwtBearerAuthorizedClientProvider = JwtBearerReactiveOAuth2AuthorizedClientProvider()
  1261. val authorizedClientProvider = ReactiveOAuth2AuthorizedClientProviderBuilder.builder()
  1262. .provider(jwtBearerAuthorizedClientProvider)
  1263. .build()
  1264. val authorizedClientManager = DefaultReactiveOAuth2AuthorizedClientManager(
  1265. clientRegistrationRepository, authorizedClientRepository)
  1266. authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider)
  1267. return authorizedClientManager
  1268. }
  1269. ----
  1270. ====
  1271. You may obtain the `OAuth2AccessToken` as follows:
  1272. ====
  1273. .Java
  1274. [source,java,role="primary"]
  1275. ----
  1276. @RestController
  1277. public class OAuth2ResourceServerController {
  1278. @Autowired
  1279. private ReactiveOAuth2AuthorizedClientManager authorizedClientManager;
  1280. @GetMapping("/resource")
  1281. public Mono<String> resource(JwtAuthenticationToken jwtAuthentication, ServerWebExchange exchange) {
  1282. OAuth2AuthorizeRequest authorizeRequest = OAuth2AuthorizeRequest.withClientRegistrationId("okta")
  1283. .principal(jwtAuthentication)
  1284. .build();
  1285. return this.authorizedClientManager.authorize(authorizeRequest)
  1286. .map(OAuth2AuthorizedClient::getAccessToken)
  1287. ...
  1288. }
  1289. }
  1290. ----
  1291. .Kotlin
  1292. [source,kotlin,role="secondary"]
  1293. ----
  1294. class OAuth2ResourceServerController {
  1295. @Autowired
  1296. private lateinit var authorizedClientManager: ReactiveOAuth2AuthorizedClientManager
  1297. @GetMapping("/resource")
  1298. fun resource(jwtAuthentication: JwtAuthenticationToken, exchange: ServerWebExchange): Mono<String> {
  1299. val authorizeRequest = OAuth2AuthorizeRequest.withClientRegistrationId("okta")
  1300. .principal(jwtAuthentication)
  1301. .build()
  1302. return authorizedClientManager.authorize(authorizeRequest)
  1303. .map { it.accessToken }
  1304. ...
  1305. }
  1306. }
  1307. ----
  1308. ====
  1309. [[oauth2Client-client-auth-support]]
  1310. == Client Authentication Support
  1311. [[oauth2Client-jwt-bearer-auth]]
  1312. === JWT Bearer
  1313. [NOTE]
  1314. 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.
  1315. The default implementation for JWT Bearer Client Authentication is `NimbusJwtClientAuthenticationParametersConverter`,
  1316. which is a `Converter` that customizes the Token Request parameters by adding
  1317. a signed JSON Web Token (JWS) in the `client_assertion` parameter.
  1318. The `java.security.PrivateKey` or `javax.crypto.SecretKey` used for signing the JWS
  1319. is supplied by the `com.nimbusds.jose.jwk.JWK` resolver associated with `NimbusJwtClientAuthenticationParametersConverter`.
  1320. ==== Authenticate using `private_key_jwt`
  1321. Given the following Spring Boot 2.x properties for an OAuth 2.0 Client registration:
  1322. [source,yaml]
  1323. ----
  1324. spring:
  1325. security:
  1326. oauth2:
  1327. client:
  1328. registration:
  1329. okta:
  1330. client-id: okta-client-id
  1331. client-authentication-method: private_key_jwt
  1332. authorization-grant-type: authorization_code
  1333. ...
  1334. ----
  1335. The following example shows how to configure `WebClientReactiveAuthorizationCodeTokenResponseClient`:
  1336. ====
  1337. .Java
  1338. [source,java,role="primary"]
  1339. ----
  1340. Function<ClientRegistration, JWK> jwkResolver = (clientRegistration) -> {
  1341. if (clientRegistration.getClientAuthenticationMethod().equals(ClientAuthenticationMethod.PRIVATE_KEY_JWT)) {
  1342. // Assuming RSA key type
  1343. RSAPublicKey publicKey = ...
  1344. RSAPrivateKey privateKey = ...
  1345. return new RSAKey.Builder(publicKey)
  1346. .privateKey(privateKey)
  1347. .keyID(UUID.randomUUID().toString())
  1348. .build();
  1349. }
  1350. return null;
  1351. };
  1352. WebClientReactiveAuthorizationCodeTokenResponseClient tokenResponseClient =
  1353. new WebClientReactiveAuthorizationCodeTokenResponseClient();
  1354. tokenResponseClient.addParametersConverter(
  1355. new NimbusJwtClientAuthenticationParametersConverter<>(jwkResolver));
  1356. ----
  1357. .Kotlin
  1358. [source,kotlin,role="secondary"]
  1359. ----
  1360. val jwkResolver: Function<ClientRegistration, JWK> =
  1361. Function<ClientRegistration, JWK> { clientRegistration ->
  1362. if (clientRegistration.clientAuthenticationMethod.equals(ClientAuthenticationMethod.PRIVATE_KEY_JWT)) {
  1363. // Assuming RSA key type
  1364. var publicKey: RSAPublicKey = ...
  1365. var privateKey: RSAPrivateKey = ...
  1366. RSAKey.Builder(publicKey)
  1367. .privateKey(privateKey)
  1368. .keyID(UUID.randomUUID().toString())
  1369. .build()
  1370. }
  1371. null
  1372. }
  1373. val tokenResponseClient = WebClientReactiveAuthorizationCodeTokenResponseClient()
  1374. tokenResponseClient.addParametersConverter(
  1375. NimbusJwtClientAuthenticationParametersConverter(jwkResolver)
  1376. )
  1377. ----
  1378. ====
  1379. ==== Authenticate using `client_secret_jwt`
  1380. Given the following Spring Boot 2.x properties for an OAuth 2.0 Client registration:
  1381. [source,yaml]
  1382. ----
  1383. spring:
  1384. security:
  1385. oauth2:
  1386. client:
  1387. registration:
  1388. okta:
  1389. client-id: okta-client-id
  1390. client-secret: okta-client-secret
  1391. client-authentication-method: client_secret_jwt
  1392. authorization-grant-type: client_credentials
  1393. ...
  1394. ----
  1395. The following example shows how to configure `DefaultClientCredentialsTokenResponseClient`:
  1396. ====
  1397. .Java
  1398. [source,java,role="primary"]
  1399. ----
  1400. Function<ClientRegistration, JWK> jwkResolver = (clientRegistration) -> {
  1401. if (clientRegistration.getClientAuthenticationMethod().equals(ClientAuthenticationMethod.CLIENT_SECRET_JWT)) {
  1402. SecretKeySpec secretKey = new SecretKeySpec(
  1403. clientRegistration.getClientSecret().getBytes(StandardCharsets.UTF_8),
  1404. "HmacSHA256");
  1405. return new OctetSequenceKey.Builder(secretKey)
  1406. .keyID(UUID.randomUUID().toString())
  1407. .build();
  1408. }
  1409. return null;
  1410. };
  1411. WebClientReactiveAuthorizationCodeTokenResponseClient tokenResponseClient =
  1412. new WebClientReactiveAuthorizationCodeTokenResponseClient();
  1413. tokenResponseClient.addParametersConverter(
  1414. new NimbusJwtClientAuthenticationParametersConverter<>(jwkResolver));
  1415. ----
  1416. .Kotlin
  1417. [source,kotlin,role="secondary"]
  1418. ----
  1419. val jwkResolver = Function<ClientRegistration, JWK?> { clientRegistration: ClientRegistration ->
  1420. if (clientRegistration.clientAuthenticationMethod == ClientAuthenticationMethod.CLIENT_SECRET_JWT) {
  1421. val secretKey = SecretKeySpec(
  1422. clientRegistration.clientSecret.toByteArray(StandardCharsets.UTF_8),
  1423. "HmacSHA256"
  1424. )
  1425. OctetSequenceKey.Builder(secretKey)
  1426. .keyID(UUID.randomUUID().toString())
  1427. .build()
  1428. }
  1429. null
  1430. }
  1431. val tokenResponseClient = WebClientReactiveAuthorizationCodeTokenResponseClient()
  1432. tokenResponseClient.addParametersConverter(
  1433. NimbusJwtClientAuthenticationParametersConverter(jwkResolver)
  1434. )
  1435. ----
  1436. ====
  1437. [[oauth2Client-additional-features]]
  1438. == Additional Features
  1439. [[oauth2Client-registered-authorized-client]]
  1440. === Resolving an Authorized Client
  1441. The `@RegisteredOAuth2AuthorizedClient` annotation provides the capability of resolving a method parameter to an argument value of type `OAuth2AuthorizedClient`.
  1442. This is a convenient alternative compared to accessing the `OAuth2AuthorizedClient` using the `ReactiveOAuth2AuthorizedClientManager` or `ReactiveOAuth2AuthorizedClientService`.
  1443. ====
  1444. .Java
  1445. [source,java,role="primary"]
  1446. ----
  1447. @Controller
  1448. public class OAuth2ClientController {
  1449. @GetMapping("/")
  1450. public Mono<String> index(@RegisteredOAuth2AuthorizedClient("okta") OAuth2AuthorizedClient authorizedClient) {
  1451. return Mono.just(authorizedClient.getAccessToken())
  1452. ...
  1453. .thenReturn("index");
  1454. }
  1455. }
  1456. ----
  1457. .Kotlin
  1458. [source,kotlin,role="secondary"]
  1459. ----
  1460. @Controller
  1461. class OAuth2ClientController {
  1462. @GetMapping("/")
  1463. fun index(@RegisteredOAuth2AuthorizedClient("okta") authorizedClient: OAuth2AuthorizedClient): Mono<String> {
  1464. return Mono.just(authorizedClient.accessToken)
  1465. ...
  1466. .thenReturn("index")
  1467. }
  1468. }
  1469. ----
  1470. ====
  1471. The `@RegisteredOAuth2AuthorizedClient` annotation is handled by `OAuth2AuthorizedClientArgumentResolver`, which directly uses an <<oauth2Client-authorized-manager-provider, ReactiveOAuth2AuthorizedClientManager>> and therefore inherits it's capabilities.
  1472. [[oauth2Client-webclient-webflux]]
  1473. == WebClient integration for Reactive Environments
  1474. The OAuth 2.0 Client support integrates with `WebClient` using an `ExchangeFilterFunction`.
  1475. The `ServerOAuth2AuthorizedClientExchangeFilterFunction` provides a simple mechanism for requesting protected resources by using an `OAuth2AuthorizedClient` and including the associated `OAuth2AccessToken` as a Bearer Token.
  1476. It directly uses an <<oauth2Client-authorized-manager-provider, ReactiveOAuth2AuthorizedClientManager>> and therefore inherits the following capabilities:
  1477. * An `OAuth2AccessToken` will be requested if the client has not yet been authorized.
  1478. ** `authorization_code` - triggers the Authorization Request redirect to initiate the flow
  1479. ** `client_credentials` - the access token is obtained directly from the Token Endpoint
  1480. ** `password` - the access token is obtained directly from the Token Endpoint
  1481. * If the `OAuth2AccessToken` is expired, it will be refreshed (or renewed) if a `ReactiveOAuth2AuthorizedClientProvider` is available to perform the authorization
  1482. The following code shows an example of how to configure `WebClient` with OAuth 2.0 Client support:
  1483. ====
  1484. .Java
  1485. [source,java,role="primary"]
  1486. ----
  1487. @Bean
  1488. WebClient webClient(ReactiveOAuth2AuthorizedClientManager authorizedClientManager) {
  1489. ServerOAuth2AuthorizedClientExchangeFilterFunction oauth2Client =
  1490. new ServerOAuth2AuthorizedClientExchangeFilterFunction(authorizedClientManager);
  1491. return WebClient.builder()
  1492. .filter(oauth2Client)
  1493. .build();
  1494. }
  1495. ----
  1496. .Kotlin
  1497. [source,kotlin,role="secondary"]
  1498. ----
  1499. @Bean
  1500. fun webClient(authorizedClientManager: ReactiveOAuth2AuthorizedClientManager): WebClient {
  1501. val oauth2Client = ServerOAuth2AuthorizedClientExchangeFilterFunction(authorizedClientManager)
  1502. return WebClient.builder()
  1503. .filter(oauth2Client)
  1504. .build()
  1505. }
  1506. ----
  1507. ====
  1508. === Providing the Authorized Client
  1509. The `ServerOAuth2AuthorizedClientExchangeFilterFunction` determines the client to use (for a request) by resolving the `OAuth2AuthorizedClient` from the `ClientRequest.attributes()` (request attributes).
  1510. The following code shows how to set an `OAuth2AuthorizedClient` as a request attribute:
  1511. ====
  1512. .Java
  1513. [source,java,role="primary"]
  1514. ----
  1515. @GetMapping("/")
  1516. public Mono<String> index(@RegisteredOAuth2AuthorizedClient("test-client") OAuth2AuthorizedClient authorizedClient) {
  1517. String resourceUri = ...
  1518. return webClient
  1519. .get()
  1520. .uri(resourceUri)
  1521. .attributes(oauth2AuthorizedClient(authorizedClient)) <1>
  1522. .retrieve()
  1523. .bodyToMono(String.class)
  1524. ...
  1525. .thenReturn("index");
  1526. }
  1527. ----
  1528. .Kotlin
  1529. [source,kotlin,role="secondary"]
  1530. ----
  1531. @GetMapping("/")
  1532. fun index(@RegisteredOAuth2AuthorizedClient("okta") authorizedClient: OAuth2AuthorizedClient): Mono<String> {
  1533. val resourceUri: String = ...
  1534. return webClient
  1535. .get()
  1536. .uri(resourceUri)
  1537. .attributes(oauth2AuthorizedClient(authorizedClient)) <1>
  1538. .retrieve()
  1539. .bodyToMono<String>()
  1540. ...
  1541. .thenReturn("index")
  1542. }
  1543. ----
  1544. ====
  1545. <1> `oauth2AuthorizedClient()` is a `static` method in `ServerOAuth2AuthorizedClientExchangeFilterFunction`.
  1546. The following code shows how to set the `ClientRegistration.getRegistrationId()` as a request attribute:
  1547. ====
  1548. .Java
  1549. [source,java,role="primary"]
  1550. ----
  1551. @GetMapping("/")
  1552. public Mono<String> index() {
  1553. String resourceUri = ...
  1554. return webClient
  1555. .get()
  1556. .uri(resourceUri)
  1557. .attributes(clientRegistrationId("okta")) <1>
  1558. .retrieve()
  1559. .bodyToMono(String.class)
  1560. ...
  1561. .thenReturn("index");
  1562. }
  1563. ----
  1564. .Kotlin
  1565. [source,kotlin,role="secondary"]
  1566. ----
  1567. @GetMapping("/")
  1568. fun index(): Mono<String> {
  1569. val resourceUri: String = ...
  1570. return webClient
  1571. .get()
  1572. .uri(resourceUri)
  1573. .attributes(clientRegistrationId("okta")) <1>
  1574. .retrieve()
  1575. .bodyToMono<String>()
  1576. ...
  1577. .thenReturn("index")
  1578. }
  1579. ----
  1580. ====
  1581. <1> `clientRegistrationId()` is a `static` method in `ServerOAuth2AuthorizedClientExchangeFilterFunction`.
  1582. === Defaulting the Authorized Client
  1583. 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.
  1584. If `setDefaultOAuth2AuthorizedClient(true)` is configured and the user has authenticated using `ServerHttpSecurity.oauth2Login()`, the `OAuth2AccessToken` associated with the current `OAuth2AuthenticationToken` is used.
  1585. The following code shows the specific configuration:
  1586. ====
  1587. .Java
  1588. [source,java,role="primary"]
  1589. ----
  1590. @Bean
  1591. WebClient webClient(ReactiveOAuth2AuthorizedClientManager authorizedClientManager) {
  1592. ServerOAuth2AuthorizedClientExchangeFilterFunction oauth2Client =
  1593. new ServerOAuth2AuthorizedClientExchangeFilterFunction(authorizedClientManager);
  1594. oauth2Client.setDefaultOAuth2AuthorizedClient(true);
  1595. return WebClient.builder()
  1596. .filter(oauth2Client)
  1597. .build();
  1598. }
  1599. ----
  1600. .Kotlin
  1601. [source,kotlin,role="secondary"]
  1602. ----
  1603. @Bean
  1604. fun webClient(authorizedClientManager: ReactiveOAuth2AuthorizedClientManager): WebClient {
  1605. val oauth2Client = ServerOAuth2AuthorizedClientExchangeFilterFunction(authorizedClientManager)
  1606. oauth2Client.setDefaultOAuth2AuthorizedClient(true)
  1607. return WebClient.builder()
  1608. .filter(oauth2Client)
  1609. .build()
  1610. }
  1611. ----
  1612. ====
  1613. [WARNING]
  1614. It is recommended to be cautious with this feature since all HTTP requests will receive the access token.
  1615. Alternatively, if `setDefaultClientRegistrationId("okta")` is configured with a valid `ClientRegistration`, the `OAuth2AccessToken` associated with the `OAuth2AuthorizedClient` is used.
  1616. The following code shows the specific configuration:
  1617. ====
  1618. .Java
  1619. [source,java,role="primary"]
  1620. ----
  1621. @Bean
  1622. WebClient webClient(ReactiveOAuth2AuthorizedClientManager authorizedClientManager) {
  1623. ServerOAuth2AuthorizedClientExchangeFilterFunction oauth2Client =
  1624. new ServerOAuth2AuthorizedClientExchangeFilterFunction(authorizedClientManager);
  1625. oauth2Client.setDefaultClientRegistrationId("okta");
  1626. return WebClient.builder()
  1627. .filter(oauth2Client)
  1628. .build();
  1629. }
  1630. ----
  1631. .Kotlin
  1632. [source,kotlin,role="secondary"]
  1633. ----
  1634. @Bean
  1635. fun webClient(authorizedClientManager: ReactiveOAuth2AuthorizedClientManager): WebClient {
  1636. val oauth2Client = ServerOAuth2AuthorizedClientExchangeFilterFunction(authorizedClientManager)
  1637. oauth2Client.setDefaultClientRegistrationId("okta")
  1638. return WebClient.builder()
  1639. .filter(oauth2Client)
  1640. .build()
  1641. }
  1642. ----
  1643. ====
  1644. [WARNING]
  1645. It is recommended to be cautious with this feature since all HTTP requests will receive the access token.