Home > other >  Logic to implement a RESTFUL logout API using oauth2ResourceServer JWT in a spring application
Logic to implement a RESTFUL logout API using oauth2ResourceServer JWT in a spring application

Time:10-29

The issue I have is after the user is authenticated meaning user has signed in, I understand from the client side to logout a user, I delete the token from the local storage but the issue I have is how do I invalidate the token or logout from the serverside.

My intial approach was to make the logout API permit all in my SecurityFilterChain but when I try to grab the authenticated user from SecurityContextHolder after the user had signed in I was getting anonymousUser.

My second/current approach is I instead authorized LOGOUT API which means to access the API, a token has to passed in the header. Then I can then set SecurityContextHolder.getContext().setAuthentication(authentication == false); and also clearContext(). With this approach I am able to get the logged in user but my questions are:

  1. Is this the right logic to implement a log out?
  2. I understand a token cannot be invalidated because it is STATELESS. But is there a way to get around this? Because even after setting Authentication to false in SecurityContextHolder and clearing security context SecurityContextHolder.clearContext(); when I try accessing Authenticated API i.e CRUD operations, I am still able to use the token.

Here is my login and logout methods in my RestController Class

logout

@PostMapping(path = "/logout", headers = "Authorization")
@ResponseStatus(OK)
public ResponseEntity<?> logout() {
        LOGGER.info("Trying to Logout ");

        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        String username = authentication.getName();
        LOGGER.info("Username {} ", username);
        authentication.setAuthenticated(false);
        SecurityContextHolder.getContext().setAuthentication(authentication);
        SecurityContextHolder.clearContext();

        return ResponseEntity.ok().body("Successfully logged out");
    }

login

@PostMapping(path = "/login", consumes = "application/json", produces = "application/json")
@ResponseStatus(OK)
public ResponseEntity<?> login(@Valid @RequestBody UserDTO userDTO) {

        Authentication authentication;
        LOGGER.info("Authenticating {}", userDTO.getUsername());

        var authenticationToken = confirmUser(userDTO); // returns a UsernamePasswordAuthenticationToken
        try {
            authentication = authenticationManager.authenticate(authenticationToken); // Authenticate user password token
            SecurityContextHolder.getContext().setAuthentication(authentication); // Set the security context to the logged user
        } catch (AuthenticationException e) {
            LOGGER.error("Stack trace {}", e.getMessage());
            SecurityContextHolder.getContext().setAuthentication(null);
            throw new InvalidPasswordException("Wrong username or password");
        }

        LOGGER.info("{} has signed in", userDTO.getUsername());
        return ResponseEntity.ok()
                .header( AUTHORIZATION, tokenService.generateToken(authentication) )
                .build();
    }

CodePudding user response:

I might recommend a different approach, but let's start with your question.

Expiring Access Tokens

To expire a resource server token, you will need to add some kind of state.

This usually comes in the form of some kind of list of valid tokens. If the token isn't in the list, then the token is not valid.

A common way to achieve this is to rely on the authorization server. Many authorization servers ship with an endpoint that you can hit to see if a token is still valid.

Modeling Things Differently

That said, it might be worth considering if you should be thinking about the access token differently. The access token does not represent a user's authenticated session. It represents the user granting access to the client to operate on the user's behalf.

So after the user logs out, it still makes quite a bit of sense for the client to have a valid access token so that the user doesn't have to reauthorize the client every time they log in.

  • Related