I'm upgrading a working application from Spring Boot 2.6.5 to 2.7.8 (Spring Security 5.7.6) so that I can better upgrade to 3.0.
Logout was working fine before. But after upgrading to Sprint Boot 2.7.8 the logout handling is hanging and never redirecting to the logoutSuccessUrl().
Here is the complete filter chain configuration:
http
.authorizeHttpRequests((authz) -> authz
.antMatchers("/webjars/**","/login/**","/mobile-manifest.json","/service-worker.js","/cache.manifest","/favicon.ico","/async/**","/api/**").permitAll()
.anyRequest().authenticated()
)
.httpBasic(withDefaults())
.formLogin(formLogin -> formLogin
.loginPage("/login")
.permitAll()
.loginProcessingUrl("/login")
.successHandler(savedRequestAwareAuthenticationSuccessHandler())
.failureUrl("/login?loginFailed=true"))
.logout( logout -> logout
.logoutUrl("/logout")
.logoutSuccessUrl("/login?logoutSuccess=true")
.invalidateHttpSession(true)
.deleteCookies(COOKIE_STRING))
.rememberMe( rememberMe -> rememberMe
.key(TOKEN_KEY)
.rememberMeParameter(REMEMBER_ME_KEY)
.tokenRepository(persistentTokenRepository())
.userDetailsService(userSvc)
.tokenValiditySeconds(validitySeconds));
The output:
[https-jsse-nio-7001-exec-10] [DEBUG] [o.s.security.web.FilterChainProxy] - Securing POST /logout
[https-jsse-nio-7001-exec-10] [DEBUG] [o.s.s.w.c.HttpSessionSecurityContextRepository] - Retrieved SecurityContextImpl [Authentication=UsernamePasswordAuthenticationToken [Principal=UserProfile [ blah blah, eventRoles=null, lastLoginDisplay=today, directoryUrl=null, groups=[]], Credentials=[PROTECTED], Authenticated=true, Details=WebAuthenticationDetails [RemoteIpAddress=127.0.0.1, SessionId=740230E1C02B57BEF504C23570FFA9EC], Granted Authorities=[]]]
[https-jsse-nio-7001-exec-10] [DEBUG] [o.s.s.w.c.SecurityContextPersistenceFilter] - Set SecurityContextHolder to SecurityContextImpl [Authentication=UsernamePasswordAuthenticationToken [Principal=UserProfile [blah blah eventRoles=null, lastLoginDisplay=today, directoryUrl=null, groups=[]], Credentials=[PROTECTED], Authenticated=true, Details=WebAuthenticationDetails [RemoteIpAddress=127.0.0.1, SessionId=740230E1C02B57BEF504C23570FFA9EC], Granted Authorities=[]]]
[https-jsse-nio-7001-exec-10] [DEBUG] [o.s.s.w.a.logout.LogoutFilter] - Logging out [UsernamePasswordAuthenticationToken [Principal=UserProfile [blah blah, eventRoles=null, lastLoginDisplay=today, directoryUrl=null, groups=[]], Credentials=[PROTECTED], Authenticated=true, Details=WebAuthenticationDetails [RemoteIpAddress=127.0.0.1, SessionId=740230E1C02B57BEF504C23570FFA9EC], Granted Authorities=[]]]
[https-jsse-nio-7001-exec-10] [DEBUG] [o.s.s.w.a.r.PersistentTokenBasedRememberMeServices] - Logout of user username
[https-jsse-nio-7001-exec-10] [DEBUG] [o.s.s.w.a.r.PersistentTokenBasedRememberMeServices] - Cancelling cookie
[https-jsse-nio-7001-exec-10] [DEBUG] [o.s.jdbc.core.JdbcTemplate] - Executing prepared SQL update
[https-jsse-nio-7001-exec-10] [DEBUG] [o.s.jdbc.core.JdbcTemplate] - Executing prepared SQL statement [delete from persistent_logins where username = ?]
[https-jsse-nio-7001-exec-10] [DEBUG] [o.s.jdbc.datasource.DataSourceUtils] - Fetching JDBC Connection from DataSource
[https-jsse-nio-7001-exec-10] [DEBUG] [o.s.s.w.a.l.SecurityContextLogoutHandler] - Invalidated session F3BC69312EC05C175A0FCEC298B49D06
[https-jsse-nio-7001-exec-10] [DEBUG] [o.s.s.w.c.HttpSessionSecurityContextRepository] - Did not store empty SecurityContext
[https-jsse-nio-7001-exec-10] [DEBUG] [o.s.s.w.c.HttpSessionSecurityContextRepository] - Did not store empty SecurityContext
[https-jsse-nio-7001-exec-10] [DEBUG] [o.s.s.w.c.SecurityContextPersistenceFilter] - Cleared SecurityContextHolder to complete request
Browser is never redirected to the loginSuccessUrl(), all DEBUG logging ends above. CSRF is enabled and logout was submitted POST.
I had .permitAll() in the logout configuration, but it seemed to have no effect in this case.
Screenshot showing request / response
Spring Security provides two implementations of LogoutSuccessHandler, see LogoutSuccessHandler:
LogoutSuccessHandler
The LogoutSuccessHandler is called after a successful logout by the LogoutFilter, to handle (for example) redirection or forwarding to the appropriate destination. Note that the interface is almost the same as the LogoutHandler but may raise an exception.
Spring Security provides the following implementations:
SimpleUrlLogoutSuccessHandler
HttpStatusReturningLogoutSuccessHandler
As mentioned earlier, you need not specify the SimpleUrlLogoutSuccessHandler directly. Instead, the fluent API provides a shortcut by setting the logoutSuccessUrl(). This sets up the SimpleUrlLogoutSuccessHandler under the covers. The provided URL is redirected to after a logout has occurred. The default is /login?logout.
The HttpStatusReturningLogoutSuccessHandler can be interesting in REST API type scenarios. Instead of redirecting to a URL upon the successful logout, this LogoutSuccessHandler lets you provide a plain HTTP status code to be returned. If not configured, a status code 200 is returned by default.
If you configure HTTP basic authentication, the HttpStatusReturningLogoutSuccessHandler is added, see
Default Logout Handler with HTTP Basic and XMLHttpRequest should be 204:
The default behavior for logout when the header X-Requested-With: XMLHttpRequest and HTTP Basic is enabled should be a 201. This will improve AngularJS experience which sends an accept header which includes text/html
See also: SEC-3103: Logout Success Content Negotiation.
Description:
I tried Logout sample application as given in blog: https://developer.okta.com/blog/2020/03/27/spring-oidc-logout-options, it is working as expected.
But with the same configurations, we are unable to logout from Okta in our main/ organizations’ Application.
Expected Flow:
On logout button click, UI calls ‘…/testbootapp/logout’ → Spring Security by default handles logout Spring Boot logout
‘OidcClientInitiatedLogoutSuccessHandler’ is configured in SpringBoot application, this should redirect to enter link description here
--- Here, in our application Spring Security is not redirecting to Okta logout URL
Implementation Details:
Spring boot dependency version: 2.7.0
application.properties is same as defined in blog
Attached SecurityConfiguration class as used in our application:
SecurityConfiguration.java
After enabling spring security logs in our application, we saw that ‘o.s.s.web.DefaultRedirectStrategy’ does not generate Okta logout URL, instead redirects to root URL
Extracted Logs from our Application:
Jul 21 2022 12:47:16 PM IST DEBUG o.s.s.w.a.l.SecurityContextLogoutHandler -Invalidated session 280BDF27CFF81149D6C829EF2ADAC9DF
Jul 21 2022 12:47:16 PM IST DEBUG o.s.s.web.DefaultRedirectStrategy -Redirecting to /testbootapp/
Extracted Logs from Sample application given in blog:
2022-07-21 11:25:20.230 DEBUG 7382 — [nio-8080-exec-5] o.s.s.w.a.l.SecurityContextLogoutHandler : Invalidating session: C54C40030C14B14F9F03250B769D2E99
2022-07-21 11:25:20.231 DEBUG 7382 — [nio-8080-exec-5] o.s.s.web.DefaultRedirectStrategy : Redirecting to ‘https://dev-.oktapreview.com/oauth2/default/v1/logout?id_token_hint=&post_logout_redirect_uri=https://twitter.com/’
Add the the below attribute in the ClientRegistration :
.providerConfigurationMetadata(Map.of("end_session_endpoint","{idc url}/oauth2/v1/logout"))
That would hit server and do the complete logout.
Updated spring security context not available on redirect
In one of my controllers - before redirect - I execute the following code:
Credential oldCredential = (Credential) authentication.getCredentials();
Authentication authenticationToken = new AuthenticationToken(new Credential(oldCredential.getCookieValue(), oldCredential.getPassword()),
updatedAccountDetails);
SecurityContextHolder.getContext().setAuthentication(authenticationToken);
When I inspect the authentication object after setting it I see the authentication context is correctly updated. After executing the code above I redirect.
After the redirect completes the security context loaded is the old one! That is: when I inspect the security context it contains not the new authentication token.
If I do the following for test - I add the setAttributE(...) after setAuthentication(...) - I get the new authenticationToken in my session but under (obviously) a different key: test. I tried a couple of strategies for the SecurityContextHolder (MODE_THREADLOCAL, MODE_INHERITABLETHREADLOCAL, MODE_GLOBAL) although I think the default MODE_THREADLOCAL is suited for my application.
request.getSession(false).setAttribute("test", authenticationToken);
The strange thing is that if I debug and check in SecurityContextPersistenceFilter the HttpSessionSecurityContextRepository I see that after the redirect the loaded context is actually the new one! For some reason - unknown to me at this moment - this load is however at some later point replaced(?) with the old security context.
Make sure that you've called setAuthenticated(true) otherwise the security context is not updated.
Can't find the way how to implement RememberMe service in OpenID. Have a class that extends AbstractRememberMeServices and it works with simple form login. Have next logs after login:
2015-12-17 07:29:15.086 DEBUG 260 --- [nio-8080-exec-5] p.s.s.CustomPersistentRememberMeServices : Did not send remember-me cookie (principal did not set parameter 'remember-me')
2015-12-17 07:29:15.086 DEBUG 260 --- [nio-8080-exec-5] p.s.s.CustomPersistentRememberMeServices : Remember-me login not requested.
Login works through /openid?openid_identifier=IDENTIFIER
Just add setAlwaysRemember(true); to your AbstractRememberMeServices implementation's constructor.
I am trying to implement OAuth2 into my Spring Boot app. When making a call to /oauth/token using a password grant, I am able to authenticate using Basic auth with a username/password against a database. The authentication object is loaded into the security context and the filter continues. However I noticed that further down the line the OAuth2AuthenticationProcessingFilter is called and cleans out the security context b/c no OAuth bearer token is found. This ultimately results in an authentication failure at the TokenEndpoint.postAccessToken method (e.g. /oauth/token).
Is it expected that the OAuth2AuthenticationProcessingFilter would be called during a /oauth/token call? If not, any ideas why? If it is expected what is the best fix/work around for this issue, to set stateless on the OAuth2AuthenticationProcessingFilter to false? Not sure if that is a good idea or not...
Some relevant info, I am using xml config for my spring security settings and JavaConfig for the rest. The grant type is password on the /oauth/token call.
Any help is greatly appreciated. Below are some log messages for context on what I am seeing.
09/30/15 22:42:50.899 [http-nio-9931-exec-8] DEBUG o.s.s.o.p.c.ClientCredentialsTokenEndpointFilter - Authentication success. Updating SecurityContextHolder to contain: org.springframework.security.oauth2.provider.OAuth2Authentication#9d98054f: Principal: testUser; Credentials: [PROTECTED]; Authenticated: true; Details: org.springframework.security.web.authentication.WebAuthenticationDetails#b364: RemoteIpAddress: 0:0:0:0:0:0:0:1; SessionId: null; Granted Authorities: ROLE_USER
09/30/15 22:42:50.899 [http-nio-9931-exec-8] DEBUG o.s.s.o.p.a.BearerTokenExtractor - Token not found in headers. Trying request parameters.
09/30/15 22:42:50.899 [http-nio-9931-exec-8] DEBUG o.s.s.o.p.a.BearerTokenExtractor - Token not found in request parameters. Not an OAuth2 request.
09/30/15 22:42:50.899 [http-nio-9931-exec-8] DEBUG o.s.s.o.p.a.OAuth2AuthenticationProcessingFilter - Clearing security context.
09/30/15 22:42:50.900 [http-nio-9931-exec-8] DEBUG o.s.s.o.p.a.OAuth2AuthenticationProcessingFilter - No token in request, will continue chain.
09/30/15 22:42:50.900 [http-nio-9931-exec-8] DEBUG o.s.s.o.p.a.BearerTokenExtractor - Token not found in headers. Trying request parameters.
09/30/15 22:42:50.900 [http-nio-9931-exec-8] DEBUG o.s.s.o.p.a.BearerTokenExtractor - Token not found in request parameters. Not an OAuth2 request.
09/30/15 22:42:50.900 [http-nio-9931-exec-8] DEBUG o.s.s.o.p.a.OAuth2AuthenticationProcessingFilter - No token in request, will continue chain.
09/30/15 22:42:50.934 [http-nio-9931-exec-8] DEBUG o.s.s.o.p.e.FrameworkEndpointHandlerMapping - Looking up handler method for path /oauth/token
09/30/15 22:42:50.937 [http-nio-9931-exec-8] DEBUG o.s.s.o.p.e.FrameworkEndpointHandlerMapping - Returning handler method [public org.springframework.http.ResponseEntity<org.springframework.security.oauth2.common.OAuth2AccessToken> org.springframework.security.oauth2.provider.endpoint.TokenEndpoint.postAccessToken(java.security.Principal,java.util.Map<java.lang.String, java.lang.String>) throws org.springframework.web.HttpRequestMethodNotSupportedException]
09/30/15 22:42:50.958 [http-nio-9931-exec-8] INFO o.s.s.o.p.endpoint.TokenEndpoint - Handling error: InsufficientAuthenticationException, There is no client authentication. Try adding an appropriate authentication filter.
09/30/15 22:42:51.070 [http-nio-9931-exec-8] DEBUG o.s.s.w.a.ExceptionTranslationFilter - Chain processed normally
09/30/15 22:42:51.070 [http-nio-9931-exec-8] DEBUG o.s.s.w.c.SecurityContextPersistenceFilter - SecurityContextHolder now cleared, as request processing completed