How to invalidate a JWT using a blacklist (2024)

How to invalidate a JWT using a blacklist (1)

Tosin Moronfolu

Posted on • Updated on

How to invalidate a JWT using a blacklist (3) How to invalidate a JWT using a blacklist (4) How to invalidate a JWT using a blacklist (5) How to invalidate a JWT using a blacklist (6) How to invalidate a JWT using a blacklist (7)

#javascript #node #jwt #webdev

This article is going to show you how to invalidate JWTs using the token blacklist method. The token blacklist method is used when creating a logout system. This is one of the ways of invalidating JWTs on logout request.

One of the main properties of JWT is that it's stateless and is stored on the client and not in the Database. You don't have to query the database to validate the token. For as long as the signature is correct and the token hasn't expired, it would allow the user to access the restricted resource. This is most efficient when you wish to reduce the load on the database. The downside, however, is that it makes invalidating the existing, non-expired token difficult.

Why blacklist?

One reason you would need to invalidate a token is when you're creating a logout system, and JWT is used as your authentication method. Creating a blacklist is one of the various ways to invalidate a token. The logic behind it is straight forward and easy to understand and implement.

A JWT can still be valid even after it has been deleted from the client, depending on the expiration date of the token. So, invalidating it makes sure it's not being used again for authentication purposes.
If the lifetime of the token is short, it might not be an issue. All the same, you can still create a blacklist if you wish.

Creating a blacklist

  1. When your web-server receives a logout request, take the token and store it in an in-memory database, like Redis. We are using this because of speed and efficiency, as you don't want to hit your main database every time someone wants to logout. Also, you don't have to store a bunch of invalidated tokens in your database. Take a look at my approach below;

First, create a middleware to verify the token:

