How to invalidate JWT Token such as logout or reset all active tokens in Spring Boot ?

In this article, I will share my experiences on how to manage the JWT Token on the server-side. How do you make sure the token is safe?


invalid the jwt token on the server such as logout?

As you know that the JWT token is stateless and offline verification without additional connecting other services or databases. The token can be used until it is expired by itself.

Let see the structure JWT token json format (Header + Payload + Signature)

//generate into jwt tokeneyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJKV1QgQnVpbGRlciIsImlhdCI6MTU4MDM5MDAwMCwiZXhwIjoxNTg1NDg3NjAwLCJhdWQiOiJ3d3cuZXhhbXBsZS5jb20iLCJzdWIiOiJmb2ZvQGdtYWlsLmNvbSIsInVzZXJJZCI6IjEyMzQ0IiwiU3VybmFtZSI6IlJvY2tldCIsIkVtYWlsIjoiZm9mb0BnbWFpbC5jb20iLCJSb2xlIjoiW1wiUk9MRV9BRE1JTlwiLFwiVVNFUl9VU0VSXCJdIn0.uzTh_Z_oKoBSmIcOqlx5dvQ9Th6LgtgCTubm-CeIYfY-- https://jwt.io/ 
put the jwt token above you will see the all claims data
//jwt claims data-set
{
"iss": "JWT Builder",
"iat": 1580390000,
"exp": 1585487600,
"aud": "www.example.com",
"sub": "[email protected]",
"ip" : "x.x.x.x",
"userAgent":"UserAgentInfo",
"userId": "12344",
"Surname": "Rocket",
"Email": "[email protected]",
"Role": "[\"ROLE_ADMIN\",\"USER_USER\"]"
}

The JWT’s goal is designed for different purposes with server sessions, it means that it is not possible to force removing the token or invalidate the existing token.

However, for some reason in the business requirements as they need to invalidate the token immediately such as the following cases:

  • Logout
  • update current credentials
  • reset all active sessions of specific users
  • reset all previous tokens for upgrading new version

JWTFitler Validate the token

JWTFilter
Now you know and get the idea of how and where to generate the JWT token and validating the token.

How to logout?

Let assume that your project is the e-commerce web system that uses JWT token for exchanging the user login info. In some actions, the system requires the users to log in first and so that you can see that features, for instance, favorite list, my orders, and my account. After using some awhile, the user clicking logout action from the website – the client side just clears the token from local/sessions storage or cookies, but the token is still valid on the server.

If someone has copied this token to other people, they can still be able to perform the requests on behalf of the user until they expired for a short time period. So how can we prevent this situation on the server?

There are many ways to handle with JWT token:

  1. store Client IP Address in the claims objects JWT. When validating token you can check with this client IP address if it is the same source or not.
final Map<String, Object> claims = new HashMap<>();
...
claims.put(ROLES, roles);
//add ip client adress
claims.put("ip",getClientIp(request);
....
private static String getClientIp(HttpServletRequest request) {
String remoteAddr = "";
if (request != null) {
remoteAddr = request.getHeader("X-FORWARDED-FOR");
if (remoteAddr == null || "".equals(remoteAddr)) {
remoteAddr = request.getRemoteAddr();
}
}
return remoteAddr;
}
// check if token is valid structure or not
// token expired or not
// check client ip address is the matched with ip-address store in claim object or not
if (jwtTokenService.validateToken(token)) {
final String ipAddress = getClientIp(request);
// not match return error
if (!ipAddress.equals(jwtTokenService.getClientIp(token) {
//return 401
}
}

2. store the User-Agent info in claim objects. Follow the same validating as above with the User-Agent info instead

public static String getUserAgent(HttpServletRequest request) {
String ua = "";
if (request != null) {
ua = request.getHeader("User-Agent");
}
return ua;
}
// check if token is valid structure or not
// token expired or not
// check client User-Agent is the matched with User-Agent store in claim object or not

3. store both IP-address and the User-Agent in Claims Object and check all together

// check if token is valid structure or not
// token expired or not
// check client IP Address and User-Agent are the matched with IP Address and User-Agent store in claim object or not

4. When a user performs logout action, the client should call a server API and in this API will add this token into the blocklist sections either in the cache Redis or database. So the validating token process you check if the token is in blocklist or not.

Logout action
check the token in blacklist cache

The options 1, 2 and 3 are not the perfect solution but it can prevent somehow.

For option 4: It is the better solution and the best approach to handle the problem.

Change password — Invalidate the token

In this action, it depends on your tradeoff what you want to do. Let’s say any update/change password successfully and responding back with the new token, the old token should be no longer valid.

The solution to handle this, you can follow the solution logout.

Add the old token into the blacklist sections either in the cache Redis (the best option) or database. So when validating the token process, you should check if the token is valid and not expired first, if it is true, check one more condition if the token is in blocklist or not.

// check if token is valid structure or not
// token expired or not
// token is in blacklist

Reset all active Tokens of specific users

In this scenario, you can save the userId in the reset tokens list that should store in the cache for a short time.

In the validating JWT token process, if the JWT is valid structure and not expired and then continue to decrypt into UserInfo Object, and check if userId is in the reset tokens list or not. If yes you can reject the request back.

// check if token is valid structure or not
// token expired or not
//get UserObject from JWT
user.getId() and user.getUpdatedDate()
if resetTokens.contains(user.getId()) && resetTokens.get(userId).getUpdatedDate() > user.getUpdatedDate()) {
//return status 401
}

How to force removing the previous tokens on-demand?

you can save lastUpdateValue in the Redis cache for a short time. In the validate the JWT token, if the JWT is valid and decrypts into UserInfo Object, and check issue date of token < lastUpdateValue in cache or not. If yes you can reject the request back.

//check jwt is valid and not expired//get issueDate from JWT
// check issueDate < cache.get("lastUpdateValue) then return 401

How to prevent the JWT token decrypting over the online tools?

To make sure the token can be decrypted by your system only, one way you can do after generating JWT token, adding your custom key to encrypt JWT token before sending it to the client.

In the validating process decrypting token back with your custom key to original JWT token and validating the JWT as standard flow.

Popular posts from this blog

Don't like IDEs? Try grepgitvi-DevoTech.in

Man Uses 99 Smartphones to Fool Google Maps and Create a Fake Traffic Jam Here is some pic ..

Top 10 In-Demand programming languages to learn in this Year