authorization-grants.adoc 52 KB

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