const verifyToken = (request, response, next) => {// Take the token from the Authorization header const token = request.header('Authorization').replace('Bearer ', ''); if (!token) { response.status(403).send({ message: 'No token provided!', }); }// Verify the token jwt.verify(token, config.secret, (error, decoded) => { if (error) { return response.status(401).send({ status: 'error', message: error.message, }); }// Append the parameters to the request object request.userId = decoded.id; request.tokenExp = decoded.exp; request.token = token; next(); });};

Then,

// This is a NodeJs example. The logic can be replicated in any language or framework.// 1. The server recieves a logout request// 2. The verifyToken middleware checks and makes sure the token in the request object is validrouter.post('/logout', verifyToken, (request, response) => {// 3. take out the userId and toekn from the request const { userId, token } = request;// 4. use the get method provided by redis to check with the userId to see if the user exists in the blacklist redisClient.get(userId, (error, data) => { if (error) { response.send({ error }); }// 5. if the user is on the blacklist, add the new token // from the request object to the list of // token under this user that has been invalidated./*The blacklist is saved in the format => "userId": [token1, token2,...]redis doesn't accept obejcts, so you'd have to stringify it before adding */ if (data !== null) { const parsedData = JSON.parse(data); parsedData[userId].push(token); redisClient.setex(userId, 3600, JSON.stringify(parsedData)); return response.send({ status: 'success', message: 'Logout successful', }); }// 6. if the user isn't on the blacklist yet, add the user the token // and on subsequent requests to the logout route the user // will be found and the token will be appended to the already existing list. const blacklistData = { [userId]: [token], }; redisClient.setex(userId, 3600, JSON.stringify(blacklistData)); return response.send({ status: 'success', message: 'Logout successful', }); });});

  1. Then, for every request that requires that the user is authenticated, you would check the in-memory database to check if the token has been invalidated or not. Then, send a response based on the result from the check. Take a look at my approach below;
module.exports = (request, response, next) => {// 1. take out the userId and toekn from the request const { userId, token } = request;// 2. Check redis if the user exists  redisClient.get(userId, (error, data) => { if (error) { return response.status(400).send({ error }); }// 3. if so, check if the token provided in the request has been blacklisted. If so, redirect or send a response else move on with the request. if (data !== null) { const parsedData = JSON.parse(data); if (parsedData[userId].includes(token)) { return response.send({ message: 'You have to login!', }); } return next(); } });};

To make the search more efficient, you could remove tokens from the blacklist which have already expired. To do this, we would follow the series of steps below:

  1. verify the authenticity of the token
  2. If successfully verified, append the userId, the token itself and its expiration date to the request object.
  3. Store the token in Redis with the expiration date of the token itself.
 // 1. The server receives a logout request // 2. The verifyToken middleware checks  // and makes sure the token in the request  // object is valid and it appends it to the request object,  // as well as the token expiration date router.post('/logout', verifyToken, (request, response) => { // 3. take out the userId, token and tokenExp from the request const { userId, token, tokenExp } = request; /** 4. use the set method provided by Redis to insert the token Note: the format being used is to combine 'blacklist_' as a prefix to the token and use it as the key and a boolean, true, as the value. We also set the expiration time for the key in Redis to the same expiration time of the token itself as stated above **/ redisClient.setex(`blacklist_${token}`, tokenExp, true); // return the response return response.send({ status: 'success', message: 'Logout successful', }); });

Then, for every request that requires that the user is authenticated, you would need to check your in-memory database to see if the token has been invalidated or not and then send a response based on the result from the check. Take a look at my approach below.

module.exports = (request, response, next) => {// 1. take out the token from the request const { token } = request;// 2. Check Redis if the token exists. If so, redirect or send a response else move on with the request. redisClient.get(`blacklist_${token}`, (error, data) => { if (error) { return response.status(400).send({ error }); } if (data !== null) { return response.send({ message: 'You have to login!', }); }// 3. If not, move on with the request. return next(); });};

Conclusion

This is one of the various ways to invalidate a token. I personally use this approach and it works efficiently. I would like to know your thoughts in the comments.

Thank you for reading, cheers.

Top comments (25)

Subscribe

Bartosz Pietrucha

Bartosz Pietrucha

MSc Computer Science, a full-stack engineer, running websecurity-academy.com 🔑📺

  • Joined

Mar 9 '21

  • Copy link

If you are using JWT, you want your authorization system to be stateless, right? 🙂. When you introduce blacklisting, you make your authorization stateful! What sense does it make?

This ends up maintaining the list of "logged out", so why not maintain the list of "logged in" and DO NOT use a self-contained token (that exposes the content to anyone), but an opaque token (like session-id) and manage the session on the server. Server-side sessions are by design more secure and logging out isn't any problem.

TL; DR: Blacklisting stateful tokens does not make sense (despite the hype around JWT and cool blacklisting "technique", which probably is fun in developing 🤷‍♂️).

Phil Ashby

Phil Ashby

30+ years of tech, retired from an identity intelligence company, now part-time with an insurance broker.Dev community mod - mostly light gardening & weeding out spam :)

  • Location

    Felixstowe, UK

  • Education

    M.Eng (hons) Electronics, MIET, MBCS, AMCIISec, CEH

  • Pronouns

    he/him

  • Work

    Willis Towers Watson (aka WTW) part-time.

  • Joined

Mar 9 '21

  • Copy link

Good question - from work I did designing a JWT based authorisation system, we concluded that a store of invalidated but within expiry tokens would be several orders of magnitude smaller than a list of valid, in use tokens, so this significantly reduces the work/load/networking required to provide token safety through a stateful revocation mechanism.

In our case we were happy to accept the risk of a transient session token being used at any point within it's issued lifetime (an order of minutes), but wanted to revoke long-term tokens used for API keys (lifetime of months) within a similar order of minutes. We chose to publish revoked token identifiers (via their unique jti field) internally to consuming services at the same time as we published the JWT signing keys, by extending the OpenID connect metadata documents that all services regularly collect (order of minutes) from our source of truth / authentication database. Auth0 blogged a similar approach a number of years ago that was our inspiration: auth0.com/blog/denylist-json-web-t...

Bartosz Pietrucha

Bartosz Pietrucha

MSc Computer Science, a full-stack engineer, running websecurity-academy.com 🔑📺

  • Joined

Mar 10 '21

  • Copy link

Hi there! So you were maintaining a "small" list of invalidated tokens that still hadn't expired? If yes, did this approach include periodical scanning for expired tokens? Was this really advantageous compared to regular sessions with opaque tokens?

Phil Ashby

Phil Ashby

30+ years of tech, retired from an identity intelligence company, now part-time with an insurance broker.Dev community mod - mostly light gardening & weeding out spam :)

  • Location

    Felixstowe, UK

  • Education

    M.Eng (hons) Electronics, MIET, MBCS, AMCIISec, CEH

  • Pronouns

    he/him

  • Work

    Willis Towers Watson (aka WTW) part-time.

  • Joined

Mar 10 '21

  • Copy link

Yes, the list was 10s of token IDs, owned and updated (hourly IIRC) by an authentication service, and critically, published as a static JSON file to a global CDN. Advantages are: zero coupling between global systems and authentication service (unlike opaque tokens that require a much more coupled global store); very little coupling between the many autonomous global development teams consuming this information in their own services (this was important for our 1000+ people, multi-company global org!).

Bartosz Pietrucha

Bartosz Pietrucha

MSc Computer Science, a full-stack engineer, running websecurity-academy.com 🔑📺

  • Joined

Mar 10 '21

  • Copy link

Interesting! What if these global development teams didn't check against this JSON file? Just thinking aloud about the practical perspective. Was there any mandate on this?

Phil Ashby

Phil Ashby

30+ years of tech, retired from an identity intelligence company, now part-time with an insurance broker.Dev community mod - mostly light gardening & weeding out spam :)

  • Location

    Felixstowe, UK

  • Education

    M.Eng (hons) Electronics, MIET, MBCS, AMCIISec, CEH

  • Pronouns

    he/him

  • Work

    Willis Towers Watson (aka WTW) part-time.

  • Joined

