[[oauth2login-advanced]] = Advanced Configuration `HttpSecurity.oauth2Login()` provides a number of configuration options for customizing OAuth 2.0 Login. The main configuration options are grouped into their protocol endpoint counterparts. For example, `oauth2Login().authorizationEndpoint()` allows configuring the _Authorization Endpoint_, whereas `oauth2Login().tokenEndpoint()` allows configuring the _Token Endpoint_. The following code shows an example: .Advanced OAuth2 Login Configuration ==== .Java [source,java,role="primary"] ---- @EnableWebSecurity public class OAuth2LoginSecurityConfig { @Bean public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { http .oauth2Login(oauth2 -> oauth2 .authorizationEndpoint(authorization -> authorization ... ) .redirectionEndpoint(redirection -> redirection ... ) .tokenEndpoint(token -> token ... ) .userInfoEndpoint(userInfo -> userInfo ... ) ); return http.build(); } } ---- .Kotlin [source,kotlin,role="secondary"] ---- @EnableWebSecurity class OAuth2LoginSecurityConfig { @Bean open fun filterChain(http: HttpSecurity): SecurityFilterChain { http { oauth2Login { authorizationEndpoint { ... } redirectionEndpoint { ... } tokenEndpoint { ... } userInfoEndpoint { ... } } } return http.build() } } ---- ==== The main goal of the `oauth2Login()` DSL was to closely align with the naming, as defined in the specifications. The OAuth 2.0 Authorization Framework defines the https://tools.ietf.org/html/rfc6749#section-3[Protocol Endpoints] as follows: The authorization process utilizes two authorization server endpoints (HTTP resources): * Authorization Endpoint: Used by the client to obtain authorization from the resource owner via user-agent redirection. * Token Endpoint: Used by the client to exchange an authorization grant for an access token, typically with client authentication. As well as one client endpoint: * Redirection Endpoint: Used by the authorization server to return responses containing authorization credentials to the client via the resource owner user-agent. The OpenID Connect Core 1.0 specification defines the https://openid.net/specs/openid-connect-core-1_0.html#UserInfo[UserInfo Endpoint] as follows: The UserInfo Endpoint is an OAuth 2.0 Protected Resource that returns claims about the authenticated end-user. 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. These claims are normally represented by a JSON object that contains a collection of name-value pairs for the claims. The following code shows the complete configuration options available for the `oauth2Login()` DSL: .OAuth2 Login Configuration Options ==== .Java [source,java,role="primary"] ---- @EnableWebSecurity public class OAuth2LoginSecurityConfig { @Bean public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { http .oauth2Login(oauth2 -> oauth2 .clientRegistrationRepository(this.clientRegistrationRepository()) .authorizedClientRepository(this.authorizedClientRepository()) .authorizedClientService(this.authorizedClientService()) .loginPage("/login") .authorizationEndpoint(authorization -> authorization .baseUri(this.authorizationRequestBaseUri()) .authorizationRequestRepository(this.authorizationRequestRepository()) .authorizationRequestResolver(this.authorizationRequestResolver()) ) .redirectionEndpoint(redirection -> redirection .baseUri(this.authorizationResponseBaseUri()) ) .tokenEndpoint(token -> token .accessTokenResponseClient(this.accessTokenResponseClient()) ) .userInfoEndpoint(userInfo -> userInfo .userAuthoritiesMapper(this.userAuthoritiesMapper()) .userService(this.oauth2UserService()) .oidcUserService(this.oidcUserService()) ) ); return http.build(); } } ---- .Kotlin [source,kotlin,role="secondary"] ---- @EnableWebSecurity class OAuth2LoginSecurityConfig { @Bean open fun filterChain(http: HttpSecurity): SecurityFilterChain { http { oauth2Login { clientRegistrationRepository = clientRegistrationRepository() authorizedClientRepository = authorizedClientRepository() authorizedClientService = authorizedClientService() loginPage = "/login" authorizationEndpoint { baseUri = authorizationRequestBaseUri() authorizationRequestRepository = authorizationRequestRepository() authorizationRequestResolver = authorizationRequestResolver() } redirectionEndpoint { baseUri = authorizationResponseBaseUri() } tokenEndpoint { accessTokenResponseClient = accessTokenResponseClient() } userInfoEndpoint { userAuthoritiesMapper = userAuthoritiesMapper() userService = oauth2UserService() oidcUserService = oidcUserService() } } } return http.build() } } ---- ==== In addition to the `oauth2Login()` DSL, XML configuration is also supported. The following code shows the complete configuration options available in the xref:servlet/appendix/namespace/http.adoc#nsa-oauth2-login[ security namespace]: .OAuth2 Login XML Configuration Options ==== [source,xml] ---- ---- ==== The following sections go into more detail on each of the configuration options available: * <> * <> * <> * <> * <> [[oauth2login-advanced-login-page]] == OAuth 2.0 Login Page By default, the OAuth 2.0 Login Page is auto-generated by the `DefaultLoginPageGeneratingFilter`. 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). [NOTE] In order for `DefaultLoginPageGeneratingFilter` to show links for configured OAuth Clients, the registered `ClientRegistrationRepository` needs to also implement `Iterable`. See `InMemoryClientRegistrationRepository` for reference. The link's destination for each OAuth Client defaults to the following: `+OAuth2AuthorizationRequestRedirectFilter.DEFAULT_AUTHORIZATION_REQUEST_BASE_URI + "/{registrationId}"+` The following line shows an example: [source,html] ---- Google ---- To override the default login page, configure `oauth2Login().loginPage()` and (optionally) `oauth2Login().authorizationEndpoint().baseUri()`. The following listing shows an example: .OAuth2 Login Page Configuration ==== .Java [source,java,role="primary"] ---- @EnableWebSecurity public class OAuth2LoginSecurityConfig { @Bean public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { http .oauth2Login(oauth2 -> oauth2 .loginPage("/login/oauth2") ... .authorizationEndpoint(authorization -> authorization .baseUri("/login/oauth2/authorization") ... ) ); return http.build(); } } ---- .Kotlin [source,kotlin,role="secondary"] ---- @EnableWebSecurity class OAuth2LoginSecurityConfig { @Bean open fun filterChain(http: HttpSecurity): SecurityFilterChain { http { oauth2Login { loginPage = "/login/oauth2" authorizationEndpoint { baseUri = "/login/oauth2/authorization" } } } return http.build() } } ---- .Xml [source,xml,role="secondary"] ---- ---- ==== [IMPORTANT] You need to provide a `@Controller` with a `@RequestMapping("/login/oauth2")` that is capable of rendering the custom login page. [TIP] ==== As noted earlier, configuring `oauth2Login().authorizationEndpoint().baseUri()` is optional. However, if you choose to customize it, ensure the link to each OAuth Client matches the `authorizationEndpoint().baseUri()`. The following line shows an example: [source,html] ---- Google ---- ==== [[oauth2login-advanced-redirection-endpoint]] == Redirection Endpoint 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. [TIP] OAuth 2.0 Login leverages the Authorization Code Grant. Therefore, the authorization credential is the authorization code. The default Authorization Response `baseUri` (redirection endpoint) is `*/login/oauth2/code/**`, which is defined in `OAuth2LoginAuthenticationFilter.DEFAULT_FILTER_PROCESSES_URI`. If you would like to customize the Authorization Response `baseUri`, configure it as shown in the following example: .Redirection Endpoint Configuration ==== .Java [source,java,role="primary"] ---- @EnableWebSecurity public class OAuth2LoginSecurityConfig { @Bean public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { http .oauth2Login(oauth2 -> oauth2 .redirectionEndpoint(redirection -> redirection .baseUri("/login/oauth2/callback/*") ... ) ); return http.build(); } } ---- .Kotlin [source,kotlin,role="secondary"] ---- @EnableWebSecurity class OAuth2LoginSecurityConfig { @Bean open fun filterChain(http: HttpSecurity): SecurityFilterChain { http { oauth2Login { redirectionEndpoint { baseUri = "/login/oauth2/callback/*" } } } return http.build() } } ---- .Xml [source,xml,role="secondary"] ---- ---- ==== [IMPORTANT] ==== You also need to ensure the `ClientRegistration.redirectUri` matches the custom Authorization Response `baseUri`. The following listing shows an example: .Java [source,java,role="primary",attrs="-attributes"] ---- return CommonOAuth2Provider.GOOGLE.getBuilder("google") .clientId("google-client-id") .clientSecret("google-client-secret") .redirectUri("{baseUrl}/login/oauth2/callback/{registrationId}") .build(); ---- .Kotlin [source,kotlin,role="secondary",attrs="-attributes"] ---- return CommonOAuth2Provider.GOOGLE.getBuilder("google") .clientId("google-client-id") .clientSecret("google-client-secret") .redirectUri("{baseUrl}/login/oauth2/callback/{registrationId}") .build() ---- ==== [[oauth2login-advanced-userinfo-endpoint]] == UserInfo Endpoint The UserInfo Endpoint includes a number of configuration options, as described in the following sub-sections: * <> * <> * <> [[oauth2login-advanced-map-authorities]] === Mapping User Authorities After the user successfully authenticates with the OAuth 2.0 Provider, the `OAuth2User.getAuthorities()` (or `OidcUser.getAuthorities()`) may be mapped to a new set of `GrantedAuthority` instances, which will be supplied to `OAuth2AuthenticationToken` when completing the authentication. [TIP] `OAuth2AuthenticationToken.getAuthorities()` is used for authorizing requests, such as in `hasRole('USER')` or `hasRole('ADMIN')`. There are a couple of options to choose from when mapping user authorities: * <> * <> [[oauth2login-advanced-map-authorities-grantedauthoritiesmapper]] ==== Using a GrantedAuthoritiesMapper Provide an implementation of `GrantedAuthoritiesMapper` and configure it as shown in the following example: .Granted Authorities Mapper Configuration ==== .Java [source,java,role="primary"] ---- @EnableWebSecurity public class OAuth2LoginSecurityConfig { @Bean public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { http .oauth2Login(oauth2 -> oauth2 .userInfoEndpoint(userInfo -> userInfo .userAuthoritiesMapper(this.userAuthoritiesMapper()) ... ) ); return http.build(); } private GrantedAuthoritiesMapper userAuthoritiesMapper() { return (authorities) -> { Set mappedAuthorities = new HashSet<>(); authorities.forEach(authority -> { if (OidcUserAuthority.class.isInstance(authority)) { OidcUserAuthority oidcUserAuthority = (OidcUserAuthority)authority; OidcIdToken idToken = oidcUserAuthority.getIdToken(); OidcUserInfo userInfo = oidcUserAuthority.getUserInfo(); // Map the claims found in idToken and/or userInfo // to one or more GrantedAuthority's and add it to mappedAuthorities } else if (OAuth2UserAuthority.class.isInstance(authority)) { OAuth2UserAuthority oauth2UserAuthority = (OAuth2UserAuthority)authority; Map userAttributes = oauth2UserAuthority.getAttributes(); // Map the attributes found in userAttributes // to one or more GrantedAuthority's and add it to mappedAuthorities } }); return mappedAuthorities; }; } } ---- .Kotlin [source,kotlin,role="secondary"] ---- @EnableWebSecurity class OAuth2LoginSecurityConfig { @Bean open fun filterChain(http: HttpSecurity): SecurityFilterChain { http { oauth2Login { userInfoEndpoint { userAuthoritiesMapper = userAuthoritiesMapper() } } } return http.build() } private fun userAuthoritiesMapper(): GrantedAuthoritiesMapper = GrantedAuthoritiesMapper { authorities: Collection -> val mappedAuthorities = emptySet() authorities.forEach { authority -> if (authority is OidcUserAuthority) { val idToken = authority.idToken val userInfo = authority.userInfo // Map the claims found in idToken and/or userInfo // to one or more GrantedAuthority's and add it to mappedAuthorities } else if (authority is OAuth2UserAuthority) { val userAttributes = authority.attributes // Map the attributes found in userAttributes // to one or more GrantedAuthority's and add it to mappedAuthorities } } mappedAuthorities } } ---- .Xml [source,xml,role="secondary"] ---- ---- ==== Alternatively, you may register a `GrantedAuthoritiesMapper` `@Bean` to have it automatically applied to the configuration, as shown in the following example: .Granted Authorities Mapper Bean Configuration ==== .Java [source,java,role="primary"] ---- @EnableWebSecurity public class OAuth2LoginSecurityConfig { @Bean public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { http .oauth2Login(withDefaults()); return http.build(); } @Bean public GrantedAuthoritiesMapper userAuthoritiesMapper() { ... } } ---- .Kotlin [source,kotlin,role="secondary"] ---- @EnableWebSecurity class OAuth2LoginSecurityConfig { @Bean open fun filterChain(http: HttpSecurity): SecurityFilterChain { http { oauth2Login { } } return http.build() } @Bean fun userAuthoritiesMapper(): GrantedAuthoritiesMapper { ... } } ---- ==== [[oauth2login-advanced-map-authorities-oauth2userservice]] ==== Delegation-based strategy with OAuth2UserService 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). 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. The following example shows how to implement and configure a delegation-based strategy using an OpenID Connect 1.0 UserService: .OAuth2UserService Configuration ==== .Java [source,java,role="primary"] ---- @EnableWebSecurity public class OAuth2LoginSecurityConfig { @Bean public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { http .oauth2Login(oauth2 -> oauth2 .userInfoEndpoint(userInfo -> userInfo .oidcUserService(this.oidcUserService()) ... ) ); return http.build(); } private OAuth2UserService oidcUserService() { final OidcUserService delegate = new OidcUserService(); return (userRequest) -> { // Delegate to the default implementation for loading a user OidcUser oidcUser = delegate.loadUser(userRequest); OAuth2AccessToken accessToken = userRequest.getAccessToken(); Set mappedAuthorities = new HashSet<>(); // TODO // 1) Fetch the authority information from the protected resource using accessToken // 2) Map the authority information to one or more GrantedAuthority's and add it to mappedAuthorities // 3) Create a copy of oidcUser but use the mappedAuthorities instead oidcUser = new DefaultOidcUser(mappedAuthorities, oidcUser.getIdToken(), oidcUser.getUserInfo()); return oidcUser; }; } } ---- .Kotlin [source,kotlin,role="secondary"] ---- @EnableWebSecurity class OAuth2LoginSecurityConfig { @Bean open fun filterChain(http: HttpSecurity): SecurityFilterChain { http { oauth2Login { userInfoEndpoint { oidcUserService = oidcUserService() } } } return http.build() } @Bean fun oidcUserService(): OAuth2UserService { val delegate = OidcUserService() return OAuth2UserService { userRequest -> // Delegate to the default implementation for loading a user var oidcUser = delegate.loadUser(userRequest) val accessToken = userRequest.accessToken val mappedAuthorities = HashSet() // TODO // 1) Fetch the authority information from the protected resource using accessToken // 2) Map the authority information to one or more GrantedAuthority's and add it to mappedAuthorities // 3) Create a copy of oidcUser but use the mappedAuthorities instead oidcUser = DefaultOidcUser(mappedAuthorities, oidcUser.idToken, oidcUser.userInfo) oidcUser } } } ---- .Xml [source,xml,role="secondary"] ---- ---- ==== [[oauth2login-advanced-oauth2-user-service]] === OAuth 2.0 UserService `DefaultOAuth2UserService` is an implementation of an `OAuth2UserService` that supports standard OAuth 2.0 Provider's. [NOTE] `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`. `DefaultOAuth2UserService` uses a `RestOperations` when requesting the user attributes at the UserInfo Endpoint. If you need to customize the pre-processing of the UserInfo Request, you can provide `DefaultOAuth2UserService.setRequestEntityConverter()` with a custom `Converter>`. The default implementation `OAuth2UserRequestEntityConverter` builds a `RequestEntity` representation of a UserInfo Request that sets the `OAuth2AccessToken` in the `Authorization` header by default. On the other end, if you need to customize the post-handling of the UserInfo Response, you will need to provide `DefaultOAuth2UserService.setRestOperations()` with a custom configured `RestOperations`. The default `RestOperations` is configured as follows: [source,java] ---- RestTemplate restTemplate = new RestTemplate(); restTemplate.setErrorHandler(new OAuth2ErrorResponseErrorHandler()); ---- `OAuth2ErrorResponseErrorHandler` is a `ResponseErrorHandler` that can handle an OAuth 2.0 Error (400 Bad Request). It uses an `OAuth2ErrorHttpMessageConverter` for converting the OAuth 2.0 Error parameters to an `OAuth2Error`. Whether you customize `DefaultOAuth2UserService` or provide your own implementation of `OAuth2UserService`, you'll need to configure it as shown in the following example: ==== .Java [source,java,role="primary"] ---- @EnableWebSecurity public class OAuth2LoginSecurityConfig { @Bean public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { http .oauth2Login(oauth2 -> oauth2 .userInfoEndpoint(userInfo -> userInfo .userService(this.oauth2UserService()) ... ) ); return http.build(); } private OAuth2UserService oauth2UserService() { ... } } ---- .Kotlin [source,kotlin,role="secondary"] ---- @EnableWebSecurity class OAuth2LoginSecurityConfig { @Bean open fun filterChain(http: HttpSecurity): SecurityFilterChain { http { oauth2Login { userInfoEndpoint { userService = oauth2UserService() // ... } } } return http.build() } private fun oauth2UserService(): OAuth2UserService { // ... } } ---- ==== [[oauth2login-advanced-oidc-user-service]] === OpenID Connect 1.0 UserService `OidcUserService` is an implementation of an `OAuth2UserService` that supports OpenID Connect 1.0 Provider's. The `OidcUserService` leverages the `DefaultOAuth2UserService` when requesting the user attributes at the UserInfo Endpoint. 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 `OidcUserService.setOauth2UserService()` with a custom configured `DefaultOAuth2UserService`. Whether you customize `OidcUserService` or provide your own implementation of `OAuth2UserService` for OpenID Connect 1.0 Provider's, you'll need to configure it as shown in the following example: ==== .Java [source,java,role="primary"] ---- @EnableWebSecurity public class OAuth2LoginSecurityConfig { @Bean public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { http .oauth2Login(oauth2 -> oauth2 .userInfoEndpoint(userInfo -> userInfo .oidcUserService(this.oidcUserService()) ... ) ); return http.build(); } private OAuth2UserService oidcUserService() { ... } } ---- .Kotlin [source,kotlin,role="secondary"] ---- @EnableWebSecurity class OAuth2LoginSecurityConfig { @Bean open fun filterChain(http: HttpSecurity): SecurityFilterChain { http { oauth2Login { userInfoEndpoint { oidcUserService = oidcUserService() // ... } } } return http.build() } private fun oidcUserService(): OAuth2UserService { // ... } } ---- ==== [[oauth2login-advanced-idtoken-verify]] == ID Token Signature Verification 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. 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). The `OidcIdTokenDecoderFactory` provides a `JwtDecoder` used for `OidcIdToken` signature verification. The default algorithm is `RS256` but may be different when assigned during client registration. For these cases, a resolver may be configured to return the expected JWS algorithm assigned for a specific client. 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` The following code shows how to configure the `OidcIdTokenDecoderFactory` `@Bean` to default to `MacAlgorithm.HS256` for all `ClientRegistration`: ==== .Java [source,java,role="primary"] ---- @Bean public JwtDecoderFactory idTokenDecoderFactory() { OidcIdTokenDecoderFactory idTokenDecoderFactory = new OidcIdTokenDecoderFactory(); idTokenDecoderFactory.setJwsAlgorithmResolver(clientRegistration -> MacAlgorithm.HS256); return idTokenDecoderFactory; } ---- .Kotlin [source,kotlin,role="secondary"] ---- @Bean fun idTokenDecoderFactory(): JwtDecoderFactory { val idTokenDecoderFactory = OidcIdTokenDecoderFactory() idTokenDecoderFactory.setJwsAlgorithmResolver { MacAlgorithm.HS256 } return idTokenDecoderFactory } ---- ==== [NOTE] 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. [TIP] 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. [[oauth2login-advanced-oidc-logout]] == OpenID Connect 1.0 Logout OpenID Connect Session Management 1.0 allows the ability to log out the End-User at the Provider using the Client. One of the strategies available is https://openid.net/specs/openid-connect-rpinitiated-1_0.html[RP-Initiated Logout]. If the OpenID Provider supports both Session Management and https://openid.net/specs/openid-connect-discovery-1_0.html[Discovery], the client may obtain the `end_session_endpoint` `URL` from the OpenID Provider's https://openid.net/specs/openid-connect-session-1_0.html#OPMetadata[Discovery Metadata]. This can be achieved by configuring the `ClientRegistration` with the `issuer-uri`, as in the following example: [source,yaml] ---- spring: security: oauth2: client: registration: okta: client-id: okta-client-id client-secret: okta-client-secret ... provider: okta: issuer-uri: https://dev-1234.oktapreview.com ---- ...and the `OidcClientInitiatedLogoutSuccessHandler`, which implements RP-Initiated Logout, may be configured as follows: ==== .Java [source,java,role="primary"] ---- @EnableWebSecurity public class OAuth2LoginSecurityConfig { @Autowired private ClientRegistrationRepository clientRegistrationRepository; @Bean public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { http .authorizeHttpRequests(authorize -> authorize .anyRequest().authenticated() ) .oauth2Login(withDefaults()) .logout(logout -> logout .logoutSuccessHandler(oidcLogoutSuccessHandler()) ); return http.build(); } private LogoutSuccessHandler oidcLogoutSuccessHandler() { OidcClientInitiatedLogoutSuccessHandler oidcLogoutSuccessHandler = new OidcClientInitiatedLogoutSuccessHandler(this.clientRegistrationRepository); // Sets the location that the End-User's User Agent will be redirected to // after the logout has been performed at the Provider oidcLogoutSuccessHandler.setPostLogoutRedirectUri("{baseUrl}"); return oidcLogoutSuccessHandler; } } ---- .Kotlin [source,kotlin,role="secondary"] ---- @EnableWebSecurity class OAuth2LoginSecurityConfig { @Autowired private lateinit var clientRegistrationRepository: ClientRegistrationRepository @Bean open fun filterChain(http: HttpSecurity): SecurityFilterChain { http { authorizeRequests { authorize(anyRequest, authenticated) } oauth2Login { } logout { logoutSuccessHandler = oidcLogoutSuccessHandler() } } return http.build() } private fun oidcLogoutSuccessHandler(): LogoutSuccessHandler { val oidcLogoutSuccessHandler = OidcClientInitiatedLogoutSuccessHandler(clientRegistrationRepository) // Sets the location that the End-User's User Agent will be redirected to // after the logout has been performed at the Provider oidcLogoutSuccessHandler.setPostLogoutRedirectUri("{baseUrl}") return oidcLogoutSuccessHandler } } ---- ==== NOTE: `OidcClientInitiatedLogoutSuccessHandler` supports the `{baseUrl}` placeholder. If used, the application's base URL, like `https://app.example.org`, will replace it at request time.