Remove JWT on logout in Spring Application
Solution 1
Long story short
, you have to build a mechanism to delete or invalidate a token manually
on logout.
Should I store JWT token or not?
The question you should ask yourself is
- Do I need to store the JWT token in database? If so, why?
The above question will not necessary solve your logout
issue because, you still need a mechanism to invalidate
a token that is stored or not stored in database
.
One of the benefits of not storing
the token in database is that you don't have to worry about deleting
them when (no maintenance or some clean up process)
- They token expire
- Their scopes changes
- The user's (in case of
password
flow, let's not cover otherflows
) roles and permissions are downgraded or upgraded in database and hence, what's inside jwt is outdated - The user is deleted
- The user logged out (Wonder if this is a good enough reason to delete token)
- The tokens is compromised (Tricky one)
- [Add other cases]
Verifying a token's validity?
I am sure you are using the verify
endpoint and it's purpose is to validate whether a token is valid
or not but, will not necessary check all the above scenarios which means you have to either
- Customize the
verify
workflow to add morecustom checks
OR - Before the token is being
verified
forsignature validity
,expiry time
, and some otherdefault checks
you can run your owncustom checks
and if yourcustom checks
passed then proceed otherwiseyou shall not pass!
What are your options then?
Well, besides blacklisting
you could do something as follow
Use in-memory store
Simply store the uniquely-identifying-metadata
of JWT token into redis as key and give it a expiry time
that is same as JWT
token expiry time so that it self-destruct
when the token is expired.
set key {replace_with_jwt_unique_identifier} ex {jwt_expiry_timestamp}
Risk: Redis is in-memory store and the entries are not persisted.
Use database
Don't use Redis or don't want to take the risk. You can use database with a custom database table. A separate table that is either
- Related to JWT record and has a
ON DELETE CASCADE
- Not-related to JWT record and you have to maintain it yourself
When a token is issued also populate this new database table.
Common Remaining steps
When a normal
request comes in with a JWT, use the JWT to query the in-memory
store or database
table to see if record exist.
In case of in-memory
store a simple existence
check is more than enough.
In case of database table
you need to do more checks (i.e. exist and not expired, etc.) and if the checks passed the let the request through, else you shall not pass!
When a logout request
comes in, in case of in-memory
store simply delete the key
and proceed (if found) and in case of database
you could delete the JWT
record which will cascade down to new table.
When to do the custom checks?
Well, you can do it
- First and fore-most using a custom top-level filter or
- You can expand the
verify
endpoint workflow to also do these extra checks
The projects that I work with does not require a token to be invalidated on logout
so I did not have to cross this bridge. I did have to expand the verify
endpoint to make sure a token is valid if all my custom checks
have passed.
Extra reading materials
In addition to tutorial that you pointed out. There are some other SO questions that also discuss a similar issue. See
What if JWT is stolen?
How to destroy JWT on logout?
More how to delete a JWT token?
How to invlidate JWT when password changed
Github issue - how to invalidate JWT
Finally the best for the last - Invalidating JWT Web Tokens
Solution 2
We faced the similar problem and we resolved it using following method -
- We add a request Id to every JWT token along with the expiration time.
- When session is created, we persist this request Id, in database, against other token values and its expiry.
- When server initiates logout, we mark this request as expired.
- Next time if the same token is used, we know for sure that this is an expired token by verifying the request Id.
Now, checking request Id in database is costly so we use an in-memory cache as well.
Solution 3
The advantage of JWT token is that it allows for a self-contained way to securely transmit information between parties as a JSON object. This information can be verified and trusted because it is digitally signed.
Since it is Self-contained, any of your resource server would be able to validate it without the need of making a DB look-up or accessing the Authorization Server.
Now if your requirement is to invalidate the token, my suggestion would be to explore on the JDBC TokenStore that Spring-Security offers. This way, all the tokens that are issued would be stored in a DB and you could invalidate them when user logs out of your application. For this, you can expose a service to revoke the token and invoke it appropriately
@Resource(name="tokenServices")
ConsumerTokenServices tokenServices;
@RequestMapping(method = RequestMethod.POST, value = "/tokens/revoke/{tokenId:.*}")
@ResponseBody
public String revokeToken(@PathVariable String tokenId) {
tokenServices.revokeToken(tokenId);
return tokenId;
}
All your resource server(s) will have to do a DB look-up to check for the validity of the token
pranav
Updated on July 03, 2022Comments
-
pranav almost 2 years
We are trying to follow this code base for our Spring REST based application. spring-boot-jwts
The issue is that we are not able to remove the JWT token during logout from server. When we checked on net we came to know only approach for this is Blacklisting the JWT tokens as given in this Blacklisting. Is it so?
We are bit new to token based authentication, please let us know if there's a solution like expiring the token on logout calls or something.
-
pranav about 6 yearsThanks for all the pointers. As the code has already used JWT as token generator, we have to add DB as well for token storage. During subsequent requests we match the JWT token with token in DB and pass only if they match.