Mar 10 '21

  • Copy link

There was a mandate, as part of the global common user management function (which included a set of acceptance tests that had to pass).

Bartosz Pietrucha

Bartosz Pietrucha

MSc Computer Science, a full-stack engineer, running websecurity-academy.com 🔑📺

  • Joined

Mar 10 '21 • Edited on Mar 10 • Edited

  • Copy link

Interesting case. So this was implemented for long-lived API tokens (order of months)? This must have been a very detailed design process for such an architecture, haven't been?

I believe you had scalability challenges to tackle! Just curious: standard OAuth with rotating refresh tokens was not feasible?

Was the ratio between active long-lived API tokens (many) and invalidated ones (few) one of the deciding factors?

Phil Ashby

Phil Ashby

30+ years of tech, retired from an identity intelligence company, now part-time with an insurance broker.Dev community mod - mostly light gardening & weeding out spam :)

  • Location

    Felixstowe, UK

  • Education

    M.Eng (hons) Electronics, MIET, MBCS, AMCIISec, CEH

  • Pronouns

    he/him

  • Work

    Willis Towers Watson (aka WTW) part-time.

  • Joined

Mar 11 '21

  • Copy link

Yes, we looked at the risk of accepting tokens over different timescales, and concluded that only API keys were a material risk to us (use outside of contract, reputation loss), most of the risk of shorter term token misuse was carried by our customers as it would be their account that got billed if they leaked a token. I should note that the majority of our customers (80%+) used our API integration, not the browser-based UI (for which we had standard OAuth with rotating session tokens and refresh tokens with lifetimes on the order of a few days).

At the time I retired, we were handling ~1billion API calls a day globally.

Bartosz Pietrucha

Bartosz Pietrucha

MSc Computer Science, a full-stack engineer, running websecurity-academy.com 🔑📺

  • Joined

Mar 11 '21

  • Copy link

Great use case! What a scale!

Tosin Moronfolu

Tosin Moronfolu

Code is my caffeine

  • Email

  • Location

    Lagos, Nigeria

  • Work

    Backend Developer

  • Joined

Mar 10 '21

  • Copy link

Amazing, would definitely look into. Thank you!

Tosin Moronfolu

Tosin Moronfolu

Code is my caffeine

  • Email

  • Location

    Lagos, Nigeria

  • Work

    Backend Developer

  • Joined

Mar 9 '21

  • Copy link

Thank you for your feedback. You have a point, I use sessions also and it works as you've said. There are many ways to go about things, that's how code works, there isn't one way to it. I'm just sharing my knowledge, I didn't say this is the best way or most secure to go about it. Sessions have their flaws as do JWTs, it's just another way. You have your opinion and I'm happy you shared it. Thank you again.

Bartosz Pietrucha

Bartosz Pietrucha

MSc Computer Science, a full-stack engineer, running websecurity-academy.com 🔑📺

  • Joined

Mar 9 '21

  • Copy link

