index.adoc 50 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616
  1. [[webflux-oauth2]]
  2. = OAuth2 WebFlux
  3. Spring Security provides comprehensive OAuth 2.0 support.
  4. This section discusses how to integrate OAuth 2.0 into your reactive application.
  5. [[oauth2-overview]]
  6. == Overview
  7. Spring Security's OAuth 2.0 support consists of two primary feature sets:
  8. * <<oauth2-resource-server>>
  9. * <<oauth2-client>>
  10. [NOTE]
  11. ====
  12. <<oauth2-client-log-users-in,OAuth2 Login>> is a very powerful OAuth2 Client feature that deserves its own section in the reference documentation.
  13. However, it does not exist as a standalone feature and requires OAuth2 Client in order to function.
  14. ====
  15. These feature sets cover the _resource server_ and _client_ roles defined in the https://tools.ietf.org/html/rfc6749#section-1.1[OAuth 2.0 Authorization Framework], while the _authorization server_ role is covered by https://docs.spring.io/spring-authorization-server/reference/index.html[Spring Authorization Server], which is a separate project built on xref:index.adoc[Spring Security].
  16. The _resource server_ and _client_ roles in OAuth2 are typically represented by one or more server-side applications.
  17. Additionally, the _authorization server_ role can be represented by one or more third parties (as is the case when centralizing identity management and/or authentication within an organization) *-or-* it can be represented by an application (as is the case with Spring Authorization Server).
  18. For example, a typical OAuth2-based microservices architecture might consist of a single user-facing client application, several backend resource servers providing REST APIs and a third party authorization server for managing users and authentication concerns.
  19. It is also common to have a single application representing only one of these roles with the need to integrate with one or more third parties that are providing the other roles.
  20. Spring Security handles these scenarios and more.
  21. The following sections cover the roles provided by Spring Security and contain examples for common scenarios.
  22. [[oauth2-resource-server]]
  23. == OAuth2 Resource Server
  24. [NOTE]
  25. ====
  26. This section contains a summary of OAuth2 Resource Server features with examples.
  27. See xref:reactive/oauth2/resource-server/index.adoc[OAuth 2.0 Resource Server] for complete reference documentation.
  28. ====
  29. To get started, add the `spring-security-oauth2-resource-server` dependency to your project.
  30. When using Spring Boot, add the following starter:
  31. .OAuth2 Client with Spring Boot
  32. [tabs]
  33. ======
  34. Gradle::
  35. +
  36. [source,gradle,role="primary"]
  37. ----
  38. implementation 'org.springframework.boot:spring-boot-starter-oauth2-resource-server'
  39. ----
  40. Maven::
  41. +
  42. [source,maven,role="secondary"]
  43. ----
  44. <dependency>
  45. <groupId>org.springframework.boot</groupId>
  46. <artifactId>spring-boot-starter-oauth2-resource-server</artifactId>
  47. </dependency>
  48. ----
  49. ======
  50. [TIP]
  51. ====
  52. See xref:getting-spring-security.adoc[] for additional options when not using Spring Boot.
  53. ====
  54. Consider the following use cases for OAuth2 Resource Server:
  55. * <<oauth2-resource-server-access-token,I want to protect access to the API using OAuth2>> (authorization server provides JWT or opaque access token)
  56. * <<oauth2-resource-server-custom-jwt,I want to protect access to the API using a JWT>> (custom token)
  57. [[oauth2-resource-server-access-token]]
  58. === Protect Access with an OAuth2 Access Token
  59. It is very common to protect access to an API using OAuth2 access tokens.
  60. In most cases, Spring Security requires only minimal configuration to secure an application with OAuth2.
  61. There are two types of `Bearer` tokens supported by Spring Security which each use a different component for validation:
  62. * <<oauth2-resource-server-access-token-jwt,JWT support>> uses a `ReactiveJwtDecoder` bean to validate signatures and decode tokens
  63. * <<oauth2-resource-server-access-token-opaque,Opaque token support>> uses a `ReactiveOpaqueTokenIntrospector` bean to introspect tokens
  64. [[oauth2-resource-server-access-token-jwt]]
  65. ==== JWT Support
  66. The following example configures a `ReactiveJwtDecoder` bean using Spring Boot configuration properties:
  67. [source,yaml]
  68. ----
  69. spring:
  70. security:
  71. oauth2:
  72. resourceserver:
  73. jwt:
  74. issuer-uri: https://my-auth-server.com
  75. ----
  76. When using Spring Boot, this is all that is required.
  77. The default arrangement provided by Spring Boot is equivalent to the following:
  78. .Configure Resource Server with JWTs
  79. [tabs]
  80. =====
  81. Java::
  82. +
  83. [source,java,role="primary"]
  84. ----
  85. @Configuration
  86. @EnableWebFluxSecurity
  87. public class SecurityConfig {
  88. @Bean
  89. public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {
  90. http
  91. .authorizeExchange((authorize) -> authorize
  92. .anyExchange().authenticated()
  93. )
  94. .oauth2ResourceServer((oauth2) -> oauth2
  95. .jwt(Customizer.withDefaults())
  96. );
  97. return http.build();
  98. }
  99. @Bean
  100. public ReactiveJwtDecoder jwtDecoder() {
  101. return ReactiveJwtDecoders.fromIssuerLocation("https://my-auth-server.com");
  102. }
  103. }
  104. ----
  105. Kotlin::
  106. +
  107. [source,kotlin,role="secondary"]
  108. ----
  109. import org.springframework.security.config.web.server.invoke
  110. @Configuration
  111. @EnableWebFluxSecurity
  112. class SecurityConfig {
  113. @Bean
  114. fun securityWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {
  115. return http {
  116. authorizeExchange {
  117. authorize(anyExchange, authenticated)
  118. }
  119. oauth2ResourceServer {
  120. jwt { }
  121. }
  122. }
  123. }
  124. @Bean
  125. fun jwtDecoder(): ReactiveJwtDecoder {
  126. return ReactiveJwtDecoders.fromIssuerLocation("https://my-auth-server.com")
  127. }
  128. }
  129. ----
  130. =====
  131. [[oauth2-resource-server-access-token-opaque]]
  132. ==== Opaque Token Support
  133. The following example configures an `OpaqueTokenIntrospector` bean using Spring Boot configuration properties:
  134. [source,yaml]
  135. ----
  136. spring:
  137. security:
  138. oauth2:
  139. resourceserver:
  140. opaquetoken:
  141. introspection-uri: https://my-auth-server.com/oauth2/introspect
  142. client-id: my-client-id
  143. client-secret: my-client-secret
  144. ----
  145. When using Spring Boot, this is all that is required.
  146. The default arrangement provided by Spring Boot is equivalent to the following:
  147. .Configure Resource Server with Opaque Tokens
  148. [tabs]
  149. =====
  150. Java::
  151. +
  152. [source,java,role="primary"]
  153. ----
  154. @Configuration
  155. @EnableWebFluxSecurity
  156. public class SecurityConfig {
  157. @Bean
  158. public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {
  159. http
  160. .authorizeExchange((authorize) -> authorize
  161. .anyExchange().authenticated()
  162. )
  163. .oauth2ResourceServer((oauth2) -> oauth2
  164. .opaqueToken(Customizer.withDefaults())
  165. );
  166. return http.build();
  167. }
  168. @Bean
  169. public ReactiveOpaqueTokenIntrospector opaqueTokenIntrospector() {
  170. return new SpringReactiveOpaqueTokenIntrospector(
  171. "https://my-auth-server.com/oauth2/introspect", "my-client-id", "my-client-secret");
  172. }
  173. }
  174. ----
  175. Kotlin::
  176. +
  177. [source,kotlin,role="secondary"]
  178. ----
  179. import org.springframework.security.config.web.server.invoke
  180. @Configuration
  181. @EnableWebFluxSecurity
  182. class SecurityConfig {
  183. @Bean
  184. fun securityWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {
  185. return http {
  186. authorizeExchange {
  187. authorize(anyExchange, authenticated)
  188. }
  189. oauth2ResourceServer {
  190. opaqueToken { }
  191. }
  192. }
  193. }
  194. @Bean
  195. fun opaqueTokenIntrospector(): ReactiveOpaqueTokenIntrospector {
  196. return SpringReactiveOpaqueTokenIntrospector(
  197. "https://my-auth-server.com/oauth2/introspect", "my-client-id", "my-client-secret"
  198. )
  199. }
  200. }
  201. ----
  202. =====
  203. [[oauth2-resource-server-custom-jwt]]
  204. === Protect Access with a custom JWT
  205. It is a fairly common goal to protect access to an API using JWTs, particularly when the frontend is developed as a single-page application.
  206. The OAuth2 Resource Server support in Spring Security can be used for any type of `Bearer` token, including a custom JWT.
  207. All that is required to protect an API using JWTs is a `ReactiveJwtDecoder` bean, which is used to validate signatures and decode tokens.
  208. Spring Security will automatically use the provided bean to configure protection within the `SecurityWebFilterChain`.
  209. The following example configures a `ReactiveJwtDecoder` bean using Spring Boot configuration properties:
  210. [source,yaml]
  211. ----
  212. spring:
  213. security:
  214. oauth2:
  215. resourceserver:
  216. jwt:
  217. public-key-location: classpath:my-public-key.pub
  218. ----
  219. [NOTE]
  220. ====
  221. You can provide the public key as a classpath resource (called `my-public-key.pub` in this example).
  222. ====
  223. When using Spring Boot, this is all that is required.
  224. The default arrangement provided by Spring Boot is equivalent to the following:
  225. .Configure Resource Server with Custom JWTs
  226. [tabs]
  227. =====
  228. Java::
  229. +
  230. [source,java,role="primary"]
  231. ----
  232. @Configuration
  233. @EnableWebFluxSecurity
  234. public class SecurityConfig {
  235. @Bean
  236. public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {
  237. http
  238. .authorizeExchange((authorize) -> authorize
  239. .anyExchange().authenticated()
  240. )
  241. .oauth2ResourceServer((oauth2) -> oauth2
  242. .jwt(Customizer.withDefaults())
  243. );
  244. return http.build();
  245. }
  246. @Bean
  247. public ReactiveJwtDecoder jwtDecoder() {
  248. return NimbusReactiveJwtDecoder.withPublicKey(publicKey()).build();
  249. }
  250. private RSAPublicKey publicKey() {
  251. // ...
  252. }
  253. }
  254. ----
  255. Kotlin::
  256. +
  257. [source,kotlin,role="secondary"]
  258. ----
  259. import org.springframework.security.config.web.server.invoke
  260. @Configuration
  261. @EnableWebFluxSecurity
  262. class SecurityConfig {
  263. @Bean
  264. fun securityWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {
  265. return http {
  266. authorizeExchange {
  267. authorize(anyExchange, authenticated)
  268. }
  269. oauth2ResourceServer {
  270. jwt { }
  271. }
  272. }
  273. }
  274. @Bean
  275. fun jwtDecoder(): ReactiveJwtDecoder {
  276. return NimbusReactiveJwtDecoder.withPublicKey(publicKey()).build()
  277. }
  278. private fun publicKey(): RSAPublicKey {
  279. // ...
  280. }
  281. }
  282. ----
  283. =====
  284. [NOTE]
  285. ====
  286. Spring Security does not provide an endpoint for minting tokens.
  287. However, Spring Security does provide the `JwtEncoder` interface along with one implementation, which is `NimbusJwtEncoder`.
  288. ====
  289. [[oauth2-client]]
  290. == OAuth2 Client
  291. [NOTE]
  292. ====
  293. This section contains a summary of OAuth2 Client features with examples.
  294. See xref:reactive/oauth2/client/index.adoc[OAuth 2.0 Client] and xref:reactive/oauth2/login/index.adoc[OAuth 2.0 Login] for complete reference documentation.
  295. ====
  296. To get started, add the `spring-security-oauth2-client` dependency to your project.
  297. When using Spring Boot, add the following starter:
  298. .OAuth2 Client with Spring Boot
  299. [tabs]
  300. ======
  301. Gradle::
  302. +
  303. [source,gradle,role="primary"]
  304. ----
  305. implementation 'org.springframework.boot:spring-boot-starter-oauth2-client'
  306. ----
  307. Maven::
  308. +
  309. [source,maven,role="secondary"]
  310. ----
  311. <dependency>
  312. <groupId>org.springframework.boot</groupId>
  313. <artifactId>spring-boot-starter-oauth2-client</artifactId>
  314. </dependency>
  315. ----
  316. ======
  317. [TIP]
  318. ====
  319. See xref:getting-spring-security.adoc[] for additional options when not using Spring Boot.
  320. ====
  321. Consider the following use cases for OAuth2 Client:
  322. * <<oauth2-client-log-users-in,I want to log users in using OAuth 2.0 or OpenID Connect 1.0>>
  323. * <<oauth2-client-access-protected-resources,I want to obtain an access token for users in order to access a third-party API>>
  324. * <<oauth2-client-access-protected-resources-current-user,I want to do both>> (log users in _and_ access a third-party API)
  325. * <<oauth2-client-enable-extension-grant-type,I want to enable an extension grant type>>
  326. * <<oauth2-client-customize-existing-grant-type,I want to customize an existing grant type>>
  327. * <<oauth2-client-customize-request-parameters,I want to customize token request parameters>>
  328. * <<oauth2-client-customize-web-client,I want to customize the `WebClient` used by OAuth2 Client components>>
  329. [[oauth2-client-log-users-in]]
  330. === Log Users In with OAuth2
  331. It is very common to require users to log in via OAuth2.
  332. https://openid.net/specs/openid-connect-core-1_0.html[OpenID Connect 1.0] provides a special token called the `id_token` which is designed to provide an OAuth2 Client with the ability to perform user identity verification and log users in.
  333. In certain cases, OAuth2 can be used directly to log users in (as is the case with popular social login providers that do not implement OpenID Connect such as GitHub and Facebook).
  334. The following example configures the application to act as an OAuth2 Client capable of logging users in with OAuth2 or OpenID Connect:
  335. .Configure OAuth2 Login
  336. [tabs]
  337. =====
  338. Java::
  339. +
  340. [source,java,role="primary"]
  341. ----
  342. @Configuration
  343. @EnableWebFluxSecurity
  344. public class SecurityConfig {
  345. @Bean
  346. public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {
  347. http
  348. // ...
  349. .oauth2Login(Customizer.withDefaults());
  350. return http.build();
  351. }
  352. }
  353. ----
  354. Kotlin::
  355. +
  356. [source,kotlin,role="secondary"]
  357. ----
  358. import org.springframework.security.config.web.server.invoke
  359. @Configuration
  360. @EnableWebFluxSecurity
  361. class SecurityConfig {
  362. @Bean
  363. fun securityWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {
  364. return http {
  365. // ...
  366. oauth2Login { }
  367. }
  368. }
  369. }
  370. ----
  371. =====
  372. In addition to the above configuration, the application requires at least one `ClientRegistration` to be configured through the use of a `ReactiveClientRegistrationRepository` bean.
  373. The following example configures an `InMemoryReactiveClientRegistrationRepository` bean using Spring Boot configuration properties:
  374. [source,yaml]
  375. ----
  376. spring:
  377. security:
  378. oauth2:
  379. client:
  380. registration:
  381. my-oidc-client:
  382. provider: my-oidc-provider
  383. client-id: my-client-id
  384. client-secret: my-client-secret
  385. authorization-grant-type: authorization_code
  386. scope: openid,profile
  387. provider:
  388. my-oidc-provider:
  389. issuer-uri: https://my-oidc-provider.com
  390. ----
  391. With the above configuration, the application now supports two additional endpoints:
  392. 1. The login endpoint (e.g. `/oauth2/authorization/my-oidc-client`) is used to initiate login and perform a redirect to the third party authorization server.
  393. 2. The redirection endpoint (e.g. `/login/oauth2/code/my-oidc-client`) is used by the authorization server to redirect back to the client application, and will contain a `code` parameter used to obtain an `id_token` and/or `access_token` via the access token request.
  394. [NOTE]
  395. ====
  396. The presence of the `openid` scope in the above configuration indicates that OpenID Connect 1.0 should be used.
  397. This instructs Spring Security to use OIDC-specific components (such as `OidcReactiveOAuth2UserService`) during request processing.
  398. Without this scope, Spring Security will use OAuth2-specific components (such as `DefaultReactiveOAuth2UserService`) instead.
  399. ====
  400. [[oauth2-client-access-protected-resources]]
  401. === Access Protected Resources
  402. Making requests to a third party API that is protected by OAuth2 is a core use case of OAuth2 Client.
  403. This is accomplished by authorizing a client (represented by the `OAuth2AuthorizedClient` class in Spring Security) and accessing protected resources by placing a `Bearer` token in the `Authorization` header of an outbound request.
  404. The following example configures the application to act as an OAuth2 Client capable of requesting protected resources from a third party API:
  405. .Configure OAuth2 Client
  406. [tabs]
  407. =====
  408. Java::
  409. +
  410. [source,java,role="primary"]
  411. ----
  412. @Configuration
  413. @EnableWebFluxSecurity
  414. public class SecurityConfig {
  415. @Bean
  416. public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {
  417. http
  418. // ...
  419. .oauth2Client(Customizer.withDefaults());
  420. return http.build();
  421. }
  422. }
  423. ----
  424. Kotlin::
  425. +
  426. [source,kotlin,role="secondary"]
  427. ----
  428. import org.springframework.security.config.web.server.invoke
  429. @Configuration
  430. @EnableWebFluxSecurity
  431. class SecurityConfig {
  432. @Bean
  433. fun securityWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {
  434. return http {
  435. // ...
  436. oauth2Client { }
  437. }
  438. }
  439. }
  440. ----
  441. =====
  442. [NOTE]
  443. ====
  444. The above example does not provide a way to log users in.
  445. You can use any other login mechanism (such as `formLogin()`).
  446. See the <<oauth2-client-access-protected-resources-current-user,next section>> for an example combining `oauth2Client()` with `oauth2Login()`.
  447. ====
  448. In addition to the above configuration, the application requires at least one `ClientRegistration` to be configured through the use of a `ReactiveClientRegistrationRepository` bean.
  449. The following example configures an `InMemoryReactiveClientRegistrationRepository` bean using Spring Boot configuration properties:
  450. [source,yaml]
  451. ----
  452. spring:
  453. security:
  454. oauth2:
  455. client:
  456. registration:
  457. my-oauth2-client:
  458. provider: my-auth-server
  459. client-id: my-client-id
  460. client-secret: my-client-secret
  461. authorization-grant-type: authorization_code
  462. scope: message.read,message.write
  463. provider:
  464. my-auth-server:
  465. issuer-uri: https://my-auth-server.com
  466. ----
  467. In addition to configuring Spring Security to support OAuth2 Client features, you will also need to decide how you will be accessing protected resources and configure your application accordingly.
  468. Spring Security provides implementations of `ReactiveOAuth2AuthorizedClientManager` for obtaining access tokens that can be used to access protected resources.
  469. [TIP]
  470. ====
  471. Spring Security registers a default `ReactiveOAuth2AuthorizedClientManager` bean for you when one does not exist.
  472. ====
  473. The easiest way to use a `ReactiveOAuth2AuthorizedClientManager` is via an `ExchangeFilterFunction` that intercepts requests through a `WebClient`.
  474. The following example uses the default `ReactiveOAuth2AuthorizedClientManager` to configure a `WebClient` capable of accessing protected resources by placing `Bearer` tokens in the `Authorization` header of each request:
  475. .Configure `WebClient` with `ExchangeFilterFunction`
  476. [tabs]
  477. =====
  478. Java::
  479. +
  480. [source,java,role="primary"]
  481. ----
  482. @Configuration
  483. public class WebClientConfig {
  484. @Bean
  485. public WebClient webClient(ReactiveOAuth2AuthorizedClientManager authorizedClientManager) {
  486. ServerOAuth2AuthorizedClientExchangeFilterFunction filter =
  487. new ServerOAuth2AuthorizedClientExchangeFilterFunction(authorizedClientManager);
  488. return WebClient.builder()
  489. .filter(filter)
  490. .build();
  491. }
  492. }
  493. ----
  494. Kotlin::
  495. +
  496. [source,kotlin,role="secondary"]
  497. ----
  498. @Configuration
  499. class WebClientConfig {
  500. @Bean
  501. fun webClient(authorizedClientManager: ReactiveOAuth2AuthorizedClientManager): WebClient {
  502. val filter = ServerOAuth2AuthorizedClientExchangeFilterFunction(authorizedClientManager)
  503. return WebClient.builder()
  504. .filter(filter)
  505. .build()
  506. }
  507. }
  508. ----
  509. =====
  510. This configured `WebClient` can be used as in the following example:
  511. [[oauth2-client-accessing-protected-resources-example]]
  512. .Use `WebClient` to Access Protected Resources
  513. [tabs]
  514. =====
  515. Java::
  516. +
  517. [source,java,role="primary"]
  518. ----
  519. import static org.springframework.security.oauth2.client.web.reactive.function.client.ServerOAuth2AuthorizedClientExchangeFilterFunction.clientRegistrationId;
  520. @RestController
  521. public class MessagesController {
  522. private final WebClient webClient;
  523. public MessagesController(WebClient webClient) {
  524. this.webClient = webClient;
  525. }
  526. @GetMapping("/messages")
  527. public Mono<ResponseEntity<List<Message>>> messages() {
  528. return this.webClient.get()
  529. .uri("http://localhost:8090/messages")
  530. .attributes(clientRegistrationId("my-oauth2-client"))
  531. .retrieve()
  532. .toEntityList(Message.class);
  533. }
  534. public record Message(String message) {
  535. }
  536. }
  537. ----
  538. Kotlin::
  539. +
  540. [source,kotlin,role="secondary"]
  541. ----
  542. import org.springframework.security.oauth2.client.web.reactive.function.client.ServerOAuth2AuthorizedClientExchangeFilterFunction.clientRegistrationId
  543. @RestController
  544. class MessagesController(private val webClient: WebClient) {
  545. @GetMapping("/messages")
  546. fun messages(): Mono<ResponseEntity<List<Message>>> {
  547. return webClient.get()
  548. .uri("http://localhost:8090/messages")
  549. .attributes(clientRegistrationId("my-oauth2-client"))
  550. .retrieve()
  551. .toEntityList<Message>()
  552. }
  553. data class Message(val message: String)
  554. }
  555. ----
  556. =====
  557. [[oauth2-client-access-protected-resources-current-user]]
  558. === Access Protected Resources for the Current User
  559. When a user is logged in via OAuth2 or OpenID Connect, the authorization server may provide an access token that can be used directly to access protected resources.
  560. This is convenient because it only requires a single `ClientRegistration` to be configured for both use cases simultaneously.
  561. [NOTE]
  562. ====
  563. This section combines <<oauth2-client-log-users-in>> and <<oauth2-client-access-protected-resources>> into a single configuration.
  564. Other advanced scenarios exist, such as configuring one `ClientRegistration` for login and another for accessing protected resources.
  565. All such scenarios would use the same basic configuration.
  566. ====
  567. The following example configures the application to act as an OAuth2 Client capable of logging the user in _and_ requesting protected resources from a third party API:
  568. .Configure OAuth2 Login and OAuth2 Client
  569. [tabs]
  570. =====
  571. Java::
  572. +
  573. [source,java,role="primary"]
  574. ----
  575. @Configuration
  576. @EnableWebFluxSecurity
  577. public class SecurityConfig {
  578. @Bean
  579. public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {
  580. http
  581. // ...
  582. .oauth2Login(Customizer.withDefaults())
  583. .oauth2Client(Customizer.withDefaults());
  584. return http.build();
  585. }
  586. }
  587. ----
  588. Kotlin::
  589. +
  590. [source,kotlin,role="secondary"]
  591. ----
  592. import org.springframework.security.config.web.server.invoke
  593. @Configuration
  594. @EnableWebFluxSecurity
  595. class SecurityConfig {
  596. @Bean
  597. fun securityWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {
  598. return http {
  599. // ...
  600. oauth2Login { }
  601. oauth2Client { }
  602. }
  603. }
  604. }
  605. ----
  606. =====
  607. In addition to the above configuration, the application requires at least one `ClientRegistration` to be configured through the use of a `ReactiveClientRegistrationRepository` bean.
  608. The following example configures an `InMemoryReactiveClientRegistrationRepository` bean using Spring Boot configuration properties:
  609. [source,yaml]
  610. ----
  611. spring:
  612. security:
  613. oauth2:
  614. client:
  615. registration:
  616. my-combined-client:
  617. provider: my-auth-server
  618. client-id: my-client-id
  619. client-secret: my-client-secret
  620. authorization-grant-type: authorization_code
  621. scope: openid,profile,message.read,message.write
  622. provider:
  623. my-auth-server:
  624. issuer-uri: https://my-auth-server.com
  625. ----
  626. [NOTE]
  627. ====
  628. The main difference between the previous examples (<<oauth2-client-log-users-in>>, <<oauth2-client-access-protected-resources>>) and this one is what is configured via the `scope` property, which combines the standard scopes `openid` and `profile` with the custom scopes `message.read` and `message.write`.
  629. ====
  630. In addition to configuring Spring Security to support OAuth2 Client features, you will also need to decide how you will be accessing protected resources and configure your application accordingly.
  631. Spring Security provides implementations of `ReactiveOAuth2AuthorizedClientManager` for obtaining access tokens that can be used to access protected resources.
  632. [TIP]
  633. ====
  634. Spring Security registers a default `ReactiveOAuth2AuthorizedClientManager` bean for you when one does not exist.
  635. ====
  636. The easiest way to use a `ReactiveOAuth2AuthorizedClientManager` is via an `ExchangeFilterFunction` that intercepts requests through a `WebClient`.
  637. The following example uses the default `ReactiveOAuth2AuthorizedClientManager` to configure a `WebClient` capable of accessing protected resources by placing `Bearer` tokens in the `Authorization` header of each request:
  638. .Configure `WebClient` with `ExchangeFilterFunction`
  639. [tabs]
  640. =====
  641. Java::
  642. +
  643. [source,java,role="primary"]
  644. ----
  645. @Configuration
  646. public class WebClientConfig {
  647. @Bean
  648. public WebClient webClient(ReactiveOAuth2AuthorizedClientManager authorizedClientManager) {
  649. ServerOAuth2AuthorizedClientExchangeFilterFunction filter =
  650. new ServerOAuth2AuthorizedClientExchangeFilterFunction(authorizedClientManager);
  651. return WebClient.builder()
  652. .filter(filter)
  653. .build();
  654. }
  655. }
  656. ----
  657. Kotlin::
  658. +
  659. [source,kotlin,role="secondary"]
  660. ----
  661. @Configuration
  662. class WebClientConfig {
  663. @Bean
  664. fun webClient(authorizedClientManager: ReactiveOAuth2AuthorizedClientManager): WebClient {
  665. val filter = ServerOAuth2AuthorizedClientExchangeFilterFunction(authorizedClientManager)
  666. return WebClient.builder()
  667. .filter(filter)
  668. .build()
  669. }
  670. }
  671. ----
  672. =====
  673. This configured `WebClient` can be used as in the following example:
  674. [[oauth2-client-accessing-protected-resources-current-user-example]]
  675. .Use `WebClient` to Access Protected Resources (Current User)
  676. [tabs]
  677. =====
  678. Java::
  679. +
  680. [source,java,role="primary"]
  681. ----
  682. @RestController
  683. public class MessagesController {
  684. private final WebClient webClient;
  685. public MessagesController(WebClient webClient) {
  686. this.webClient = webClient;
  687. }
  688. @GetMapping("/messages")
  689. public Mono<ResponseEntity<List<Message>>> messages() {
  690. return this.webClient.get()
  691. .uri("http://localhost:8090/messages")
  692. .retrieve()
  693. .toEntityList(Message.class);
  694. }
  695. public record Message(String message) {
  696. }
  697. }
  698. ----
  699. Kotlin::
  700. +
  701. [source,kotlin,role="secondary"]
  702. ----
  703. @RestController
  704. class MessagesController(private val webClient: WebClient) {
  705. @GetMapping("/messages")
  706. fun messages(): Mono<ResponseEntity<List<Message>>> {
  707. return webClient.get()
  708. .uri("http://localhost:8090/messages")
  709. .retrieve()
  710. .toEntityList<Message>()
  711. }
  712. data class Message(val message: String)
  713. }
  714. ----
  715. =====
  716. [NOTE]
  717. ====
  718. Unlike the <<oauth2-client-accessing-protected-resources-example,previous example>>, notice that we do not need to tell Spring Security about the `clientRegistrationId` we'd like to use.
  719. This is because it can be derived from the currently logged in user.
  720. ====
  721. [[oauth2-client-enable-extension-grant-type]]
  722. === Enable an Extension Grant Type
  723. A common use case involves enabling and/or configuring an extension grant type.
  724. For example, Spring Security provides support for the `jwt-bearer` and `token-exchange` grant types, but does not enable them by default because they are not part of the core OAuth 2.0 specification.
  725. With Spring Security 6.3 and later, we can simply publish a bean for one or more `ReactiveOAuth2AuthorizedClientProvider` and they will be picked up automatically.
  726. The following example simply enables the `jwt-bearer` grant type:
  727. .Enable `jwt-bearer` Grant Type
  728. [tabs]
  729. =====
  730. Java::
  731. +
  732. [source,java,role="primary"]
  733. ----
  734. @Configuration
  735. public class SecurityConfig {
  736. @Bean
  737. public ReactiveOAuth2AuthorizedClientProvider jwtBearer() {
  738. return new JwtBearerReactiveOAuth2AuthorizedClientProvider();
  739. }
  740. }
  741. ----
  742. Kotlin::
  743. +
  744. [source,kotlin,role="secondary"]
  745. ----
  746. @Configuration
  747. class SecurityConfig {
  748. @Bean
  749. fun jwtBearer(): ReactiveOAuth2AuthorizedClientProvider {
  750. return JwtBearerReactiveOAuth2AuthorizedClientProvider()
  751. }
  752. }
  753. ----
  754. =====
  755. A default `ReactiveOAuth2AuthorizedClientManager` will be published automatically by Spring Security when one is not already provided.
  756. [TIP]
  757. ====
  758. Any custom `OAuth2AuthorizedClientProvider` bean will also be picked up and applied to the provided `ReactiveOAuth2AuthorizedClientManager` after the default grant types.
  759. ====
  760. In order to achieve the above configuration prior to Spring Security 6.3, we had to publish this bean ourselves and ensure we re-enabled default grant types as well.
  761. To understand what is being configured behind the scenes, here's what the configuration might have looked like:
  762. .Enable `jwt-bearer` Grant Type (prior to 6.3)
  763. [tabs]
  764. =====
  765. Java::
  766. +
  767. [source,java,role="primary"]
  768. ----
  769. @Configuration
  770. public class SecurityConfig {
  771. @Bean
  772. public ReactiveOAuth2AuthorizedClientManager authorizedClientManager(
  773. ReactiveClientRegistrationRepository clientRegistrationRepository,
  774. ServerOAuth2AuthorizedClientRepository authorizedClientRepository) {
  775. ReactiveOAuth2AuthorizedClientProvider authorizedClientProvider =
  776. ReactiveOAuth2AuthorizedClientProviderBuilder.builder()
  777. .authorizationCode()
  778. .refreshToken()
  779. .clientCredentials()
  780. .password()
  781. .provider(new JwtBearerReactiveOAuth2AuthorizedClientProvider())
  782. .build();
  783. DefaultReactiveOAuth2AuthorizedClientManager authorizedClientManager =
  784. new DefaultReactiveOAuth2AuthorizedClientManager(
  785. clientRegistrationRepository, authorizedClientRepository);
  786. authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider);
  787. return authorizedClientManager;
  788. }
  789. }
  790. ----
  791. Kotlin::
  792. +
  793. [source,kotlin,role="secondary"]
  794. ----
  795. @Configuration
  796. class SecurityConfig {
  797. @Bean
  798. fun authorizedClientManager(
  799. clientRegistrationRepository: ReactiveClientRegistrationRepository,
  800. authorizedClientRepository: ServerOAuth2AuthorizedClientRepository
  801. ): ReactiveOAuth2AuthorizedClientManager {
  802. val authorizedClientProvider = ReactiveOAuth2AuthorizedClientProviderBuilder.builder()
  803. .authorizationCode()
  804. .refreshToken()
  805. .clientCredentials()
  806. .password()
  807. .provider(JwtBearerReactiveOAuth2AuthorizedClientProvider())
  808. .build()
  809. val authorizedClientManager = DefaultReactiveOAuth2AuthorizedClientManager(
  810. clientRegistrationRepository, authorizedClientRepository
  811. )
  812. authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider)
  813. return authorizedClientManager
  814. }
  815. }
  816. ----
  817. =====
  818. [[oauth2-client-customize-existing-grant-type]]
  819. === Customize an Existing Grant Type
  820. The ability to <<oauth2-client-enable-extension-grant-type,enable extension grant types>> by publishing a bean also provides the opportunity for customizing an existing grant type without the need to re-define the defaults.
  821. For example, if we want to customize the clock skew of the `ReactiveOAuth2AuthorizedClientProvider` for the `client_credentials` grant, we can simply publish a bean like so:
  822. .Customize Client Credentials Grant Type
  823. [tabs]
  824. =====
  825. Java::
  826. +
  827. [source,java,role="primary"]
  828. ----
  829. @Configuration
  830. public class SecurityConfig {
  831. @Bean
  832. public ReactiveOAuth2AuthorizedClientProvider clientCredentials() {
  833. ClientCredentialsReactiveOAuth2AuthorizedClientProvider authorizedClientProvider =
  834. new ClientCredentialsReactiveOAuth2AuthorizedClientProvider();
  835. authorizedClientProvider.setClockSkew(Duration.ofMinutes(5));
  836. return authorizedClientProvider;
  837. }
  838. }
  839. ----
  840. Kotlin::
  841. +
  842. [source,kotlin,role="secondary"]
  843. ----
  844. @Configuration
  845. class SecurityConfig {
  846. @Bean
  847. fun clientCredentials(): ReactiveOAuth2AuthorizedClientProvider {
  848. val authorizedClientProvider = ClientCredentialsReactiveOAuth2AuthorizedClientProvider()
  849. authorizedClientProvider.setClockSkew(Duration.ofMinutes(5))
  850. return authorizedClientProvider
  851. }
  852. }
  853. ----
  854. =====
  855. [[oauth2-client-customize-request-parameters]]
  856. === Customize Token Request Parameters
  857. The need to customize request parameters when obtaining an access token is fairly common.
  858. For example, let's say we want to add a custom `audience` parameter to the token request because the provider requires this parameter for the `authorization_code` grant.
  859. We can simply publish a bean of type `ReactiveOAuth2AccessTokenResponseClient` with the generic type `OAuth2AuthorizationCodeGrantRequest` and it will be used by Spring Security to configure OAuth2 Client components.
  860. The following example customizes token request parameters for the `authorization_code` grant:
  861. .Customize Token Request Parameters for Authorization Code Grant
  862. [tabs]
  863. =====
  864. Java::
  865. +
  866. [source,java,role="primary"]
  867. ----
  868. @Configuration
  869. public class SecurityConfig {
  870. @Bean
  871. public ReactiveOAuth2AccessTokenResponseClient<OAuth2AuthorizationCodeGrantRequest> authorizationCodeAccessTokenResponseClient() {
  872. WebClientReactiveAuthorizationCodeTokenResponseClient accessTokenResponseClient =
  873. new WebClientReactiveAuthorizationCodeTokenResponseClient();
  874. accessTokenResponseClient.addParametersConverter(parametersConverter());
  875. return accessTokenResponseClient;
  876. }
  877. private static Converter<OAuth2AuthorizationCodeGrantRequest, MultiValueMap<String, String>> parametersConverter() {
  878. return (grantRequest) -> {
  879. MultiValueMap<String, String> parameters = new LinkedMultiValueMap<>();
  880. parameters.set("audience", "xyz_value");
  881. return parameters;
  882. };
  883. }
  884. }
  885. ----
  886. Kotlin::
  887. +
  888. [source,kotlin,role="secondary"]
  889. ----
  890. @Configuration
  891. class SecurityConfig {
  892. @Bean
  893. fun authorizationCodeAccessTokenResponseClient(): ReactiveOAuth2AccessTokenResponseClient<OAuth2AuthorizationCodeGrantRequest> {
  894. val accessTokenResponseClient = WebClientReactiveAuthorizationCodeTokenResponseClient()
  895. accessTokenResponseClient.addParametersConverter(parametersConverter())
  896. return accessTokenResponseClient
  897. }
  898. private fun parametersConverter(): Converter<OAuth2AuthorizationCodeGrantRequest, MultiValueMap<String, String>> {
  899. return Converter<OAuth2AuthorizationCodeGrantRequest, MultiValueMap<String, String>> { grantRequest ->
  900. LinkedMultiValueMap<String, String>().also { parameters ->
  901. parameters["audience"] = "xyz_value"
  902. }
  903. }
  904. }
  905. }
  906. ----
  907. =====
  908. [TIP]
  909. ====
  910. Notice that we don't need to customize the `SecurityWebFilterChain` bean in this case, and can stick with the defaults.
  911. If using Spring Boot with no additional customizations, we can actually omit the `SecurityWebFilterChain` bean entirely.
  912. ====
  913. As you can see, providing the `ReactiveOAuth2AccessTokenResponseClient` as a bean is quite convenient.
  914. When using the Spring Security DSL directly, we need to ensure that this customization is applied for both OAuth2 Login (if we are using this feature) and OAuth2 Client components.
  915. To understand what is being configured behind the scenes, here's what the configuration would look like with the DSL:
  916. .Customize Token Request Parameters for Authorization Code Grant using the DSL
  917. [tabs]
  918. =====
  919. Java::
  920. +
  921. [source,java,role="primary"]
  922. ----
  923. @Configuration
  924. @EnableWebFluxSecurity
  925. public class SecurityConfig {
  926. @Bean
  927. public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {
  928. WebClientReactiveAuthorizationCodeTokenResponseClient accessTokenResponseClient =
  929. new WebClientReactiveAuthorizationCodeTokenResponseClient();
  930. accessTokenResponseClient.addParametersConverter(parametersConverter());
  931. http
  932. .authorizeExchange((authorize) -> authorize
  933. .anyExchange().authenticated()
  934. )
  935. .oauth2Login((oauth2Login) -> oauth2Login
  936. .authenticationManager(new DelegatingReactiveAuthenticationManager(
  937. new OidcAuthorizationCodeReactiveAuthenticationManager(
  938. accessTokenResponseClient, new OidcReactiveOAuth2UserService()
  939. ),
  940. new OAuth2LoginReactiveAuthenticationManager(
  941. accessTokenResponseClient, new DefaultReactiveOAuth2UserService()
  942. )
  943. ))
  944. )
  945. .oauth2Client((oauth2Client) -> oauth2Client
  946. .authenticationManager(new OAuth2AuthorizationCodeReactiveAuthenticationManager(
  947. accessTokenResponseClient
  948. ))
  949. );
  950. return http.build();
  951. }
  952. private static Converter<OAuth2AuthorizationCodeGrantRequest, MultiValueMap<String, String>> parametersConverter() {
  953. // ...
  954. }
  955. }
  956. ----
  957. Kotlin::
  958. +
  959. [source,kotlin,role="secondary"]
  960. ----
  961. import org.springframework.security.config.web.server.invoke
  962. @Configuration
  963. @EnableWebFluxSecurity
  964. class SecurityConfig {
  965. @Bean
  966. fun securityWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {
  967. val accessTokenResponseClient = WebClientReactiveAuthorizationCodeTokenResponseClient()
  968. accessTokenResponseClient.addParametersConverter(parametersConverter())
  969. return http {
  970. authorizeExchange {
  971. authorize(anyExchange, authenticated)
  972. }
  973. oauth2Login {
  974. authenticationManager = DelegatingReactiveAuthenticationManager(
  975. OidcAuthorizationCodeReactiveAuthenticationManager(
  976. accessTokenResponseClient, OidcReactiveOAuth2UserService()
  977. ),
  978. OAuth2LoginReactiveAuthenticationManager(
  979. accessTokenResponseClient, DefaultReactiveOAuth2UserService()
  980. )
  981. )
  982. }
  983. oauth2Client {
  984. authenticationManager = OAuth2AuthorizationCodeReactiveAuthenticationManager(
  985. accessTokenResponseClient
  986. )
  987. }
  988. }
  989. }
  990. private fun parametersConverter(): Converter<OAuth2AuthorizationCodeGrantRequest, MultiValueMap<String, String>> {
  991. // ...
  992. }
  993. }
  994. ----
  995. =====
  996. For other grant types we can publish additional `ReactiveOAuth2AccessTokenResponseClient` beans to override the defaults.
  997. For example, to customize token requests for the `client_credentials` grant we can publish the following bean:
  998. .Customize Token Request Parameters for Client Credentials Grant
  999. [tabs]
  1000. =====
  1001. Java::
  1002. +
  1003. [source,java,role="primary"]
  1004. ----
  1005. @Configuration
  1006. public class SecurityConfig {
  1007. @Bean
  1008. public ReactiveOAuth2AccessTokenResponseClient<OAuth2ClientCredentialsGrantRequest> clientCredentialsAccessTokenResponseClient() {
  1009. WebClientReactiveClientCredentialsTokenResponseClient accessTokenResponseClient =
  1010. new WebClientReactiveClientCredentialsTokenResponseClient();
  1011. accessTokenResponseClient.addParametersConverter(parametersConverter());
  1012. return accessTokenResponseClient;
  1013. }
  1014. private static Converter<OAuth2ClientCredentialsGrantRequest, MultiValueMap<String, String>> parametersConverter() {
  1015. // ...
  1016. }
  1017. }
  1018. ----
  1019. Kotlin::
  1020. +
  1021. [source,kotlin,role="secondary"]
  1022. ----
  1023. @Configuration
  1024. class SecurityConfig {
  1025. @Bean
  1026. fun clientCredentialsAccessTokenResponseClient(): ReactiveOAuth2AccessTokenResponseClient<OAuth2ClientCredentialsGrantRequest> {
  1027. val accessTokenResponseClient = WebClientReactiveClientCredentialsTokenResponseClient()
  1028. accessTokenResponseClient.addParametersConverter(parametersConverter())
  1029. return accessTokenResponseClient
  1030. }
  1031. private fun parametersConverter(): Converter<OAuth2ClientCredentialsGrantRequest, MultiValueMap<String, String>> {
  1032. // ...
  1033. }
  1034. }
  1035. ----
  1036. =====
  1037. Spring Security automatically resolves the following generic types of `ReactiveOAuth2AccessTokenResponseClient` beans:
  1038. * `OAuth2AuthorizationCodeGrantRequest` (see `WebClientReactiveAuthorizationCodeTokenResponseClient`)
  1039. * `OAuth2RefreshTokenGrantRequest` (see `WebClientReactiveRefreshTokenTokenResponseClient`)
  1040. * `OAuth2ClientCredentialsGrantRequest` (see `WebClientReactiveClientCredentialsTokenResponseClient`)
  1041. * `OAuth2PasswordGrantRequest` (see `WebClientReactivePasswordTokenResponseClient`)
  1042. * `JwtBearerGrantRequest` (see `WebClientReactiveJwtBearerTokenResponseClient`)
  1043. * `TokenExchangeGrantRequest` (see `WebClientReactiveTokenExchangeTokenResponseClient`)
  1044. [TIP]
  1045. ====
  1046. Publishing a bean of type `ReactiveOAuth2AccessTokenResponseClient<JwtBearerGrantRequest>` will automatically enable the `jwt-bearer` grant type without the need to <<oauth2-client-enable-extension-grant-type,configure it separately>>.
  1047. ====
  1048. [TIP]
  1049. ====
  1050. Publishing a bean of type `ReactiveOAuth2AccessTokenResponseClient<TokenExchangeGrantRequest>` will automatically enable the `token-exchange` grant type without the need to <<oauth2-client-enable-extension-grant-type,configure it separately>>.
  1051. ====
  1052. [[oauth2-client-customize-web-client]]
  1053. === Customize the `WebClient` used by OAuth2 Client Components
  1054. Another common use case is the need to customize the `WebClient` used when obtaining an access token.
  1055. We might need to do this to customize the underlying HTTP client library (via a custom `ClientHttpConnector`) to configure SSL settings or to apply proxy settings for a corporate network.
  1056. With Spring Security 6.3 and later, we can simply publish beans of type `ReactiveOAuth2AccessTokenResponseClient` and Spring Security will configure and publish a `ReactiveOAuth2AuthorizedClientManager` bean for us.
  1057. The following example customizes the `WebClient` for all of the supported grant types:
  1058. .Customize `WebClient` for OAuth2 Client
  1059. [tabs]
  1060. =====
  1061. Java::
  1062. +
  1063. [source,java,role="primary"]
  1064. ----
  1065. @Configuration
  1066. public class SecurityConfig {
  1067. @Bean
  1068. public ReactiveOAuth2AccessTokenResponseClient<OAuth2AuthorizationCodeGrantRequest> authorizationCodeAccessTokenResponseClient() {
  1069. WebClientReactiveAuthorizationCodeTokenResponseClient accessTokenResponseClient =
  1070. new WebClientReactiveAuthorizationCodeTokenResponseClient();
  1071. accessTokenResponseClient.setWebClient(webClient());
  1072. return accessTokenResponseClient;
  1073. }
  1074. @Bean
  1075. public ReactiveOAuth2AccessTokenResponseClient<OAuth2RefreshTokenGrantRequest> refreshTokenAccessTokenResponseClient() {
  1076. WebClientReactiveRefreshTokenTokenResponseClient accessTokenResponseClient =
  1077. new WebClientReactiveRefreshTokenTokenResponseClient();
  1078. accessTokenResponseClient.setWebClient(webClient());
  1079. return accessTokenResponseClient;
  1080. }
  1081. @Bean
  1082. public ReactiveOAuth2AccessTokenResponseClient<OAuth2ClientCredentialsGrantRequest> clientCredentialsAccessTokenResponseClient() {
  1083. WebClientReactiveClientCredentialsTokenResponseClient accessTokenResponseClient =
  1084. new WebClientReactiveClientCredentialsTokenResponseClient();
  1085. accessTokenResponseClient.setWebClient(webClient());
  1086. return accessTokenResponseClient;
  1087. }
  1088. @Bean
  1089. public ReactiveOAuth2AccessTokenResponseClient<OAuth2PasswordGrantRequest> passwordAccessTokenResponseClient() {
  1090. WebClientReactivePasswordTokenResponseClient accessTokenResponseClient =
  1091. new WebClientReactivePasswordTokenResponseClient();
  1092. accessTokenResponseClient.setWebClient(webClient());
  1093. return accessTokenResponseClient;
  1094. }
  1095. @Bean
  1096. public ReactiveOAuth2AccessTokenResponseClient<JwtBearerGrantRequest> jwtBearerAccessTokenResponseClient() {
  1097. WebClientReactiveJwtBearerTokenResponseClient accessTokenResponseClient =
  1098. new WebClientReactiveJwtBearerTokenResponseClient();
  1099. accessTokenResponseClient.setWebClient(webClient());
  1100. return accessTokenResponseClient;
  1101. }
  1102. @Bean
  1103. public ReactiveOAuth2AccessTokenResponseClient<TokenExchangeGrantRequest> tokenExchangeAccessTokenResponseClient() {
  1104. WebClientReactiveTokenExchangeTokenResponseClient accessTokenResponseClient =
  1105. new WebClientReactiveTokenExchangeTokenResponseClient();
  1106. accessTokenResponseClient.setWebClient(webClient());
  1107. return accessTokenResponseClient;
  1108. }
  1109. @Bean
  1110. public WebClient webClient() {
  1111. // ...
  1112. }
  1113. }
  1114. ----
  1115. Kotlin::
  1116. +
  1117. [source,kotlin,role="secondary"]
  1118. ----
  1119. @Configuration
  1120. class SecurityConfig {
  1121. @Bean
  1122. fun authorizationCodeAccessTokenResponseClient(): ReactiveOAuth2AccessTokenResponseClient<OAuth2AuthorizationCodeGrantRequest> {
  1123. val accessTokenResponseClient = WebClientReactiveAuthorizationCodeTokenResponseClient()
  1124. accessTokenResponseClient.setWebClient(webClient())
  1125. return accessTokenResponseClient
  1126. }
  1127. @Bean
  1128. fun refreshTokenAccessTokenResponseClient(): ReactiveOAuth2AccessTokenResponseClient<OAuth2RefreshTokenGrantRequest> {
  1129. val accessTokenResponseClient = WebClientReactiveRefreshTokenTokenResponseClient()
  1130. accessTokenResponseClient.setWebClient(webClient())
  1131. return accessTokenResponseClient
  1132. }
  1133. @Bean
  1134. fun clientCredentialsAccessTokenResponseClient(): ReactiveOAuth2AccessTokenResponseClient<OAuth2ClientCredentialsGrantRequest> {
  1135. val accessTokenResponseClient = WebClientReactiveClientCredentialsTokenResponseClient()
  1136. accessTokenResponseClient.setWebClient(webClient())
  1137. return accessTokenResponseClient
  1138. }
  1139. @Bean
  1140. fun passwordAccessTokenResponseClient(): ReactiveOAuth2AccessTokenResponseClient<OAuth2PasswordGrantRequest> {
  1141. val accessTokenResponseClient = WebClientReactivePasswordTokenResponseClient()
  1142. accessTokenResponseClient.setWebClient(webClient())
  1143. return accessTokenResponseClient
  1144. }
  1145. @Bean
  1146. fun jwtBearerAccessTokenResponseClient(): ReactiveOAuth2AccessTokenResponseClient<JwtBearerGrantRequest> {
  1147. val accessTokenResponseClient = WebClientReactiveJwtBearerTokenResponseClient()
  1148. accessTokenResponseClient.setWebClient(webClient())
  1149. return accessTokenResponseClient
  1150. }
  1151. @Bean
  1152. fun tokenExchangeAccessTokenResponseClient(): ReactiveOAuth2AccessTokenResponseClient<TokenExchangeGrantRequest> {
  1153. val accessTokenResponseClient = WebClientReactiveTokenExchangeTokenResponseClient()
  1154. accessTokenResponseClient.setWebClient(webClient())
  1155. return accessTokenResponseClient
  1156. }
  1157. @Bean
  1158. fun webClient(): WebClient {
  1159. // ...
  1160. }
  1161. }
  1162. ----
  1163. =====
  1164. A default `ReactiveOAuth2AuthorizedClientManager` will be published automatically by Spring Security when one is not already provided.
  1165. [TIP]
  1166. ====
  1167. Notice that we don't need to customize the `SecurityWebFilterChain` bean in this case, and can stick with the defaults.
  1168. If using Spring Boot with no additional customizations, we can actually omit the `SecurityWebFilterChain` bean entirely.
  1169. ====
  1170. Prior to Spring Security 6.3, we had to ensure this customization was applied to OAuth2 Client components ourselves.
  1171. While we could publish a bean of type `ReactiveOAuth2AccessTokenResponseClient<OAuth2AuthorizationCodeGrantRequest>` for the `authorization_code` grant, we had to publish a bean of type `ReactiveOAuth2AuthorizedClientManager` for other grant types.
  1172. To understand what is being configured behind the scenes, here's what the configuration might have looked like:
  1173. .Customize `WebClient` for OAuth2 Client (prior to 6.3)
  1174. [tabs]
  1175. =====
  1176. Java::
  1177. +
  1178. [source,java,role="primary"]
  1179. ----
  1180. @Configuration
  1181. public class SecurityConfig {
  1182. @Bean
  1183. public ReactiveOAuth2AccessTokenResponseClient<OAuth2AuthorizationCodeGrantRequest> authorizationCodeAccessTokenResponseClient() {
  1184. WebClientReactiveAuthorizationCodeTokenResponseClient accessTokenResponseClient =
  1185. new WebClientReactiveAuthorizationCodeTokenResponseClient();
  1186. accessTokenResponseClient.setWebClient(webClient());
  1187. return accessTokenResponseClient;
  1188. }
  1189. @Bean
  1190. public ReactiveOAuth2AuthorizedClientManager authorizedClientManager(
  1191. ReactiveClientRegistrationRepository clientRegistrationRepository,
  1192. ServerOAuth2AuthorizedClientRepository authorizedClientRepository) {
  1193. WebClientReactiveRefreshTokenTokenResponseClient refreshTokenAccessTokenResponseClient =
  1194. new WebClientReactiveRefreshTokenTokenResponseClient();
  1195. refreshTokenAccessTokenResponseClient.setWebClient(webClient());
  1196. WebClientReactiveClientCredentialsTokenResponseClient clientCredentialsAccessTokenResponseClient =
  1197. new WebClientReactiveClientCredentialsTokenResponseClient();
  1198. clientCredentialsAccessTokenResponseClient.setWebClient(webClient());
  1199. WebClientReactivePasswordTokenResponseClient passwordAccessTokenResponseClient =
  1200. new WebClientReactivePasswordTokenResponseClient();
  1201. passwordAccessTokenResponseClient.setWebClient(webClient());
  1202. WebClientReactiveJwtBearerTokenResponseClient jwtBearerAccessTokenResponseClient =
  1203. new WebClientReactiveJwtBearerTokenResponseClient();
  1204. jwtBearerAccessTokenResponseClient.setWebClient(webClient());
  1205. JwtBearerReactiveOAuth2AuthorizedClientProvider jwtBearerAuthorizedClientProvider =
  1206. new JwtBearerReactiveOAuth2AuthorizedClientProvider();
  1207. jwtBearerAuthorizedClientProvider.setAccessTokenResponseClient(jwtBearerAccessTokenResponseClient);
  1208. WebClientReactiveTokenExchangeTokenResponseClient tokenExchangeAccessTokenResponseClient =
  1209. new WebClientReactiveTokenExchangeTokenResponseClient();
  1210. tokenExchangeAccessTokenResponseClient.setWebClient(webClient());
  1211. TokenExchangeReactiveOAuth2AuthorizedClientProvider tokenExchangeAuthorizedClientProvider =
  1212. new TokenExchangeReactiveOAuth2AuthorizedClientProvider();
  1213. tokenExchangeAuthorizedClientProvider.setAccessTokenResponseClient(tokenExchangeAccessTokenResponseClient);
  1214. ReactiveOAuth2AuthorizedClientProvider authorizedClientProvider =
  1215. ReactiveOAuth2AuthorizedClientProviderBuilder.builder()
  1216. .authorizationCode()
  1217. .refreshToken((refreshToken) -> refreshToken
  1218. .accessTokenResponseClient(refreshTokenAccessTokenResponseClient)
  1219. )
  1220. .clientCredentials((clientCredentials) -> clientCredentials
  1221. .accessTokenResponseClient(clientCredentialsAccessTokenResponseClient)
  1222. )
  1223. .password((password) -> password
  1224. .accessTokenResponseClient(passwordAccessTokenResponseClient)
  1225. )
  1226. .provider(jwtBearerAuthorizedClientProvider)
  1227. .provider(tokenExchangeAuthorizedClientProvider)
  1228. .build();
  1229. DefaultReactiveOAuth2AuthorizedClientManager authorizedClientManager =
  1230. new DefaultReactiveOAuth2AuthorizedClientManager(
  1231. clientRegistrationRepository, authorizedClientRepository);
  1232. authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider);
  1233. return authorizedClientManager;
  1234. }
  1235. @Bean
  1236. public WebClient webClient() {
  1237. // ...
  1238. }
  1239. }
  1240. ----
  1241. Kotlin::
  1242. +
  1243. [source,kotlin,role="secondary"]
  1244. ----
  1245. import org.springframework.security.config.web.server.invoke
  1246. @Configuration
  1247. class SecurityConfig {
  1248. @Bean
  1249. fun authorizationCodeAccessTokenResponseClient(): ReactiveOAuth2AccessTokenResponseClient<OAuth2AuthorizationCodeGrantRequest> {
  1250. val accessTokenResponseClient = WebClientReactiveAuthorizationCodeTokenResponseClient()
  1251. accessTokenResponseClient.setWebClient(webClient())
  1252. return accessTokenResponseClient
  1253. }
  1254. @Bean
  1255. fun authorizedClientManager(
  1256. clientRegistrationRepository: ReactiveClientRegistrationRepository?,
  1257. authorizedClientRepository: ServerOAuth2AuthorizedClientRepository?
  1258. ): ReactiveOAuth2AuthorizedClientManager {
  1259. val refreshTokenAccessTokenResponseClient = WebClientReactiveRefreshTokenTokenResponseClient()
  1260. refreshTokenAccessTokenResponseClient.setWebClient(webClient())
  1261. val clientCredentialsAccessTokenResponseClient = WebClientReactiveClientCredentialsTokenResponseClient()
  1262. clientCredentialsAccessTokenResponseClient.setWebClient(webClient())
  1263. val passwordAccessTokenResponseClient = WebClientReactivePasswordTokenResponseClient()
  1264. passwordAccessTokenResponseClient.setWebClient(webClient())
  1265. val jwtBearerAccessTokenResponseClient = WebClientReactiveJwtBearerTokenResponseClient()
  1266. jwtBearerAccessTokenResponseClient.setWebClient(webClient())
  1267. val jwtBearerAuthorizedClientProvider = JwtBearerReactiveOAuth2AuthorizedClientProvider()
  1268. jwtBearerAuthorizedClientProvider.setAccessTokenResponseClient(jwtBearerAccessTokenResponseClient)
  1269. val tokenExchangeAccessTokenResponseClient = WebClientReactiveTokenExchangeTokenResponseClient()
  1270. tokenExchangeAccessTokenResponseClient.setWebClient(webClient())
  1271. val tokenExchangeAuthorizedClientProvider = TokenExchangeReactiveOAuth2AuthorizedClientProvider()
  1272. tokenExchangeAuthorizedClientProvider.setAccessTokenResponseClient(tokenExchangeAccessTokenResponseClient)
  1273. val authorizedClientProvider = OAuth2AuthorizedClientProviderBuilder.builder()
  1274. .authorizationCode()
  1275. .refreshToken { refreshToken ->
  1276. refreshToken.accessTokenResponseClient(refreshTokenAccessTokenResponseClient)
  1277. }
  1278. .clientCredentials { clientCredentials ->
  1279. clientCredentials.accessTokenResponseClient(clientCredentialsAccessTokenResponseClient)
  1280. }
  1281. .password { password ->
  1282. password.accessTokenResponseClient(passwordAccessTokenResponseClient)
  1283. }
  1284. .provider(jwtBearerAuthorizedClientProvider)
  1285. .provider(tokenExchangeAuthorizedClientProvider)
  1286. .build()
  1287. val authorizedClientManager = DefaultReactiveOAuth2AuthorizedClientManager(
  1288. clientRegistrationRepository, authorizedClientRepository
  1289. )
  1290. authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider)
  1291. return authorizedClientManager
  1292. }
  1293. @Bean
  1294. fun webClient(): WebClient {
  1295. // ...
  1296. }
  1297. }
  1298. ----
  1299. =====
  1300. [[further-reading]]
  1301. == Further Reading
  1302. This preceding sections introduced Spring Security's support for OAuth2 with examples for common scenarios.
  1303. You can read more about OAuth2 Client and Resource Server in the following sections of the reference documentation:
  1304. * xref:reactive/oauth2/login/index.adoc[]
  1305. * xref:reactive/oauth2/client/index.adoc[]
  1306. * xref:reactive/oauth2/resource-server/index.adoc[]