advanced.adoc 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704
  1. [[webflux-oauth2-login-advanced]]
  2. = Advanced Configuration
  3. The OAuth 2.0 Authorization Framework defines the https://tools.ietf.org/html/rfc6749#section-3[Protocol Endpoints] as follows:
  4. The authorization process utilizes two authorization server endpoints (HTTP resources):
  5. * Authorization Endpoint: Used by the client to obtain authorization from the resource owner via user-agent redirection.
  6. * Token Endpoint: Used by the client to exchange an authorization grant for an access token, typically with client authentication.
  7. As well as one client endpoint:
  8. * Redirection Endpoint: Used by the authorization server to return responses containing authorization credentials to the client via the resource owner user-agent.
  9. The OpenID Connect Core 1.0 specification defines the https://openid.net/specs/openid-connect-core-1_0.html#UserInfo[UserInfo Endpoint] as follows:
  10. The UserInfo Endpoint is an OAuth 2.0 Protected Resource that returns claims about the authenticated end-user.
  11. 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.
  12. These claims are normally represented by a JSON object that contains a collection of name-value pairs for the claims.
  13. `ServerHttpSecurity.oauth2Login()` provides a number of configuration options for customizing OAuth 2.0 Login.
  14. The following code shows the complete configuration options available for the `oauth2Login()` DSL:
  15. .OAuth2 Login Configuration Options
  16. [tabs]
  17. ======
  18. Java::
  19. +
  20. [source,java,role="primary"]
  21. ----
  22. @Configuration
  23. @EnableWebFluxSecurity
  24. public class OAuth2LoginSecurityConfig {
  25. @Bean
  26. SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
  27. http
  28. .oauth2Login(oauth2 -> oauth2
  29. .authenticationConverter(this.authenticationConverter())
  30. .authenticationMatcher(this.authenticationMatcher())
  31. .authenticationManager(this.authenticationManager())
  32. .authenticationSuccessHandler(this.authenticationSuccessHandler())
  33. .authenticationFailureHandler(this.authenticationFailureHandler())
  34. .clientRegistrationRepository(this.clientRegistrationRepository())
  35. .authorizedClientRepository(this.authorizedClientRepository())
  36. .authorizedClientService(this.authorizedClientService())
  37. .authorizationRequestResolver(this.authorizationRequestResolver())
  38. .authorizationRequestRepository(this.authorizationRequestRepository())
  39. .securityContextRepository(this.securityContextRepository())
  40. );
  41. return http.build();
  42. }
  43. }
  44. ----
  45. Kotlin::
  46. +
  47. [source,kotlin,role="secondary"]
  48. ----
  49. @Configuration
  50. @EnableWebFluxSecurity
  51. class OAuth2LoginSecurityConfig {
  52. @Bean
  53. fun securityWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {
  54. http {
  55. oauth2Login {
  56. authenticationConverter = authenticationConverter()
  57. authenticationMatcher = authenticationMatcher()
  58. authenticationManager = authenticationManager()
  59. authenticationSuccessHandler = authenticationSuccessHandler()
  60. authenticationFailureHandler = authenticationFailureHandler()
  61. clientRegistrationRepository = clientRegistrationRepository()
  62. authorizedClientRepository = authorizedClientRepository()
  63. authorizedClientService = authorizedClientService()
  64. authorizationRequestResolver = authorizationRequestResolver()
  65. authorizationRequestRepository = authorizationRequestRepository()
  66. securityContextRepository = securityContextRepository()
  67. }
  68. }
  69. return http.build()
  70. }
  71. }
  72. ----
  73. ======
  74. The following sections go into more detail on each of the configuration options available:
  75. * <<webflux-oauth2-login-advanced-login-page, OAuth 2.0 Login Page>>
  76. * <<webflux-oauth2-login-advanced-redirection-endpoint, Redirection Endpoint>>
  77. * <<webflux-oauth2-login-advanced-userinfo-endpoint, UserInfo Endpoint>>
  78. * <<webflux-oauth2-login-advanced-idtoken-verify, ID Token Signature Verification>>
  79. * <<webflux-oauth2-login-advanced-oidc-logout, OpenID Connect 1.0 Logout>>
  80. [[webflux-oauth2-login-advanced-login-page]]
  81. == OAuth 2.0 Login Page
  82. By default, the OAuth 2.0 Login Page is auto-generated by the `LoginPageGeneratingWebFilter`.
  83. 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).
  84. [NOTE]
  85. In order for `LoginPageGeneratingWebFilter` to show links for configured OAuth Clients, the registered `ReactiveClientRegistrationRepository` needs to also implement `Iterable<ClientRegistration>`.
  86. See `InMemoryReactiveClientRegistrationRepository` for reference.
  87. The link's destination for each OAuth Client defaults to the following:
  88. `+"/oauth2/authorization/{registrationId}"+`
  89. The following line shows an example:
  90. [source,html]
  91. ----
  92. <a href="/oauth2/authorization/google">Google</a>
  93. ----
  94. To override the default login page, configure the `exceptionHandling().authenticationEntryPoint()` and (optionally) `oauth2Login().authorizationRequestResolver()`.
  95. The following listing shows an example:
  96. .OAuth2 Login Page Configuration
  97. [tabs]
  98. ======
  99. Java::
  100. +
  101. [source,java,role="primary",subs="-attributes"]
  102. ----
  103. @Configuration
  104. @EnableWebFluxSecurity
  105. public class OAuth2LoginSecurityConfig {
  106. @Bean
  107. public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {
  108. http
  109. .exceptionHandling(exceptionHandling -> exceptionHandling
  110. .authenticationEntryPoint(new RedirectServerAuthenticationEntryPoint("/login/oauth2"))
  111. )
  112. .oauth2Login(oauth2 -> oauth2
  113. .authorizationRequestResolver(this.authorizationRequestResolver())
  114. );
  115. return http.build();
  116. }
  117. private ServerOAuth2AuthorizationRequestResolver authorizationRequestResolver() {
  118. ServerWebExchangeMatcher authorizationRequestMatcher =
  119. new PathPatternParserServerWebExchangeMatcher(
  120. "/login/oauth2/authorization/{registrationId}");
  121. return new DefaultServerOAuth2AuthorizationRequestResolver(
  122. this.clientRegistrationRepository(), authorizationRequestMatcher);
  123. }
  124. ...
  125. }
  126. ----
  127. Kotlin::
  128. +
  129. [source,kotlin,role="secondary",subs="-attributes"]
  130. ----
  131. @Configuration
  132. @EnableWebFluxSecurity
  133. class OAuth2LoginSecurityConfig {
  134. @Bean
  135. fun securityWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {
  136. http {
  137. exceptionHandling {
  138. authenticationEntryPoint = RedirectServerAuthenticationEntryPoint("/login/oauth2")
  139. }
  140. oauth2Login {
  141. authorizationRequestResolver = authorizationRequestResolver()
  142. }
  143. }
  144. return http.build()
  145. }
  146. private fun authorizationRequestResolver(): ServerOAuth2AuthorizationRequestResolver {
  147. val authorizationRequestMatcher: ServerWebExchangeMatcher = PathPatternParserServerWebExchangeMatcher(
  148. "/login/oauth2/authorization/{registrationId}"
  149. )
  150. return DefaultServerOAuth2AuthorizationRequestResolver(
  151. clientRegistrationRepository(), authorizationRequestMatcher
  152. )
  153. }
  154. ...
  155. }
  156. ----
  157. ======
  158. [IMPORTANT]
  159. You need to provide a `@Controller` with a `@RequestMapping("/login/oauth2")` that is capable of rendering the custom login page.
  160. [TIP]
  161. ====
  162. As noted earlier, configuring `oauth2Login().authorizationRequestResolver()` is optional.
  163. However, if you choose to customize it, ensure the link to each OAuth Client matches the pattern provided through the `ServerWebExchangeMatcher`.
  164. The following line shows an example:
  165. [source,html]
  166. ----
  167. <a href="/login/oauth2/authorization/google">Google</a>
  168. ----
  169. ====
  170. [[webflux-oauth2-login-advanced-redirection-endpoint]]
  171. == Redirection Endpoint
  172. The Redirection Endpoint is used by the Authorization Server for returning the Authorization Response (which contains the authorization credentials) to the client via the Resource Owner user-agent.
  173. [TIP]
  174. OAuth 2.0 Login leverages the Authorization Code Grant.
  175. Therefore, the authorization credential is the authorization code.
  176. The default Authorization Response redirection endpoint is `+/login/oauth2/code/{registrationId}+`.
  177. If you would like to customize the Authorization Response redirection endpoint, configure it as shown in the following example:
  178. .Redirection Endpoint Configuration
  179. [tabs]
  180. ======
  181. Java::
  182. +
  183. [source,java,role="primary",subs="-attributes"]
  184. ----
  185. @Configuration
  186. @EnableWebFluxSecurity
  187. public class OAuth2LoginSecurityConfig {
  188. @Bean
  189. public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {
  190. http
  191. .oauth2Login(oauth2 -> oauth2
  192. .authenticationMatcher(new PathPatternParserServerWebExchangeMatcher("/login/oauth2/callback/{registrationId}"))
  193. );
  194. return http.build();
  195. }
  196. }
  197. ----
  198. Kotlin::
  199. +
  200. [source,kotlin,role="secondary",subs="-attributes"]
  201. ----
  202. @Configuration
  203. @EnableWebFluxSecurity
  204. class OAuth2LoginSecurityConfig {
  205. @Bean
  206. fun securityWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {
  207. http {
  208. oauth2Login {
  209. authenticationMatcher = PathPatternParserServerWebExchangeMatcher("/login/oauth2/callback/{registrationId}")
  210. }
  211. }
  212. return http.build()
  213. }
  214. }
  215. ----
  216. ======
  217. [IMPORTANT]
  218. ====
  219. You also need to ensure the `ClientRegistration.redirectUri` matches the custom Authorization Response redirection endpoint.
  220. The following listing shows an example:
  221. [tabs]
  222. ======
  223. Java::
  224. +
  225. [source,java,role="primary",subs="-attributes"]
  226. ----
  227. return CommonOAuth2Provider.GOOGLE.getBuilder("google")
  228. .clientId("google-client-id")
  229. .clientSecret("google-client-secret")
  230. .redirectUri("{baseUrl}/login/oauth2/callback/{registrationId}")
  231. .build();
  232. ----
  233. Kotlin::
  234. +
  235. [source,kotlin,role="secondary",subs="-attributes"]
  236. ----
  237. return CommonOAuth2Provider.GOOGLE.getBuilder("google")
  238. .clientId("google-client-id")
  239. .clientSecret("google-client-secret")
  240. .redirectUri("{baseUrl}/login/oauth2/callback/{registrationId}")
  241. .build()
  242. ----
  243. ======
  244. ====
  245. [[webflux-oauth2-login-advanced-userinfo-endpoint]]
  246. == UserInfo Endpoint
  247. The UserInfo Endpoint includes a number of configuration options, as described in the following sub-sections:
  248. * <<webflux-oauth2-login-advanced-map-authorities, Mapping User Authorities>>
  249. * <<webflux-oauth2-login-advanced-oauth2-user-service, OAuth 2.0 UserService>>
  250. * <<webflux-oauth2-login-advanced-oidc-user-service, OpenID Connect 1.0 UserService>>
  251. [[webflux-oauth2-login-advanced-map-authorities]]
  252. === Mapping User Authorities
  253. 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_`.
  254. These granted authorities may be mapped to a new set of `GrantedAuthority` instances, which will be supplied to `OAuth2AuthenticationToken` when completing the authentication.
  255. [TIP]
  256. `OAuth2AuthenticationToken.getAuthorities()` is used for authorizing requests, such as in `hasRole('USER')` or `hasRole('ADMIN')`.
  257. There are a couple of options to choose from when mapping user authorities:
  258. * <<webflux-oauth2-login-advanced-map-authorities-grantedauthoritiesmapper, Using a GrantedAuthoritiesMapper>>
  259. * <<webflux-oauth2-login-advanced-map-authorities-reactiveoauth2userservice, Delegation-based strategy with ReactiveOAuth2UserService>>
  260. [[webflux-oauth2-login-advanced-map-authorities-grantedauthoritiesmapper]]
  261. ==== Using a GrantedAuthoritiesMapper
  262. 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`).
  263. Register a `GrantedAuthoritiesMapper` `@Bean` to have it automatically applied to the configuration, as shown in the following example:
  264. .Granted Authorities Mapper Configuration
  265. [tabs]
  266. ======
  267. Java::
  268. +
  269. [source,java,role="primary"]
  270. ----
  271. @Configuration
  272. @EnableWebFluxSecurity
  273. public class OAuth2LoginSecurityConfig {
  274. @Bean
  275. public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {
  276. http
  277. ...
  278. .oauth2Login(withDefaults());
  279. return http.build();
  280. }
  281. @Bean
  282. public GrantedAuthoritiesMapper userAuthoritiesMapper() {
  283. return (authorities) -> {
  284. Set<GrantedAuthority> mappedAuthorities = new HashSet<>();
  285. authorities.forEach(authority -> {
  286. if (OidcUserAuthority.class.isInstance(authority)) {
  287. OidcUserAuthority oidcUserAuthority = (OidcUserAuthority)authority;
  288. OidcIdToken idToken = oidcUserAuthority.getIdToken();
  289. OidcUserInfo userInfo = oidcUserAuthority.getUserInfo();
  290. // Map the claims found in idToken and/or userInfo
  291. // to one or more GrantedAuthority's and add it to mappedAuthorities
  292. } else if (OAuth2UserAuthority.class.isInstance(authority)) {
  293. OAuth2UserAuthority oauth2UserAuthority = (OAuth2UserAuthority)authority;
  294. Map<String, Object> userAttributes = oauth2UserAuthority.getAttributes();
  295. // Map the attributes found in userAttributes
  296. // to one or more GrantedAuthority's and add it to mappedAuthorities
  297. }
  298. });
  299. return mappedAuthorities;
  300. };
  301. }
  302. }
  303. ----
  304. Kotlin::
  305. +
  306. [source,kotlin,role="secondary"]
  307. ----
  308. @Configuration
  309. @EnableWebFluxSecurity
  310. class OAuth2LoginSecurityConfig {
  311. @Bean
  312. fun securityWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {
  313. http {
  314. oauth2Login { }
  315. }
  316. return http.build()
  317. }
  318. @Bean
  319. fun userAuthoritiesMapper(): GrantedAuthoritiesMapper = GrantedAuthoritiesMapper { authorities: Collection<GrantedAuthority> ->
  320. val mappedAuthorities = emptySet<GrantedAuthority>()
  321. authorities.forEach { authority ->
  322. if (authority is OidcUserAuthority) {
  323. val idToken = authority.idToken
  324. val userInfo = authority.userInfo
  325. // Map the claims found in idToken and/or userInfo
  326. // to one or more GrantedAuthority's and add it to mappedAuthorities
  327. } else if (authority is OAuth2UserAuthority) {
  328. val userAttributes = authority.attributes
  329. // Map the attributes found in userAttributes
  330. // to one or more GrantedAuthority's and add it to mappedAuthorities
  331. }
  332. }
  333. mappedAuthorities
  334. }
  335. }
  336. ----
  337. ======
  338. [[webflux-oauth2-login-advanced-map-authorities-reactiveoauth2userservice]]
  339. ==== Delegation-based strategy with ReactiveOAuth2UserService
  340. This strategy is advanced compared to using a `GrantedAuthoritiesMapper`, however, it's 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).
  341. The `OAuth2UserRequest` (and `OidcUserRequest`) provides you access to the associated `OAuth2AccessToken`, which is very useful in the cases where the _delegator_ needs to fetch authority information from a protected resource before it can map the custom authorities for the user.
  342. The following example shows how to implement and configure a delegation-based strategy using an OpenID Connect 1.0 UserService:
  343. .ReactiveOAuth2UserService Configuration
  344. [tabs]
  345. ======
  346. Java::
  347. +
  348. [source,java,role="primary"]
  349. ----
  350. @Configuration
  351. @EnableWebFluxSecurity
  352. public class OAuth2LoginSecurityConfig {
  353. @Bean
  354. public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {
  355. http
  356. ...
  357. .oauth2Login(withDefaults());
  358. return http.build();
  359. }
  360. @Bean
  361. public ReactiveOAuth2UserService<OidcUserRequest, OidcUser> oidcUserService() {
  362. final OidcReactiveOAuth2UserService delegate = new OidcReactiveOAuth2UserService();
  363. return (userRequest) -> {
  364. // Delegate to the default implementation for loading a user
  365. return delegate.loadUser(userRequest)
  366. .flatMap((oidcUser) -> {
  367. OAuth2AccessToken accessToken = userRequest.getAccessToken();
  368. Set<GrantedAuthority> mappedAuthorities = new HashSet<>();
  369. // TODO
  370. // 1) Fetch the authority information from the protected resource using accessToken
  371. // 2) Map the authority information to one or more GrantedAuthority's and add it to mappedAuthorities
  372. // 3) Create a copy of oidcUser but use the mappedAuthorities instead
  373. oidcUser = new DefaultOidcUser(mappedAuthorities, oidcUser.getIdToken(), oidcUser.getUserInfo());
  374. return Mono.just(oidcUser);
  375. });
  376. };
  377. }
  378. }
  379. ----
  380. Kotlin::
  381. +
  382. [source,kotlin,role="secondary"]
  383. ----
  384. @Configuration
  385. @EnableWebFluxSecurity
  386. class OAuth2LoginSecurityConfig {
  387. @Bean
  388. fun securityWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {
  389. http {
  390. oauth2Login { }
  391. }
  392. return http.build()
  393. }
  394. @Bean
  395. fun oidcUserService(): ReactiveOAuth2UserService<OidcUserRequest, OidcUser> {
  396. val delegate = OidcReactiveOAuth2UserService()
  397. return ReactiveOAuth2UserService { userRequest ->
  398. // Delegate to the default implementation for loading a user
  399. delegate.loadUser(userRequest)
  400. .flatMap { oidcUser ->
  401. val accessToken = userRequest.accessToken
  402. val mappedAuthorities = mutableSetOf<GrantedAuthority>()
  403. // TODO
  404. // 1) Fetch the authority information from the protected resource using accessToken
  405. // 2) Map the authority information to one or more GrantedAuthority's and add it to mappedAuthorities
  406. // 3) Create a copy of oidcUser but use the mappedAuthorities instead
  407. val mappedOidcUser = DefaultOidcUser(mappedAuthorities, oidcUser.idToken, oidcUser.userInfo)
  408. Mono.just(mappedOidcUser)
  409. }
  410. }
  411. }
  412. }
  413. ----
  414. ======
  415. [[webflux-oauth2-login-advanced-oauth2-user-service]]
  416. === OAuth 2.0 UserService
  417. `DefaultReactiveOAuth2UserService` is an implementation of a `ReactiveOAuth2UserService` that supports standard OAuth 2.0 Provider's.
  418. [NOTE]
  419. `ReactiveOAuth2UserService` 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`.
  420. `DefaultReactiveOAuth2UserService` uses a `WebClient` when requesting the user attributes at the UserInfo Endpoint.
  421. If you need to customize the pre-processing of the UserInfo Request and/or the post-handling of the UserInfo Response, you will need to provide `DefaultReactiveOAuth2UserService.setWebClient()` with a custom configured `WebClient`.
  422. Whether you customize `DefaultReactiveOAuth2UserService` or provide your own implementation of `ReactiveOAuth2UserService`, you'll need to configure it as shown in the following example:
  423. [tabs]
  424. ======
  425. Java::
  426. +
  427. [source,java,role="primary"]
  428. ----
  429. @Configuration
  430. @EnableWebFluxSecurity
  431. public class OAuth2LoginSecurityConfig {
  432. @Bean
  433. public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {
  434. http
  435. ...
  436. .oauth2Login(withDefaults());
  437. return http.build();
  438. }
  439. @Bean
  440. public ReactiveOAuth2UserService<OAuth2UserRequest, OAuth2User> oauth2UserService() {
  441. ...
  442. }
  443. }
  444. ----
  445. Kotlin::
  446. +
  447. [source,kotlin,role="secondary"]
  448. ----
  449. @Configuration
  450. @EnableWebFluxSecurity
  451. class OAuth2LoginSecurityConfig {
  452. @Bean
  453. fun securityWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {
  454. http {
  455. oauth2Login { }
  456. }
  457. return http.build()
  458. }
  459. @Bean
  460. fun oauth2UserService(): ReactiveOAuth2UserService<OAuth2UserRequest, OAuth2User> {
  461. // ...
  462. }
  463. }
  464. ----
  465. ======
  466. [[webflux-oauth2-login-advanced-oidc-user-service]]
  467. === OpenID Connect 1.0 UserService
  468. `OidcReactiveOAuth2UserService` is an implementation of a `ReactiveOAuth2UserService` that supports OpenID Connect 1.0 Provider's.
  469. The `OidcReactiveOAuth2UserService` leverages the `DefaultReactiveOAuth2UserService` when requesting the user attributes at the UserInfo Endpoint.
  470. If you need to customize the pre-processing of the UserInfo Request and/or the post-handling of the UserInfo Response, you will need to provide `OidcReactiveOAuth2UserService.setOauth2UserService()` with a custom configured `ReactiveOAuth2UserService`.
  471. Whether you customize `OidcReactiveOAuth2UserService` or provide your own implementation of `ReactiveOAuth2UserService` for OpenID Connect 1.0 Provider's, you'll need to configure it as shown in the following example:
  472. [tabs]
  473. ======
  474. Java::
  475. +
  476. [source,java,role="primary"]
  477. ----
  478. @Configuration
  479. @EnableWebFluxSecurity
  480. public class OAuth2LoginSecurityConfig {
  481. @Bean
  482. public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {
  483. http
  484. ...
  485. .oauth2Login(withDefaults());
  486. return http.build();
  487. }
  488. @Bean
  489. public ReactiveOAuth2UserService<OidcUserRequest, OidcUser> oidcUserService() {
  490. ...
  491. }
  492. }
  493. ----
  494. Kotlin::
  495. +
  496. [source,kotlin,role="secondary"]
  497. ----
  498. @Configuration
  499. @EnableWebFluxSecurity
  500. class OAuth2LoginSecurityConfig {
  501. @Bean
  502. fun securityWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {
  503. http {
  504. oauth2Login { }
  505. }
  506. return http.build()
  507. }
  508. @Bean
  509. fun oidcUserService(): ReactiveOAuth2UserService<OidcUserRequest, OidcUser> {
  510. // ...
  511. }
  512. }
  513. ----
  514. ======
  515. [[webflux-oauth2-login-advanced-idtoken-verify]]
  516. == ID Token Signature Verification
  517. 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.
  518. The ID Token is represented as a https://tools.ietf.org/html/rfc7519[JSON Web Token] (JWT) and MUST be signed using https://tools.ietf.org/html/rfc7515[JSON Web Signature] (JWS).
  519. The `ReactiveOidcIdTokenDecoderFactory` provides a `ReactiveJwtDecoder` used for `OidcIdToken` signature verification. The default algorithm is `RS256` but may be different when assigned during client registration.
  520. For these cases, a resolver may be configured to return the expected JWS algorithm assigned for a specific client.
  521. The JWS algorithm resolver is a `Function` that accepts a `ClientRegistration` and returns the expected `JwsAlgorithm` for the client, eg. `SignatureAlgorithm.RS256` or `MacAlgorithm.HS256`
  522. The following code shows how to configure the `OidcIdTokenDecoderFactory` `@Bean` to default to `MacAlgorithm.HS256` for all `ClientRegistration`:
  523. [tabs]
  524. ======
  525. Java::
  526. +
  527. [source,java,role="primary"]
  528. ----
  529. @Bean
  530. public ReactiveJwtDecoderFactory<ClientRegistration> idTokenDecoderFactory() {
  531. ReactiveOidcIdTokenDecoderFactory idTokenDecoderFactory = new ReactiveOidcIdTokenDecoderFactory();
  532. idTokenDecoderFactory.setJwsAlgorithmResolver(clientRegistration -> MacAlgorithm.HS256);
  533. return idTokenDecoderFactory;
  534. }
  535. ----
  536. Kotlin::
  537. +
  538. [source,kotlin,role="secondary"]
  539. ----
  540. @Bean
  541. fun idTokenDecoderFactory(): ReactiveJwtDecoderFactory<ClientRegistration> {
  542. val idTokenDecoderFactory = ReactiveOidcIdTokenDecoderFactory()
  543. idTokenDecoderFactory.setJwsAlgorithmResolver { MacAlgorithm.HS256 }
  544. return idTokenDecoderFactory
  545. }
  546. ----
  547. ======
  548. [NOTE]
  549. For MAC based algorithms such as `HS256`, `HS384` or `HS512`, the `client-secret` corresponding to the `client-id` is used as the symmetric key for signature verification.
  550. [TIP]
  551. 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.
  552. [[webflux-oauth2-login-advanced-oidc-logout]]
  553. Then, you can proceed to configure xref:reactive/oauth2/login/logout.adoc[logout].