advanced.adoc 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943
  1. [[oauth2login-advanced]]
  2. = Advanced Configuration
  3. `HttpSecurity.oauth2Login()` provides a number of configuration options for customizing OAuth 2.0 Login.
  4. The main configuration options are grouped into their protocol endpoint counterparts.
  5. For example, `oauth2Login().authorizationEndpoint()` allows configuring the _Authorization Endpoint_, whereas `oauth2Login().tokenEndpoint()` allows configuring the _Token Endpoint_.
  6. The following code shows an example:
  7. .Advanced OAuth2 Login Configuration
  8. [tabs]
  9. ======
  10. Java::
  11. +
  12. [source,java,role="primary"]
  13. ----
  14. @Configuration
  15. @EnableWebSecurity
  16. public class OAuth2LoginSecurityConfig {
  17. @Bean
  18. public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
  19. http
  20. .oauth2Login((oauth2) -> oauth2
  21. .authorizationEndpoint((authorization) -> authorization
  22. ...
  23. )
  24. .redirectionEndpoint((redirection) -> redirection
  25. ...
  26. )
  27. .tokenEndpoint((token) -> token
  28. ...
  29. )
  30. .userInfoEndpoint((userInfo) -> userInfo
  31. ...
  32. )
  33. );
  34. return http.build();
  35. }
  36. }
  37. ----
  38. Kotlin::
  39. +
  40. [source,kotlin,role="secondary"]
  41. ----
  42. @Configuration
  43. @EnableWebSecurity
  44. class OAuth2LoginSecurityConfig {
  45. @Bean
  46. open fun filterChain(http: HttpSecurity): SecurityFilterChain {
  47. http {
  48. oauth2Login {
  49. authorizationEndpoint {
  50. ...
  51. }
  52. redirectionEndpoint {
  53. ...
  54. }
  55. tokenEndpoint {
  56. ...
  57. }
  58. userInfoEndpoint {
  59. ...
  60. }
  61. }
  62. }
  63. return http.build()
  64. }
  65. }
  66. ----
  67. ======
  68. The main goal of the `oauth2Login()` DSL was to closely align with the naming, as defined in the specifications.
  69. The OAuth 2.0 Authorization Framework defines the https://tools.ietf.org/html/rfc6749#section-3[Protocol Endpoints] as follows:
  70. The authorization process uses two authorization server endpoints (HTTP resources):
  71. * Authorization Endpoint: Used by the client to obtain authorization from the resource owner through user-agent redirection.
  72. * Token Endpoint: Used by the client to exchange an authorization grant for an access token, typically with client authentication.
  73. The authorization process also uses one client endpoint:
  74. * Redirection Endpoint: Used by the authorization server to return responses that contain authorization credentials to the client through the resource owner user-agent.
  75. The OpenID Connect Core 1.0 specification defines the https://openid.net/specs/openid-connect-core-1_0.html#UserInfo[UserInfo Endpoint] as follows:
  76. The UserInfo Endpoint is an OAuth 2.0 Protected Resource that returns claims about the authenticated end-user.
  77. To obtain the requested claims about the end-user, the client makes a request to the UserInfo Endpoint by using an access token obtained through OpenID Connect Authentication.
  78. These claims are normally represented by a JSON object that contains a collection of name-value pairs for the claims.
  79. The following code shows the complete configuration options available for the `oauth2Login()` DSL:
  80. .OAuth2 Login Configuration Options
  81. [tabs]
  82. ======
  83. Java::
  84. +
  85. [source,java,role="primary"]
  86. ----
  87. @Configuration
  88. @EnableWebSecurity
  89. public class OAuth2LoginSecurityConfig {
  90. @Bean
  91. public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
  92. http
  93. .oauth2Login((oauth2) -> oauth2
  94. .clientRegistrationRepository(this.clientRegistrationRepository())
  95. .authorizedClientRepository(this.authorizedClientRepository())
  96. .authorizedClientService(this.authorizedClientService())
  97. .loginPage("/login")
  98. .authorizationEndpoint((authorization) -> authorization
  99. .baseUri(this.authorizationRequestBaseUri())
  100. .authorizationRequestRepository(this.authorizationRequestRepository())
  101. .authorizationRequestResolver(this.authorizationRequestResolver())
  102. )
  103. .redirectionEndpoint((redirection) -> redirection
  104. .baseUri(this.authorizationResponseBaseUri())
  105. )
  106. .tokenEndpoint((token) -> token
  107. .accessTokenResponseClient(this.accessTokenResponseClient())
  108. )
  109. .userInfoEndpoint((userInfo) -> userInfo
  110. .userAuthoritiesMapper(this.userAuthoritiesMapper())
  111. .userService(this.oauth2UserService())
  112. .oidcUserService(this.oidcUserService())
  113. )
  114. );
  115. return http.build();
  116. }
  117. }
  118. ----
  119. Kotlin::
  120. +
  121. [source,kotlin,role="secondary"]
  122. ----
  123. @Configuration
  124. @EnableWebSecurity
  125. class OAuth2LoginSecurityConfig {
  126. @Bean
  127. open fun filterChain(http: HttpSecurity): SecurityFilterChain {
  128. http {
  129. oauth2Login {
  130. clientRegistrationRepository = clientRegistrationRepository()
  131. authorizedClientRepository = authorizedClientRepository()
  132. authorizedClientService = authorizedClientService()
  133. loginPage = "/login"
  134. authorizationEndpoint {
  135. baseUri = authorizationRequestBaseUri()
  136. authorizationRequestRepository = authorizationRequestRepository()
  137. authorizationRequestResolver = authorizationRequestResolver()
  138. }
  139. redirectionEndpoint {
  140. baseUri = authorizationResponseBaseUri()
  141. }
  142. tokenEndpoint {
  143. accessTokenResponseClient = accessTokenResponseClient()
  144. }
  145. userInfoEndpoint {
  146. userAuthoritiesMapper = userAuthoritiesMapper()
  147. userService = oauth2UserService()
  148. oidcUserService = oidcUserService()
  149. }
  150. }
  151. }
  152. return http.build()
  153. }
  154. }
  155. ----
  156. ======
  157. In addition to the `oauth2Login()` DSL, XML configuration is also supported.
  158. The following code shows the complete configuration options available in the xref:servlet/appendix/namespace/http.adoc#nsa-oauth2-login[ security namespace]:
  159. .OAuth2 Login XML Configuration Options
  160. [source,xml]
  161. ----
  162. <http>
  163. <oauth2-login client-registration-repository-ref="clientRegistrationRepository"
  164. authorized-client-repository-ref="authorizedClientRepository"
  165. authorized-client-service-ref="authorizedClientService"
  166. authorization-request-repository-ref="authorizationRequestRepository"
  167. authorization-request-resolver-ref="authorizationRequestResolver"
  168. access-token-response-client-ref="accessTokenResponseClient"
  169. user-authorities-mapper-ref="userAuthoritiesMapper"
  170. user-service-ref="oauth2UserService"
  171. oidc-user-service-ref="oidcUserService"
  172. login-processing-url="/login/oauth2/code/*"
  173. login-page="/login"
  174. authentication-success-handler-ref="authenticationSuccessHandler"
  175. authentication-failure-handler-ref="authenticationFailureHandler"
  176. jwt-decoder-factory-ref="jwtDecoderFactory"/>
  177. </http>
  178. ----
  179. The following sections go into more detail on each of the configuration options available:
  180. * <<oauth2login-advanced-login-page>>
  181. * <<oauth2login-advanced-redirection-endpoint>>
  182. * <<oauth2login-advanced-userinfo-endpoint>>
  183. * <<oauth2login-advanced-idtoken-verify>>
  184. * <<oauth2login-advanced-oidc-logout>>
  185. [[oauth2login-advanced-login-page]]
  186. == OAuth 2.0 Login Page
  187. By default, the OAuth 2.0 Login Page is auto-generated by the `DefaultLoginPageGeneratingFilter`.
  188. The default login page shows each configured OAuth Client with its `ClientRegistration.clientName` as a link, which is capable of initiating the Authorization Request (or OAuth 2.0 Login).
  189. [NOTE]
  190. ====
  191. For `DefaultLoginPageGeneratingFilter` to show links for configured OAuth Clients, the registered `ClientRegistrationRepository` needs to also implement `Iterable<ClientRegistration>`.
  192. See `InMemoryClientRegistrationRepository` for reference.
  193. ====
  194. The link's destination for each OAuth Client defaults to the following:
  195. `+OAuth2AuthorizationRequestRedirectFilter.DEFAULT_AUTHORIZATION_REQUEST_BASE_URI + "/{registrationId}"+`
  196. The following line shows an example:
  197. [source,html]
  198. ----
  199. <a href="/oauth2/authorization/google">Google</a>
  200. ----
  201. To override the default login page, configure `oauth2Login().loginPage()` and (optionally) `oauth2Login().authorizationEndpoint().baseUri()`.
  202. The following listing shows an example:
  203. .OAuth2 Login Page Configuration
  204. [tabs]
  205. ======
  206. Java::
  207. +
  208. [source,java,role="primary"]
  209. ----
  210. @Configuration
  211. @EnableWebSecurity
  212. public class OAuth2LoginSecurityConfig {
  213. @Bean
  214. public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
  215. http
  216. .oauth2Login((oauth2) -> oauth2
  217. .loginPage("/login/oauth2")
  218. ...
  219. .authorizationEndpoint((authorization) -> authorization
  220. .baseUri("/login/oauth2/authorization")
  221. ...
  222. )
  223. );
  224. return http.build();
  225. }
  226. }
  227. ----
  228. Kotlin::
  229. +
  230. [source,kotlin,role="secondary"]
  231. ----
  232. @Configuration
  233. @EnableWebSecurity
  234. class OAuth2LoginSecurityConfig {
  235. @Bean
  236. open fun filterChain(http: HttpSecurity): SecurityFilterChain {
  237. http {
  238. oauth2Login {
  239. loginPage = "/login/oauth2"
  240. authorizationEndpoint {
  241. baseUri = "/login/oauth2/authorization"
  242. }
  243. }
  244. }
  245. return http.build()
  246. }
  247. }
  248. ----
  249. Xml::
  250. +
  251. [source,xml,role="secondary"]
  252. ----
  253. <http>
  254. <oauth2-login login-page="/login/oauth2"
  255. ...
  256. />
  257. </http>
  258. ----
  259. ======
  260. [IMPORTANT]
  261. ====
  262. You need to provide a `@Controller` with a `@RequestMapping("/login/oauth2")` that is capable of rendering the custom login page.
  263. ====
  264. [TIP]
  265. =====
  266. As noted earlier, configuring `oauth2Login().authorizationEndpoint().baseUri()` is optional.
  267. However, if you choose to customize it, ensure the link to each OAuth Client matches the `authorizationEndpoint().baseUri()`.
  268. The following line shows an example:
  269. [source,html]
  270. ----
  271. <a href="/login/oauth2/authorization/google">Google</a>
  272. ----
  273. =====
  274. [[oauth2login-advanced-redirection-endpoint]]
  275. == Redirection Endpoint
  276. The Redirection Endpoint is used by the Authorization Server for returning the Authorization Response (which contains the authorization credentials) to the client through the Resource Owner user-agent.
  277. [TIP]
  278. ====
  279. OAuth 2.0 Login leverages the Authorization Code Grant.
  280. Therefore, the authorization credential is the authorization code.
  281. ====
  282. The default Authorization Response `baseUri` (redirection endpoint) is `*/login/oauth2/code/**`, which is defined in `OAuth2LoginAuthenticationFilter.DEFAULT_FILTER_PROCESSES_URI`.
  283. If you would like to customize the Authorization Response `baseUri`, configure it as follows:
  284. .Redirection Endpoint Configuration
  285. [tabs]
  286. ======
  287. Java::
  288. +
  289. [source,java,role="primary"]
  290. ----
  291. @Configuration
  292. @EnableWebSecurity
  293. public class OAuth2LoginSecurityConfig {
  294. @Bean
  295. public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
  296. http
  297. .oauth2Login((oauth2) -> oauth2
  298. .redirectionEndpoint((redirection) -> redirection
  299. .baseUri("/login/oauth2/callback/*")
  300. ...
  301. )
  302. );
  303. return http.build();
  304. }
  305. }
  306. ----
  307. Kotlin::
  308. +
  309. [source,kotlin,role="secondary"]
  310. ----
  311. @Configuration
  312. @EnableWebSecurity
  313. class OAuth2LoginSecurityConfig {
  314. @Bean
  315. open fun filterChain(http: HttpSecurity): SecurityFilterChain {
  316. http {
  317. oauth2Login {
  318. redirectionEndpoint {
  319. baseUri = "/login/oauth2/callback/*"
  320. }
  321. }
  322. }
  323. return http.build()
  324. }
  325. }
  326. ----
  327. Xml::
  328. +
  329. [source,xml,role="secondary"]
  330. ----
  331. <http>
  332. <oauth2-login login-processing-url="/login/oauth2/callback/*"
  333. ...
  334. />
  335. </http>
  336. ----
  337. ======
  338. [IMPORTANT]
  339. =====
  340. You also need to ensure the `ClientRegistration.redirectUri` matches the custom Authorization Response `baseUri`.
  341. The following listing shows an example:
  342. [tabs]
  343. ======
  344. Java::
  345. +
  346. [source,java,role="primary",subs="-attributes"]
  347. ----
  348. return CommonOAuth2Provider.GOOGLE.getBuilder("google")
  349. .clientId("google-client-id")
  350. .clientSecret("google-client-secret")
  351. .redirectUri("{baseUrl}/login/oauth2/callback/{registrationId}")
  352. .build();
  353. ----
  354. Kotlin::
  355. +
  356. [source,kotlin,role="secondary",subs="-attributes"]
  357. ----
  358. return CommonOAuth2Provider.GOOGLE.getBuilder("google")
  359. .clientId("google-client-id")
  360. .clientSecret("google-client-secret")
  361. .redirectUri("{baseUrl}/login/oauth2/callback/{registrationId}")
  362. .build()
  363. ----
  364. ======
  365. =====
  366. [[oauth2login-advanced-userinfo-endpoint]]
  367. == UserInfo Endpoint
  368. The UserInfo Endpoint includes a number of configuration options, as described in the following sub-sections:
  369. * <<oauth2login-advanced-map-authorities>>
  370. * <<oauth2login-advanced-oauth2-user-service>>
  371. * <<oauth2login-advanced-oidc-user-service>>
  372. [[oauth2login-advanced-map-authorities]]
  373. === Mapping User Authorities
  374. After the user successfully authenticates with the OAuth 2.0 Provider, the `OAuth2User.getAuthorities()` (or `OidcUser.getAuthorities()`) contains a list of granted authorities populated from `OAuth2UserRequest.getAccessToken().getScopes()` and prefixed with `SCOPE_`.
  375. These granted authorities can be mapped to a new set of `GrantedAuthority` instances, which are supplied to `OAuth2AuthenticationToken` when completing the authentication.
  376. [TIP]
  377. `OAuth2AuthenticationToken.getAuthorities()` is used for authorizing requests, such as in `hasRole('USER')` or `hasRole('ADMIN')`.
  378. There are a couple of options to choose from when mapping user authorities:
  379. * <<oauth2login-advanced-map-authorities-grantedauthoritiesmapper>>
  380. * <<oauth2login-advanced-map-authorities-oauth2userservice>>
  381. [[oauth2login-advanced-map-authorities-grantedauthoritiesmapper]]
  382. ==== Using a GrantedAuthoritiesMapper
  383. The `GrantedAuthoritiesMapper` is given a list of granted authorities which contains a special authority of type `OAuth2UserAuthority` and the authority string `OAUTH2_USER` (or `OidcUserAuthority` and the authority string `OIDC_USER`).
  384. Provide an implementation of `GrantedAuthoritiesMapper` and configure it, as follows:
  385. .Granted Authorities Mapper Configuration
  386. [tabs]
  387. ======
  388. Java::
  389. +
  390. [source,java,role="primary"]
  391. ----
  392. @Configuration
  393. @EnableWebSecurity
  394. public class OAuth2LoginSecurityConfig {
  395. @Bean
  396. public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
  397. http
  398. .oauth2Login((oauth2) -> oauth2
  399. .userInfoEndpoint((userInfo) -> userInfo
  400. .userAuthoritiesMapper(this.userAuthoritiesMapper())
  401. ...
  402. )
  403. );
  404. return http.build();
  405. }
  406. private GrantedAuthoritiesMapper userAuthoritiesMapper() {
  407. return (authorities) -> {
  408. Set<GrantedAuthority> mappedAuthorities = new HashSet<>();
  409. authorities.forEach(authority -> {
  410. if (OidcUserAuthority.class.isInstance(authority)) {
  411. OidcUserAuthority oidcUserAuthority = (OidcUserAuthority)authority;
  412. OidcIdToken idToken = oidcUserAuthority.getIdToken();
  413. OidcUserInfo userInfo = oidcUserAuthority.getUserInfo();
  414. // Map the claims found in idToken and/or userInfo
  415. // to one or more GrantedAuthority's and add it to mappedAuthorities
  416. } else if (OAuth2UserAuthority.class.isInstance(authority)) {
  417. OAuth2UserAuthority oauth2UserAuthority = (OAuth2UserAuthority)authority;
  418. Map<String, Object> userAttributes = oauth2UserAuthority.getAttributes();
  419. // Map the attributes found in userAttributes
  420. // to one or more GrantedAuthority's and add it to mappedAuthorities
  421. }
  422. });
  423. return mappedAuthorities;
  424. };
  425. }
  426. }
  427. ----
  428. Kotlin::
  429. +
  430. [source,kotlin,role="secondary"]
  431. ----
  432. @Configuration
  433. @EnableWebSecurity
  434. class OAuth2LoginSecurityConfig {
  435. @Bean
  436. open fun filterChain(http: HttpSecurity): SecurityFilterChain {
  437. http {
  438. oauth2Login {
  439. userInfoEndpoint {
  440. userAuthoritiesMapper = userAuthoritiesMapper()
  441. }
  442. }
  443. }
  444. return http.build()
  445. }
  446. private fun userAuthoritiesMapper(): GrantedAuthoritiesMapper = GrantedAuthoritiesMapper { authorities: Collection<GrantedAuthority> ->
  447. val mappedAuthorities = emptySet<GrantedAuthority>()
  448. authorities.forEach { authority ->
  449. if (authority is OidcUserAuthority) {
  450. val idToken = authority.idToken
  451. val userInfo = authority.userInfo
  452. // Map the claims found in idToken and/or userInfo
  453. // to one or more GrantedAuthority's and add it to mappedAuthorities
  454. } else if (authority is OAuth2UserAuthority) {
  455. val userAttributes = authority.attributes
  456. // Map the attributes found in userAttributes
  457. // to one or more GrantedAuthority's and add it to mappedAuthorities
  458. }
  459. }
  460. mappedAuthorities
  461. }
  462. }
  463. ----
  464. Xml::
  465. +
  466. [source,xml,role="secondary"]
  467. ----
  468. <http>
  469. <oauth2-login user-authorities-mapper-ref="userAuthoritiesMapper"
  470. ...
  471. />
  472. </http>
  473. ----
  474. ======
  475. Alternatively, you can register a `GrantedAuthoritiesMapper` `@Bean` to have it automatically applied to the configuration, as follows:
  476. .Granted Authorities Mapper Bean Configuration
  477. [tabs]
  478. ======
  479. Java::
  480. +
  481. [source,java,role="primary"]
  482. ----
  483. @Configuration
  484. @EnableWebSecurity
  485. public class OAuth2LoginSecurityConfig {
  486. @Bean
  487. public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
  488. http
  489. .oauth2Login(withDefaults());
  490. return http.build();
  491. }
  492. @Bean
  493. public GrantedAuthoritiesMapper userAuthoritiesMapper() {
  494. ...
  495. }
  496. }
  497. ----
  498. Kotlin::
  499. +
  500. [source,kotlin,role="secondary"]
  501. ----
  502. @Configuration
  503. @EnableWebSecurity
  504. class OAuth2LoginSecurityConfig {
  505. @Bean
  506. open fun filterChain(http: HttpSecurity): SecurityFilterChain {
  507. http {
  508. oauth2Login { }
  509. }
  510. return http.build()
  511. }
  512. @Bean
  513. fun userAuthoritiesMapper(): GrantedAuthoritiesMapper {
  514. ...
  515. }
  516. }
  517. ----
  518. ======
  519. [[oauth2login-advanced-map-authorities-oauth2userservice]]
  520. ==== Delegation-based Strategy with OAuth2UserService
  521. This strategy is advanced compared to using a `GrantedAuthoritiesMapper`. However, it is also more flexible, as it gives you access to the `OAuth2UserRequest` and `OAuth2User` (when using an OAuth 2.0 UserService) or `OidcUserRequest` and `OidcUser` (when using an OpenID Connect 1.0 UserService).
  522. The `OAuth2UserRequest` (and `OidcUserRequest`) provides you access to the associated `OAuth2AccessToken`, which is very useful in cases where the _delegator_ needs to fetch authority information from a protected resource before it can map the custom authorities for the user.
  523. The following example shows how to implement and configure a delegation-based strategy using an OpenID Connect 1.0 UserService:
  524. .OAuth2UserService Configuration
  525. [tabs]
  526. ======
  527. Java::
  528. +
  529. [source,java,role="primary"]
  530. ----
  531. @Configuration
  532. @EnableWebSecurity
  533. public class OAuth2LoginSecurityConfig {
  534. @Bean
  535. public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
  536. http
  537. .oauth2Login((oauth2) -> oauth2
  538. .userInfoEndpoint((userInfo) -> userInfo
  539. .oidcUserService(this.oidcUserService())
  540. ...
  541. )
  542. );
  543. return http.build();
  544. }
  545. private OAuth2UserService<OidcUserRequest, OidcUser> oidcUserService() {
  546. final OidcUserService delegate = new OidcUserService();
  547. return (userRequest) -> {
  548. // Delegate to the default implementation for loading a user
  549. OidcUser oidcUser = delegate.loadUser(userRequest);
  550. OAuth2AccessToken accessToken = userRequest.getAccessToken();
  551. Set<GrantedAuthority> mappedAuthorities = new HashSet<>();
  552. // TODO
  553. // 1) Fetch the authority information from the protected resource using accessToken
  554. // 2) Map the authority information to one or more GrantedAuthority's and add it to mappedAuthorities
  555. // 3) Create a copy of oidcUser but use the mappedAuthorities instead
  556. ProviderDetails providerDetails = userRequest.getClientRegistration().getProviderDetails();
  557. String userNameAttributeName = providerDetails.getUserInfoEndpoint().getUserNameAttributeName();
  558. if (StringUtils.hasText(userNameAttributeName)) {
  559. oidcUser = new DefaultOidcUser(mappedAuthorities, oidcUser.getIdToken(), oidcUser.getUserInfo(), userNameAttributeName);
  560. } else {
  561. oidcUser = new DefaultOidcUser(mappedAuthorities, oidcUser.getIdToken(), oidcUser.getUserInfo());
  562. }
  563. return oidcUser;
  564. };
  565. }
  566. }
  567. ----
  568. Kotlin::
  569. +
  570. [source,kotlin,role="secondary"]
  571. ----
  572. @Configuration
  573. @EnableWebSecurity
  574. class OAuth2LoginSecurityConfig {
  575. @Bean
  576. open fun filterChain(http: HttpSecurity): SecurityFilterChain {
  577. http {
  578. oauth2Login {
  579. userInfoEndpoint {
  580. oidcUserService = oidcUserService()
  581. }
  582. }
  583. }
  584. return http.build()
  585. }
  586. @Bean
  587. fun oidcUserService(): OAuth2UserService<OidcUserRequest, OidcUser> {
  588. val delegate = OidcUserService()
  589. return OAuth2UserService { userRequest ->
  590. // Delegate to the default implementation for loading a user
  591. val oidcUser = delegate.loadUser(userRequest)
  592. val accessToken = userRequest.accessToken
  593. val mappedAuthorities = HashSet<GrantedAuthority>()
  594. // TODO
  595. // 1) Fetch the authority information from the protected resource using accessToken
  596. // 2) Map the authority information to one or more GrantedAuthority's and add it to mappedAuthorities
  597. // 3) Create a copy of oidcUser but use the mappedAuthorities instead
  598. val providerDetails = userRequest.getClientRegistration().getProviderDetails()
  599. val userNameAttributeName = providerDetails.getUserInfoEndpoint().getUserNameAttributeName()
  600. if (StringUtils.hasText(userNameAttributeName)) {
  601. DefaultOidcUser(mappedAuthorities, oidcUser.idToken, oidcUser.userInfo, userNameAttributeName)
  602. } else {
  603. DefaultOidcUser(mappedAuthorities, oidcUser.idToken, oidcUser.userInfo)
  604. }
  605. }
  606. }
  607. }
  608. ----
  609. Xml::
  610. +
  611. [source,xml,role="secondary"]
  612. ----
  613. <http>
  614. <oauth2-login oidc-user-service-ref="oidcUserService"
  615. ...
  616. />
  617. </http>
  618. ----
  619. ======
  620. [[oauth2login-advanced-oauth2-user-service]]
  621. === OAuth 2.0 UserService
  622. `DefaultOAuth2UserService` is an implementation of an `OAuth2UserService` that supports standard OAuth 2.0 Provider's.
  623. [NOTE]
  624. ====
  625. `OAuth2UserService` obtains the user attributes of the end-user (the resource owner) from the UserInfo Endpoint (by using the access token granted to the client during the authorization flow) and returns an `AuthenticatedPrincipal` in the form of an `OAuth2User`.
  626. ====
  627. `DefaultOAuth2UserService` uses a `RestOperations` instance when requesting the user attributes at the UserInfo Endpoint.
  628. If you need to customize the pre-processing of the UserInfo Request, you can provide `DefaultOAuth2UserService.setRequestEntityConverter()` with a custom `Converter<OAuth2UserRequest, RequestEntity<?>>`.
  629. The default implementation `OAuth2UserRequestEntityConverter` builds a `RequestEntity` representation of a UserInfo Request that sets the `OAuth2AccessToken` in the `Authorization` header by default.
  630. On the other end, if you need to customize the post-handling of the UserInfo Response, you need to provide `DefaultOAuth2UserService.setRestOperations()` with a custom configured `RestOperations`.
  631. The default `RestOperations` is configured as follows:
  632. [source,java]
  633. ----
  634. RestTemplate restTemplate = new RestTemplate();
  635. restTemplate.setErrorHandler(new OAuth2ErrorResponseErrorHandler());
  636. ----
  637. `OAuth2ErrorResponseErrorHandler` is a `ResponseErrorHandler` that can handle an OAuth 2.0 Error (400 Bad Request).
  638. It uses an `OAuth2ErrorHttpMessageConverter` for converting the OAuth 2.0 Error parameters to an `OAuth2Error`.
  639. Whether you customize `DefaultOAuth2UserService` or provide your own implementation of `OAuth2UserService`, you need to configure it as follows:
  640. [tabs]
  641. ======
  642. Java::
  643. +
  644. [source,java,role="primary"]
  645. ----
  646. @Configuration
  647. @EnableWebSecurity
  648. public class OAuth2LoginSecurityConfig {
  649. @Bean
  650. public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
  651. http
  652. .oauth2Login((oauth2) -> oauth2
  653. .userInfoEndpoint((userInfo) -> userInfo
  654. .userService(this.oauth2UserService())
  655. ...
  656. )
  657. );
  658. return http.build();
  659. }
  660. private OAuth2UserService<OAuth2UserRequest, OAuth2User> oauth2UserService() {
  661. ...
  662. }
  663. }
  664. ----
  665. Kotlin::
  666. +
  667. [source,kotlin,role="secondary"]
  668. ----
  669. @Configuration
  670. @EnableWebSecurity
  671. class OAuth2LoginSecurityConfig {
  672. @Bean
  673. open fun filterChain(http: HttpSecurity): SecurityFilterChain {
  674. http {
  675. oauth2Login {
  676. userInfoEndpoint {
  677. userService = oauth2UserService()
  678. // ...
  679. }
  680. }
  681. }
  682. return http.build()
  683. }
  684. private fun oauth2UserService(): OAuth2UserService<OAuth2UserRequest, OAuth2User> {
  685. // ...
  686. }
  687. }
  688. ----
  689. ======
  690. [[oauth2login-advanced-oidc-user-service]]
  691. === OpenID Connect 1.0 UserService
  692. `OidcUserService` is an implementation of an `OAuth2UserService` that supports OpenID Connect 1.0 Provider's.
  693. The `OidcUserService` leverages the `DefaultOAuth2UserService` when requesting the user attributes at the UserInfo Endpoint.
  694. If you need to customize the pre-processing of the UserInfo Request or the post-handling of the UserInfo Response, you need to provide `OidcUserService.setOauth2UserService()` with a custom configured `DefaultOAuth2UserService`.
  695. Whether you customize `OidcUserService` or provide your own implementation of `OAuth2UserService` for OpenID Connect 1.0 Provider's, you need to configure it as follows:
  696. [tabs]
  697. ======
  698. Java::
  699. +
  700. [source,java,role="primary"]
  701. ----
  702. @Configuration
  703. @EnableWebSecurity
  704. public class OAuth2LoginSecurityConfig {
  705. @Bean
  706. public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
  707. http
  708. .oauth2Login((oauth2) -> oauth2
  709. .userInfoEndpoint((userInfo) -> userInfo
  710. .oidcUserService(this.oidcUserService())
  711. ...
  712. )
  713. );
  714. return http.build();
  715. }
  716. private OAuth2UserService<OidcUserRequest, OidcUser> oidcUserService() {
  717. ...
  718. }
  719. }
  720. ----
  721. Kotlin::
  722. +
  723. [source,kotlin,role="secondary"]
  724. ----
  725. @Configuration
  726. @EnableWebSecurity
  727. class OAuth2LoginSecurityConfig {
  728. @Bean
  729. open fun filterChain(http: HttpSecurity): SecurityFilterChain {
  730. http {
  731. oauth2Login {
  732. userInfoEndpoint {
  733. oidcUserService = oidcUserService()
  734. // ...
  735. }
  736. }
  737. }
  738. return http.build()
  739. }
  740. private fun oidcUserService(): OAuth2UserService<OidcUserRequest, OidcUser> {
  741. // ...
  742. }
  743. }
  744. ----
  745. ======
  746. [[oauth2login-advanced-idtoken-verify]]
  747. == ID Token Signature Verification
  748. OpenID Connect 1.0 Authentication introduces the https://openid.net/specs/openid-connect-core-1_0.html#IDToken[ID Token], which is a security token that contains Claims about the Authentication of an End-User by an Authorization Server when used by a Client.
  749. The ID Token is represented as a https://tools.ietf.org/html/rfc7519[JSON Web Token] (JWT) and MUST be signed by using https://tools.ietf.org/html/rfc7515[JSON Web Signature] (JWS).
  750. The `OidcIdTokenDecoderFactory` provides a `JwtDecoder` used for `OidcIdToken` signature verification. The default algorithm is `RS256` but may be different when assigned during client registration.
  751. For these cases, you can configure a resolver to return the expected JWS algorithm assigned for a specific client.
  752. The JWS algorithm resolver is a `Function` that accepts a `ClientRegistration` and returns the expected `JwsAlgorithm` for the client, such as `SignatureAlgorithm.RS256` or `MacAlgorithm.HS256`
  753. The following code shows how to configure the `OidcIdTokenDecoderFactory` `@Bean` to default to `MacAlgorithm.HS256` for all `ClientRegistration` instances:
  754. [tabs]
  755. ======
  756. Java::
  757. +
  758. [source,java,role="primary"]
  759. ----
  760. @Bean
  761. public JwtDecoderFactory<ClientRegistration> idTokenDecoderFactory() {
  762. OidcIdTokenDecoderFactory idTokenDecoderFactory = new OidcIdTokenDecoderFactory();
  763. idTokenDecoderFactory.setJwsAlgorithmResolver((clientRegistration) -> clientRegistration.HS256);
  764. return idTokenDecoderFactory;
  765. }
  766. ----
  767. Kotlin::
  768. +
  769. [source,kotlin,role="secondary"]
  770. ----
  771. @Bean
  772. fun idTokenDecoderFactory(): JwtDecoderFactory<ClientRegistration?> {
  773. val idTokenDecoderFactory = OidcIdTokenDecoderFactory()
  774. idTokenDecoderFactory.setJwsAlgorithmResolver { MacAlgorithm.HS256 }
  775. return idTokenDecoderFactory
  776. }
  777. ----
  778. ======
  779. [NOTE]
  780. ====
  781. For MAC-based algorithms (such as `HS256`, `HS384`, or `HS512`), the `client-secret` that corresponds to the `client-id` is used as the symmetric key for signature verification.
  782. ====
  783. [TIP]
  784. ====
  785. If more than one `ClientRegistration` is configured for OpenID Connect 1.0 Authentication, the JWS algorithm resolver may evaluate the provided `ClientRegistration` to determine which algorithm to return.
  786. ====
  787. [[oauth2login-advanced-oidc-logout]]
  788. Then, you can proceed to configure xref:servlet/oauth2/login/logout.adoc[logout]