How to logout oauth2 client in Spring?

19,697

Solution 1

You can delete the refresh token as well as access token from database to save space.

@PostMapping("/oauth/logout")
public ResponseEntity<String> revoke(HttpServletRequest request) {
    try {
        String authorization = request.getHeader("Authorization");
        if (authorization != null && authorization.contains("Bearer")) {
            String tokenValue = authorization.replace("Bearer", "").trim();

            OAuth2AccessToken accessToken = tokenStore.readAccessToken(tokenValue);
            tokenStore.removeAccessToken(accessToken);

            //OAuth2RefreshToken refreshToken = tokenStore.readRefreshToken(tokenValue);
            OAuth2RefreshToken refreshToken = accessToken.getRefreshToken();
            tokenStore.removeRefreshToken(refreshToken);
        }
    } catch (Exception e) {
        return ResponseEntity.badRequest().body("Invalid access token");
    }

    return ResponseEntity.ok().body("Access token invalidated successfully");
}

The URL to logout will be : http://localhost:9999/oauth/logout Also, pass the access token in the Authorization header, as

Authorization: Bearer 0cb72897-c4f7-4f01-aed9-2f3f79a75484

where, 0cb72897-c4f7-4f01-aed9-2f3f79a75484 is the access token.

Since, its Spring security, don't forget to bypass /oauth/logout url from authorize access, as

public void configure(WebSecurity web) throws Exception {
    web.ignoring().antMatchers("/hello", "/oauth/logout");
}

Hope, it will solve your logout problem in Springboot2+Oauth2. Its working for me.

Solution 2

Add following code snippet to your ClientApplication class. This will also clear your session details.

Replace below code with the configure method of your web security adapter class.

@Override
    protected void configure(HttpSecurity http) throws Exception {
        http.antMatcher("/**")
                .authorizeRequests()
                .antMatchers( "/login**", "/webjars/**", "/error**").permitAll()
                .anyRequest()
                .authenticated()
                .and().logout().invalidateHttpSession(true)
                .clearAuthentication(true).logoutSuccessUrl("/login?logout").deleteCookies("JSESSIONID").permitAll().and().csrf().csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse());
    }

Solution 3

You probably want to use the Spring Security built-in support for the /logout endpoint which will do the right thing (clear the session and invalidate the cookie). To configure the endpoint extend the existing configure() method in our WebSecurityConfigurer:

@Override
protected void configure(HttpSecurity http) throws Exception {
  http.antMatcher("/**")
     .and().logout().logoutSuccessUrl("/").permitAll();
}
Share:
19,697
gstackoverflow
Author by

gstackoverflow

Updated on June 15, 2022

Comments

  • gstackoverflow
    gstackoverflow almost 2 years

    I have the simplest oauth2 client:

    @EnableAutoConfiguration
    @Configuration
    @EnableOAuth2Sso
    @RestController
    public class ClientApplication {
    
        @RequestMapping("/")
        public String home(Principal user, HttpServletRequest request, HttpServletResponse response) throws ServletException {       
            return "Hello " + user.getName();
        }
    
        public static void main(String[] args) {
            new SpringApplicationBuilder(ClientApplication.class)
                    .properties("spring.config.name=application").run(args);
        }
    
    }
    

    I also have the following application.yml:

    server:
      port: 9999
      servlet:
        context-path: /client
    security:
      oauth2:
        client:
          client-id: acme
          client-secret: acmesecret
          access-token-uri: http://localhost:8080/oauth/token
          user-authorization-uri: http://localhost:8080/oauth/authorize
        resource:
          user-info-uri: http://localhost:8080/me
    
    logging:
      level:
        org.springframework.security: DEBUG
        org.springframework.web: DEBUG
    

    It is the full code. I don't have any additional source code. It works properly.

    But now I want to add a logout feature. I've added an endpoint but it doesn't work. I tried to do the following:

    @RequestMapping("/logout")
        public void logout(HttpServletRequest request, HttpServletResponse response) throws ServletException {
            Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
            authentication.setAuthenticated(false);
            new SecurityContextLogoutHandler().logout(request,response,authentication);
            SecurityContextHolder.clearContext();
            request.logout();
            request.getSession().invalidate();
        }
    

    But I am still logged in and can access / url and it responds to me with the username.

    Can you help me fix this issue?

    Update

    I tried the approach described here https://spring.io/guides/tutorials/spring-boot-oauth2/#_social_login_logout :

    @EnableAutoConfiguration
    @Configuration
    @EnableOAuth2Sso
    @Controller
    public class ClientApplication extends WebSecurityConfigurerAdapter {
        private Logger logger = LoggerFactory.getLogger(ClientApplication.class);
    
        @RequestMapping("/hello")
        public String home(Principal user, HttpServletRequest request, HttpServletResponse response, Model model) throws ServletException {
            model.addAttribute("name", user.getName());
            return "hello";
        }
    
        @Override
        protected void configure(HttpSecurity http) throws Exception {
            // @formatter:off
            http.antMatcher("/**")
                    .authorizeRequests()
                    .antMatchers( "/login**", "/webjars/**", "/error**").permitAll()
                    .anyRequest()
                    .authenticated()
                    .and().logout().logoutSuccessUrl("/").permitAll()
                    .and()
                        .csrf()
                        .csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse());
            // @formatter:on
        }
    
        public static void main(String[] args) {
            new SpringApplicationBuilder(ClientApplication.class)
                    .properties("spring.config.name=application").run(args);
        }
    }
    

    and on FE I wrote:

    <script type="text/javascript">
            $.ajaxSetup({
                beforeSend: function (xhr, settings) {
                    if (settings.type == 'POST' || settings.type == 'PUT'
                        || settings.type == 'DELETE') {
                        if (!(/^http:.*/.test(settings.url) || /^https:.*/
                                .test(settings.url))) {
                            // Only send the token to relative URLs i.e. locally.
                            xhr.setRequestHeader("X-XSRF-TOKEN",
                                Cookies.get('XSRF-TOKEN'));
                        }
                    }
                }
            });
            var logout = function () {
                $.post("/client/logout", function () {
                    $("#user").html('');
                    $(".unauthenticated").show();
                    $(".authenticated").hide();
                });
                return true;
            };
            $(function() {
                $("#logoutButton").on("click", function () {
                    logout();
                });
            });
    
        </script>
    

    and

    <input type="button" id="logoutButton" value="Logout"/>
    

    But it still doesn't work. It results in the following behavior:

    Post http://localhost:9999/client/logout redirects to the http://localhost:9999/client but this page doesn't exist

    source code on gitub:
    client - https://github.com/gredwhite/logour_social-auth-client (use localhost:9999/client/hello url)
    server - https://github.com/gredwhite/logout_social-auth-server