Thursday, 16 January 2025

Expiring a JWT Token Before Its Natural Expiry

 Expiring a JWT Token Before Its Natural Expiry

JSON Web Tokens (JWTs) are widely used for stateless authentication. By default, a JWT is valid until its expiration time (exp claim). However, there are scenarios where you might want to invalidate a token before its natural expiry, such as:

  • A user logs out.
  • A user changes their password.
  • A security breach or session compromise.

In this blog post, we’ll explore how to expire a JWT before its natural expiration and best practices to handle token invalidation.


Why JWT Invalidation Is Challenging

JWTs are stateless, meaning they do not rely on a server-side session. Once issued, they remain valid until their exp time unless you introduce a mechanism to track or revoke them. This statelessness makes JWT efficient but complicates early expiration.


Techniques to Invalidate JWTs Early

1. Use a Blacklist

Store invalidated tokens in a blacklist and check against it during token validation. While effective for early token invalidation, maintaining a blacklist in memory can lead to scalability challenges in large-scale applications. For instance, a high user base may result in significant memory usage and slower lookups. Using a distributed in-memory data store like Redis can help mitigate these issues by enabling efficient and scalable token management.

Implementation:

@Service
public class JwtBlacklistService {

    private final Set<String> blacklist = new ConcurrentHashMap.newKeySet();

    public void blacklistToken(String token) {
        blacklist.add(token);
    }

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

Validation:

Integrate the blacklist check in your authentication process:

public boolean validateToken(String token) {
    if (blacklistService.isBlacklisted(token)) {
        return false; // Token is invalidated
    }
    // Perform other validation checks (signature, expiry, etc.)
    return true;
}

2. Use a Short Token Lifespan with Refresh Tokens

Limit the lifespan of your access tokens (e.g., 15 minutes) and issue long-lived refresh tokens for re-authentication. If an access token is compromised, it will expire soon.

Implementation:

  • Access token lifespan: 15 minutes
  • Refresh token lifespan: 7 days

On token refresh, validate the refresh token and issue a new access token.

3. Store a Revocation Flag in a Centralized Store

Maintain a flag in your database to track token validity.

Example:

  1. Add a jwtRevokedAt field to your user table.
  2. During token validation, ensure the token was issued before this timestamp.

Validation Code:

public boolean validateToken(String token, User user) {
    Date issuedAt = getIssuedAtFromToken(token); // Extract 'iat' claim
    return issuedAt.before(user.getJwtRevokedAt());
}

4. Token Versioning

Include a version claim in the JWT payload and store the current version in the database.

Example:

  1. JWT Payload:
{
  "sub": "user123",
  "version": 1,
  "exp": 1700000000
}
  1. Database:
User ID Current Version
user123 2
  1. Validation Code:
public boolean validateToken(String token, User user) {
    int tokenVersion = getVersionFromToken(token); // Extract 'version'
    return tokenVersion == user.getCurrentVersion();
}

Best Practices for Early Token Expiry

  1. Use HTTPS: Always secure token transmission to prevent interception.
  2. Implement Rotation: Use refresh tokens and rotate them regularly.
  3. Minimal Scope: Issue tokens with the least required privileges.
  4. Log Out Detection: Monitor login/logout activities and blacklist tokens accordingly.
  5. Optimize Blacklists: Use a Redis cache for performance when managing blacklists.

Common Mistakes

  1. Relying Solely on JWT Expiry: Neglecting early invalidation mechanisms.
  2. Not Encrypting Sensitive Data: Storing confidential information in the payload without encryption.
  3. Ignoring Logout Needs: Users expect their tokens to be invalidated upon logout.
  4. Unlimited Refresh Tokens: Allowing indefinite refresh token usage without rotation.

Interview Explanation

Question: How can you invalidate a JWT before its expiry?

Answer:

"JWT invalidation can be achieved through blacklisting, maintaining a revocation timestamp, or token versioning. For example, when a user logs out, their token can be added to a blacklist checked during token validation. Alternatively, storing a jwtRevokedAt timestamp ensures tokens issued before the timestamp are invalid. Combining these methods with short-lived access tokens and refresh tokens balances security and user experience."


Further Topics in System Design

  • OAuth 2.0 and OpenID Connect
  • Token Revocation Standards (RFC 7009)
  • Stateless vs Stateful Authentication
  • Designing Secure APIs with JWT

Mastering token management and expiration strategies is critical for designing secure and user-friendly systems.