What are the flaws of sessions (in comparison to this "JWT blacklisting")? I am not sure I understand your point.

Meat Boy

Meat Boy

AWS, Web & Mobile Dev

  • Work

    Software Engineer

  • Joined

Mar 9 '21

  • Copy link

Great article :) JWT is an awesome topic.
Protip: you can use pub/sub model of Redis to notify the app about new tokens. However, the main JWT has to be stateless like you mention and possible to verify without additional calls so a better approach is to blacklist refresh tokens and make general token live very short.

Tosin Moronfolu

Tosin Moronfolu

Code is my caffeine

  • Email

  • Location

    Lagos, Nigeria

  • Work

    Backend Developer

  • Joined

Mar 9 '21

  • Copy link

True, this is another way to go about it. Thank you for the feedback

Anthony Domingue

Anthony Domingue

  • Joined

Mar 10 '21 • Edited on Mar 15 • Edited

  • Copy link

I think it is not a good idea to add a JWT token to a denylist as it is encoded in base64... You can add "==" to the end of your token to bypass the denylist check and login.

Tosin Moronfolu

Tosin Moronfolu

Code is my caffeine

  • Email

  • Location

    Lagos, Nigeria

  • Work

    Backend Developer

  • Joined

Mar 10 '21

  • Copy link

That's if the user somehow gets access to the token and sends it with a request, which is very hard expect they're some hacker or something. And adding extra characters to the end of the token will make sure it's invalid and the verify token middleware checks for that.

If I didn't get you right, please explain.

Anthony Domingue

Anthony Domingue

  • Joined

Mar 10 '21 • Edited on Mar 10 • Edited

  • Copy link

The token is in the header "Authorization" right ? Any user can update the Authorization token in the request. Maybe it's not easy for everyone but it is possible.
JWT is encoded in base64, but in base64 you can add padding with "=" without changing the encoded message. "Hi dev.to !" in base64 is "SGkgZGV2LnRvICE=" but also "SGkgZGV2LnRvICE==" or "SGkgZGV2LnRvICE===".
So if you check the encoded token in the denylist you can just add "=" at the end of the token to bypass the denylist and use the token without changing the decoded value.
Here is the RFC for the base64: tools.ietf.org/html/rfc4648

Tosin Moronfolu

Tosin Moronfolu

Code is my caffeine

  • Email

  • Location

    Lagos, Nigeria

  • Work

    Backend Developer

  • Joined

Mar 10 '21

  • Copy link

Oh wow, didn't realize this. Thank you for sharing. Will check it out.

Phil Ashby

Phil Ashby

30+ years of tech, retired from an identity intelligence company, now part-time with an insurance broker.Dev community mod - mostly light gardening & weeding out spam :)

  • Location

    Felixstowe, UK

  • Education

    M.Eng (hons) Electronics, MIET, MBCS, AMCIISec, CEH

  • Pronouns

    he/him

  • Work

    Willis Towers Watson (aka WTW) part-time.

  • Joined

Mar 11 '21

  • Copy link

This is why our design revoked tokens via their jti field, which is not changeable provided the tokens are correctly signed (with an RSA or elliptic curve key pair). it does require all tokens to be parsed, but we can delegate that to a trusted library that should be resistant to attack...

Jyotirmaya Sahu

Jyotirmaya Sahu

A basic developer with not so basic learning affinity. Learn to Grow. Teach to Learn.

  • Location

    Rourkela, Odisha, India

  • Work

    Application Development Analyst at Accenture

  • Joined

Mar 9 '21 • Edited on Mar 9 • Edited

  • Copy link

But then we need to scan the redis store at some intervals to ensure removal of expired tokens.

Tosin Moronfolu

Tosin Moronfolu

Code is my caffeine

  • Email

  • Location

    Lagos, Nigeria

  • Work

    Backend Developer

  • Joined

Mar 9 '21

  • Copy link

You could If you want to, but it would be redundant as the expiry date works automatically to ensure it is removed at the set date. Since the expiry date is the same as the one on the token itself, I don't think there is need to check at intervals anymore

Jyotirmaya Sahu

Jyotirmaya Sahu

A basic developer with not so basic learning affinity. Learn to Grow. Teach to Learn.

  • Location

    Rourkela, Odisha, India

  • Work

    Application Development Analyst at Accenture

  • Joined

