JWT | Spring Security

Invalidate/Revoked the JWT : Force logout the user from spring security

Zeeshan Adil
JavaToDev
Published in
5 min readNov 1, 2023

--

In my previous post, I discussed the implementation of JWT-based authentication, authorization, and a token refresh mechanism in Spring Boot 3 and Spring Security 6. This post serves as a continuation of that discussion.

To ensure we’re aligned, please review the following topics before proceeding further.

  1. JWT Authentication & Authorization (Login)
  2. Refresh Token

JWTs offer the advantage of being stateless, which eliminates the need to query the database for token validation. This can be beneficial in terms of reducing the database load. However, it becomes a limitation when you want to invalidate a non-expired token.

We have multiple options to achieve it. Let’s discuss all possible options.

  1. Storing JWTs in a database allows you to track the validity of tokens and identify revoked ones. However, some argue that this approach contradicts the fundamental purpose of using JWTs, which is to maintain statelessness.
  2. Removing a token from the client effectively prevents that client from making authenticated requests. However, if the token is still valid and falls into the hands of someone else, it remains susceptible to unauthorized usage. This brings me to the next issue I’d like to address.
  3. Implementing a short token lifetime strategy involves setting tokens to expire rapidly, which can vary from a few minutes to up to half an hour, depending on the application’s requirements. When a client deletes its token, there exists a brief window during which the token can still be utilized. Combining the practice of token deletion from the client and short token lifetimes does not necessitate significant backend adjustments. However, the downside is that frequent token expiration results in users being frequently logged out due to expired tokens.
  4. Introducing the concept of refresh tokens can be a valuable approach. During user login, you provide them with both a JWT and a refresh token. The refresh token is securely stored in a database. For authenticated requests, the client utilizes the JWT, but when the JWT expires (or approaches expiration), the client can use the refresh token to request a new JWT. This minimizes the need to query the database except when a user logs in or seeks a new JWT. It’s important to ensure that when a user logs out, you invalidate the stored refresh token, as failing to do so could potentially allow eavesdroppers to obtain new JWTs even after the user has logged out.
  5. Establishing a JWT blacklist can be a useful strategy. Depending on the token’s expiration time, there might be a delay between when the client deletes its token and when it becomes invalid. If you desire immediate invalidation, you can implement a token blacklist. When the server receives a logout request, it extracts the JWT from the request and stores it in an in-memory database. For every authenticated request, the system checks the in-memory database to determine if the token has been invalidated. To manage the search space efficiently, you can remove tokens from the blacklist once they have expired.

We will implement last option: Blacklist the JWT approach here…

To expire/invalidate a JWT token at logout in a Spring Boot application, you can implement the following steps:

  1. Blacklist or Invalidate JWT Tokens

To invalidate the JWT token upon logout, you can maintain a blacklist or a list of revoked tokens. When a user logs out, add their token to this blacklist. When a request is made with a blacklisted token, it should be rejected. You can store the blacklisted tokens in memory, in a database, or using a distributed cache like Redis.

Here’s a simplified example of a TokenBlacklist service:

public interface TokenBlacklist {
void addToBlacklist(String token);
boolean isBlacklisted(String token);
}

@Service
public class InMemoryTokenBlacklist implements TokenBlacklist {
private Set<String> blacklist = new HashSet<>();

@Override
public void addToBlacklist(String token) {
blacklist.add(token);
}

@Override
public boolean isBlacklisted(String token) {
return blacklist.contains(token);
}
}

2. Implement a Logout Endpoint

Create a REST endpoint that handles the logout action. When a user logs out, you will invalidate the JWT token associated with their session. Here’s an example of how you can create a logout endpoint.

@PostMapping
public ResponseEntity<String> logout(HttpServletRequest request) {
String token = extractTokenFromRequest(request);
tokenBlacklist.addToBlacklist(token);

// Clear any session-related data if necessary

return ResponseEntity.ok("Logged out successfully");
}
public String extractTokenFromRequest(HttpServletRequest request) {
// Get the Authorization header from the request
String authorizationHeader = request.getHeader("Authorization");

// Check if the Authorization header is not null and starts with "Bearer "
if (StringUtils.hasText(authorizationHeader) && authorizationHeader.startsWith("Bearer ")) {
// Extract the JWT token (remove "Bearer " prefix)
return authorizationHeader.substring(7);
}

// If the Authorization header is not valid, return null
return null;
}

3. Token Validation

Before processing any secured requests, validate the JWT token and check if it’s blacklisted. You can do this in a filter or Spring Security configuration. If the token is blacklisted or expired, deny access.

@Component
public class JwtTokenFilter extends OncePerRequestFilter {
@Autowired
private TokenBlacklist tokenBlacklist;

@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
String token = extractTokenFromRequest(request);

if (token != null && !tokenBlacklist.isBlacklisted(token)) {
// Token is valid and not blacklisted
// Proceed with request processing
filterChain.doFilter(request, response);
} else {
// Token is blacklisted or expired, deny access
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
}
}
}

Conclusion

In summary, implementing a JWT blacklist is a technique to ensure immediate invalidation of tokens, addressing the potential delay between token deletion by the client and actual expiration. When a logout request is received, the backend stores the JWT in an in-memory database. Subsequently, for each authenticated request, the system checks the blacklist to verify token validity. To optimize this process, expired tokens can be periodically removed from the blacklist. This approach provides additional control over token revocation, although it may require additional overhead for managing the blacklist.

Note: If you decide to implement the JWT blacklist as a means to invalidate JWTs, I strongly recommend storing the blacklist in a caching database rather than relying on in-memory storage.

Feel free to share your questions or seek advice on how to store the blacklist in a caching database like Redis. I’m here to help with any recommendations or guidance you may need.

❤️ Support & Engagement ❤️

❤ If you find this article informative and beneficial, please consider showing your appreciation by giving it a clap 👏👏👏, highlight it and replying on my story story. Feel free to share this article with your peers. Your support and knowledge sharing within the developer community are highly valued.
Please share on social media
Follow me on : Medium || LinkedIn
Check out my other articles on Medium
Subscribe to my newsletter 📧, so that you don’t miss out on my latest articles.
❤ If you enjoyed my article, please consider buying me a coffee ❤️ and stay tuned to more articles about java, technologies and AI. 🧑‍💻

--

--

Zeeshan Adil
JavaToDev

Full Stack Developer || Educator || Technical Blogger 🧑‍💻Let's Connect : https://www.linkedin.com/in/zeeshan-adil-a94b3867/