authorization-grants.adoc 52 KB

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