Mar 9 '21

  • Copy link

Yes, correct. But, my point is the expired tokens would pile up eventually consuming a significant part of the store memory at some point of time.

Tosin Moronfolu

Tosin Moronfolu

Code is my caffeine

  • Email

  • Location

    Lagos, Nigeria

  • Work

    Backend Developer

  • Joined

Mar 9 '21

  • Copy link

True, thank you for the feedback.

Phil Ashby

Phil Ashby

30+ years of tech, retired from an identity intelligence company, now part-time with an insurance broker.Dev community mod - mostly light gardening & weeding out spam :)

  • Location

    Felixstowe, UK

  • Education

    M.Eng (hons) Electronics, MIET, MBCS, AMCIISec, CEH

  • Pronouns

    he/him

  • Work

    Willis Towers Watson (aka WTW) part-time.

  • Joined

Mar 9 '21

  • Copy link

Maybe choose a shared/distributed store that supports automated expiry of records (eg: MongoDB, Zookeeper, etc.), or can execute scheduled jobs (yes, SQLserver could be the right answer :))

Tosin Moronfolu

Tosin Moronfolu

Code is my caffeine

  • Email

  • Location

    Lagos, Nigeria

  • Work

    Backend Developer

  • Joined

Mar 10 '21

  • Copy link

Thank you for this, I appreciate it!

View full discussion (25 comments)

For further actions, you may consider blocking this person and/or reporting abuse

How to invalidate a JWT using a blacklist (2024)
Top Articles
Analysis Of Three Farm Bill
...
Best Pizza Novato
The Largest Banks - ​​How to Transfer Money With Only Card Number and CVV (2024)
Best Team In 2K23 Myteam
What happened to Lori Petty? What is she doing today? Wiki
Die Windows GDI+ (Teil 1)
Kristine Leahy Spouse
Stl Craiglist
Mr Tire Rockland Maine
Craigslist Dog Kennels For Sale
Curtains - Cheap Ready Made Curtains - Deconovo UK
DBZ Dokkan Battle Full-Power Tier List [All Cards Ranked]
Zoe Mintz Adam Duritz
Recap: Noah Syndergaard earns his first L.A. win as Dodgers sweep Cardinals
Nurse Logic 2.0 Testing And Remediation Advanced Test
Georgetown 10 Day Weather
Mail.zsthost Change Password
Somewhere In Queens Showtimes Near The Maple Theater
Dulce
Reborn Rich Kissasian
Craigslist Houses For Rent In Milan Tennessee
Providence Medical Group-West Hills Primary Care
Anotherdeadfairy
Www Va Lottery Com Result
Litter Robot 3 RED SOLID LIGHT
Ihub Fnma Message Board
Walgreens On Bingle And Long Point
R/Airforcerecruits
Elijah Streams Videos
Lininii
Filmy Met
Verizon TV and Internet Packages
About | Swan Medical Group
Puerto Rico Pictures and Facts
Diana Lolalytics
Selfservice Bright Lending
Colorado Parks And Wildlife Reissue List
Oreillys Federal And Evans
Solemn Behavior Antonym
Pawn Shop Open Now
Tsbarbiespanishxxl
Oppenheimer Showtimes Near B&B Theatres Liberty Cinema 12
At Home Hourly Pay
Umd Men's Basketball Duluth
56X40X25Cm
Conan Exiles Tiger Cub Best Food
Germany’s intensely private and immensely wealthy Reimann family
Quest Diagnostics Mt Morris Appointment
Gear Bicycle Sales Butler Pa
Jovan Pulitzer Telegram
Latest Posts
Article information

Author: Dan Stracke

Last Updated:

Views: 6235

Rating: 4.2 / 5 (63 voted)

Reviews: 86% of readers found this page helpful

Author information

Name: Dan Stracke

Birthday: 1992-08-25

Address: 2253 Brown Springs, East Alla, OH 38634-0309

Phone: +398735162064

Job: Investor Government Associate

Hobby: Shopping, LARPing, Scrapbooking, Surfing, Slacklining, Dance, Glassblowing

Introduction: My name is Dan Stracke, I am a homely, gleaming, glamorous, inquisitive, homely, gorgeous, light person who loves writing and wants to share my knowledge and understanding with you.