Why JWTs Suck as Session Tokens

Why JWTs Suck as Session Tokens

Learn why you should never use JSON web tokens as session tokens (either in cookies or HTML local storage).

Brian Brewder

Good article. I also am not a big fan of using JWT for session state. I see a couple additional problems with it that you didn’t mention.

1. A basic rule-of-thumb I learned over the years is to never trust information coming from the client. There isn’t currently any known way to break JWT, but I still feel uncomfortable having the client tell me what their permissions are (there are several security systems that have been broken that people thought were secure at one point). The project I’m building is intended to be used for large construction projects ($100 million + not unusual), so there is ample reason to be paranoid :).

2. Security information changes. I don’t want a banned user to be able to continue using my application as long as their JWT token is still valid. Similar situation when reducing a users permissions. Adding permissions can be handled simply by forcing them to log back in if they want the enhanced permissions, but a better experience would be if the system simply adapted correctly.

I’ve recently started an ASP.Net Core API project and they seem to be forcing the use of JWT (I was unable to figure out how to use the Authorize system without them). This was a good opportunity for me to learn JWT, but it certainly is annoying to be forced to use a bloated authorization token just for simple session IDs :). I’m sure there must be a way, but I had run out of the time I gave it to resolve the issue. Hopefully I’ll be able to come back around to it at some point and maybe there will be some better documentation available by then.

Since the API is for a SPA, I did end up putting permissions into the JWT (I used a bitmask to keep the size down). Using JWTs this way has the double benefit of being able to use the JWT to support permissioning in the client (access menus items and such) and re-initializing the client when permissions change (by forcing them to log back in). I would prefer a simple session token, but I’ll use what I’ve got.

I think using cookies is becoming less common in modern SPA sites. The main problem with cookies is that they are sent on every request to the server instead of just the API calls. I’m using a BEARER Authorization header for the API instead of a cookie. Using a decent client-side http library like axios makes this easy to do (just set it once and it is included in all future requests). Of course, then you lose out on some of the security features in cookies supported by most browsers, such as server-only and SSL cookies.

Thanks for the article.

Randall Degges

I’m not super worried about #1, because as long as you are keeping your signing key secret on the server, you should be OK. But I understand what you mean =)

#2 is a legit issue. I think a lot of people don’t realize that by using JWTs they’re making the assumption that authorization data is OUTDATED BY DESIGN. There’s a BIG difference between using a JWT to effectively “cache” authorization data vs storing authorization data in a server side cache. The main difference is that doing the latter allows you to use the cache invalidation pattern to CONTROL your authorization data in a consistent manner so that you don’t run into edge cases where users are doing bad things with revoked permissions.

There are actually a wholeee lotta other reasons I didn’t include here about why JWTs suck. As I was working on this article it started turning into a small book so I cut it short and decided to formally write things down in a more long-form format. Still working on that =)

Thanks for the comment!

Kiran Challa

A nice informative article. Thank you!

Nkansah Rexford

Maybe this might be me, but I’m not sure why if I’m not building a client to server API system, I would ever use JWT.

Until I started using express js for API building, I’ve always loved the session mechanism of Django. Seamless and solid.

I can’t think of myself using Django for a not API structure and use JWT.


This make me think about, why I should care about 24MB even 30MB band width each month. if I already have 100k page views per month, and they are all members.

<br>Let’s say that your website gets roughly 100k page views per month. That means you’d be consuming an additional ~24MB of bandwidth each month. That doesn’t sound like a lot, but when you’re consistently bloating every single page request, all the little things start to add up.<br>

Randall Degges

Not 100% sure what you mean here. But ya – if you’re building a site and speed isn’t a concern (or bandwidth) then you should ignore that bit.

Martin Kosicky

Well… it’s higher security. It’s easier to steal your cookie (session id) than to get the JWT signing key… cause it never leaves the computer. Also it’s not a good practice to send permissions from client. You should send claims (who am I, my ID, birthday, department)… but it’s up to each service to check also if he has right to do the operation (after we know he is who he say he is… but does he have right to do it?) . By the claims you are supposed to identify the user… permisions should always be stored on server. I don’t think anybody would want to use it that way.

Randall Degges

Hmm, well, it isn’t easier to steal a cookie than a JWT – they’re both almost the same in that regard. Both cookies AND JWTs are cryptographically signed using a server-side key, so there’s no difference.

