Search code examples
springspring-bootspring-oauth2

Unable to logout in Spring OAuth2 using JdbcTokenStore


Spring OAuth2 with JdbcTokenStore - custom login page has been used, as coded in the snippet below.

From different resources online e.g. here Spring Security seems to have an inbuilt endpoint /logout to log a user out, but that doesn't seem to work for me. When I hit that endpoint, it redirects back to the custom login page, which is good, but inconsistent. Working with multiple tabs, it works sometimes but not every time. Also noticed that the cookie created by Spring is not clearing out as well.

Is there something wrong with the WebSecurityConfigurerAdapter defined as below?

@Configuration
@Order(-20)
protected static class LoginConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private AuthenticationManager authenticationManager;

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        // @formatter:off
        http
            .formLogin()
            .loginPage("/login")
            .permitAll()
            .defaultSuccessUrl("/homepage", false)
            .failureUrl("/login?error=true")    
        .and()
            .requestMatchers().antMatchers("/login", "/homepage", "/login?error=true", "/oauth/authorize", "/oauth/confirm_access")
        .and()
            .authorizeRequests().anyRequest().authenticated();
        // @formatter:on
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.parentAuthenticationManager(authenticationManager);
    }
}

Once the in-built logout functionality starts working, it would be ideal to delete the token created in the database as well. Tried a few potential answers online but they are not working. Any pointers would be deeply appreciated?

I can post more code snippets, if it will help provide more clarity.


Solution

  • Finally we got this to work - hopefully this is helpful for someone else in the same boat.

    You can ignore the session management configuration if you do not need that in the code snippet below.

    @Configuration
    @Order(-20)
    protected static class LoginConfig extends WebSecurityConfigurerAdapter {
        @Autowired
        private AuthenticationManager authenticationManager;
    
        @Override
        protected void configure(HttpSecurity http) throws Exception {
            // @formatter:off
            http.sessionManagement()
                .sessionCreationPolicy(SessionCreationPolicy.NEVER)
                .invalidSessionUrl("/login")
                .sessionAuthenticationErrorUrl("/login")
                .and()
                    .formLogin().loginPage("/login").permitAll().defaultSuccessUrl("/homepage", false)
                    .failureUrl("/login?error=true")
                .and()
                    .requestMatchers()
                    .antMatchers("/", "/login", "/logout", "/homepage", "/login?error=true", "/oauth/authorize", "/oauth/confirm_access")
                .and()
                    .logout().logoutUrl("/logout").logoutSuccessUrl("/login").invalidateHttpSession(true)
                    .permitAll()
                .and().authorizeRequests()
                    .antMatchers("/login**")
                    .permitAll().anyRequest().authenticated();
            // @formatter:on
        }
    
        @Override
        protected void configure(AuthenticationManagerBuilder auth) throws Exception {
            auth.parentAuthenticationManager(authenticationManager);
        }
    }
    

    Creating a logout controller along with the above will do the trick

    @Controller
    public class LogoutController {
    
        @RequestMapping(value = "/logout", method = RequestMethod.GET)
        public String logout(HttpServletRequest request, HttpServletResponse response, Model model) {
            /* Getting session and then invalidating it */
            HttpSession session = request.getSession();
            if (session != null) {
                session.invalidate();
            }
            HandleLogOutResponse(response, request);
            return "logout";
        }
    
        private void HandleLogOutResponse(HttpServletResponse response, HttpServletRequest request) {
            Cookie[] cookies = request.getCookies();
            for (Cookie cookie : cookies) {
                cookie.setMaxAge(0);
                cookie.setValue(null);
                cookie.setPath("/");
                response.addCookie(cookie);
            }
        }
    

    And you can register your view using a simple function like below

    public void addViewControllers(ViewControllerRegistry registry) {
        registry.addViewController("/").setViewName("login");
        registry.addViewController("/login").setViewName("login");
        registry.addViewController("/homepage").setViewName("homepage");
        registry.addViewController("/logout").setViewName("logout");
    }