Kishan Solanki
Kishan Solanki

Reputation: 14636

JAVA : How to invalidate all JWT token of particular user?

I have a project where currently there is a functionality where multiple user session is enabled. But now I want to make it something like when a new user logged in, then all other sessions of that user will be invalidated. User authentication is based on JWT Token. So how can I invalidate all the existing JWT Tokens of that user and keep only the new one?

Note: Currently I am not storing JWT Token in Database and I don't want that because it leaks the security.

Here is the class for JWTTokenUtils from where I generate the token and validate it.

@Component
public class JwtTokenUtil implements Serializable {

    public String getUsernameFromToken(String token) {
        return getClaimFromToken(token, Claims::getSubject);
    }

    public Date getExpirationDateFromToken(String token) {
        return getClaimFromToken(token, Claims::getExpiration);
    }

    public <T> T getClaimFromToken(String token, Function<Claims, T> claimsResolver) {
        final Claims claims = getAllClaimsFromToken(token);
        return claimsResolver.apply(claims);
    }

    private Claims getAllClaimsFromToken(String token) {
        return Jwts.parser()
                .setSigningKey(Base64.getEncoder().encodeToString(Constants.JWTToken.SIGNING_KEY.getBytes()))
                .parseClaimsJws(token)
                .getBody();
    }

    private Boolean isTokenExpired(String token) {
        final Date expiration = getExpirationDateFromToken(token);
        return expiration.before(new Date());
    }

    public String generateToken(String userId) {
        return doGenerateToken(userId);
    }

    private String doGenerateToken(String userId) {
        Claims claims = Jwts.claims().setSubject(userId);
        claims.put("scopes", Arrays.asList(new SimpleGrantedAuthority("ROLE_USER")));
        return Jwts.builder()
                .setClaims(claims)
                .setId(userId)
                .setIssuer(null)
                .setIssuedAt(new Date(System.currentTimeMillis()))
                .setExpiration(new Date(System.currentTimeMillis() + ACCESS_TOKEN_VALIDITY_SECONDS * 1000))
                .signWith(SignatureAlgorithm.HS256, Base64.getEncoder().encodeToString(Constants.JWTToken.SIGNING_KEY.getBytes()))
                .setHeaderParam("typ", "JWT")
                .compact();
    }

    public Boolean validateToken(String token, UserDetails userDetails) {
        final String username = getUsernameFromToken(token);
        return (username.equals(userDetails.getUsername())
                && !isTokenExpired(token));
    }
}

Upvotes: 1

Views: 1060

Answers (1)

Tim Biegeleisen
Tim Biegeleisen

Reputation: 522541

In order to do this, you basically need to persist some sort of state, somewhere. One general approach which you may consider would be to maintain a blacklist cache of users who should be blocked. In your particular case, you could also store a date associated with each user. For each incoming request, you would first quickly hit the blacklist cache and make sure that the incoming JWT's date were not too old. Of course, you could assert the exp claim, because perhaps the incoming JWT be already expired in the first place.

The key point here is to use a cache, like Redis, which has very fast lookups. You could also use a database, but that would be about 100 times slower than a good cache tool. Regarding pruning the cache, when a user no longer requires the old JWT state, you may delete his entry. This can be automated, perhaps, by assigning an expiry to the cache entry when you write it.

Upvotes: 4

Related Questions