oauth2-client.adoc 88 KB

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