It’s also impossible to set permissions on the client, the server must generate both the tokens AND cookies either way.

In general: storing authorization data (claims) in any sort of token (JWT, cookie, or otherwise) is a bad idea as you’re basically saying you are going to trust (and cache) sensitive information that is likely to change.

Martin Kosicky

Its not the same. As cookies are sent at every request the server signing key is sent only once which reduces the risk. As there is no 100% security. Also you dont understand what are claims most likely. They shouldnt be sensiive data. They should be trusted data which you can should use to further decide if you can do an operation… if the claim has a user id you should hit the db to check if you have permision to do something. You shouldnt store what the user has access to as you should never trust the client with his permissions. Claims can be data in your driving license… but the fact that you have been busted 2 times for speeding can be looked up by your driver id, its not part of claims.

Randall Degges

I think the part missing here (which is one of the points in the article and in my talk) is that it’s a lot easier to steal JWTs than server side cookies because XSS is so prevalent: https://speakerdeck.com/rde…

The signing key is never sent over the network.

The idea of claims is that you want to use them to cache authorization data usually – which I think is what you’re saying here.

For instance, in a JWT you might include a claim called admin with a value of true. This way, when a client makes an authenticated request back to the API service using the token, the server will validate the token isn’t expired and that it has a signature, then it will parse the claims out of the token and make decisions on them WITHOUT consulting a central DB (this is the stateless bit).

That is the problem (the above part) because your server is now trusting cached (old) authorization data that has been included in claims. There’s a ton of other reasons why which I cover in my talk: https://www.youtube.com/wat…

Martin Kosicky

Eh… dont ever send claims admin:true :D… you are supposed to use jwt to do authentication… NOT authorization. The difference is important!

anyway ur right…i forgot that in jwt only the claims and header part is signed. Not the entire message.ok my bad :D. So yes it is easy to steal cookie as a jwt. Unless you put an expiration in the claims payload which is similiar to timeouting cookies. But not hitting the database to perform authentification is a killer feature for me.

Randall Degges

That is again the same problem as with authorization =p

You never want to cache authentication OR authorization data. In either case it is bad. EG: What happens if the user’s account is disabled server-side? If you’re not checking against a database the user can still authenticate and do stuff… Which is a huge security issue.

Martin Kosicky

If you are authenticated and all your rights have been revoked you cant do anything. When they freeze your bank account you have the claims credit card… but the bank has to do authorization… another level of processing. There is nothing wrong with caching authentication data. Its a firtst level of defense that kills off 99% of problems. You still have to do authorization which cannot be cached as you say

Lourival Almeida

Good article and good points, but we have to see the big picture.

In fact, I cannot see the need for JWT in a cenario where we have just one server, no clusterization, no single sign-on requirements.

The other way around, JWT came to help solving this kind of problems, where a user can be served by any node on your cloud, scalability problems, single sign-on needs, microservices and so on. That’s the cenario where it is a good solution. A stateful solution (sessions) is terrible for multiple servers as the state must be replicated in each server raising some good problems to solve.


Randall Degges

Hey – I disagree completely. JWTs were not originally designed for this purpose – they were designed to pass signed data around between untrusted parties.

The way they’re used today as a cache of sensitive data is not their intended purpose, and regardless of what type of large site you build, centralizing is absolutely required. This is what every large company does (Google, FB, etc.) – they have to.

Let’s say you have a token and you revoke it due to a user logout or any other action, you can’t very well keep honor a token until its expiration has been reached, that would cause havoc. You have to centralize the tokens and build a revocation list of some sort, which means now all requests need to go back through a central point to verify their validity.

Simon Weaver

I think he means that this is a poor excuse and likely to be negligible compared to anything else going on with your bandwidth / cpu.

Vinay Rajput

Great Article, I am looking into JWT first time, and got very clear view of point about how to use JWT, Thumbs Up for you @Randall Degges@rdegges

Igor Savin

(sorry, posted in a wrong level)

Igor Savin

Claim that Node.js is going to be bottle-necked by cryptographic operations is grossly inaccurate. Any proper cryptographic library in Node.js works asynchronously and is going to be multi-threaded just fine.

See this: https://itnext.io/multi-thr…