Skip to content

Instantly share code, notes, and snippets.

@samsch
Last active November 5, 2025 21:39
Show Gist options
  • Select an option

  • Save samsch/0d1f3d3b4745d778f78b230cf6061452 to your computer and use it in GitHub Desktop.

Select an option

Save samsch/0d1f3d3b4745d778f78b230cf6061452 to your computer and use it in GitHub Desktop.
Stop using JWTs

Stop using JWTs!

TLDR: JWTs should not be used for keeping your user logged in. They are not designed for this purpose, they are not secure, and there is a much better tool which is designed for it: regular cookie sessions.

If you've got a bit of time to watch a presentation on it, I highly recommend this talk: https://www.youtube.com/watch?v=pYeekwv3vC4 (Note that other topics are largely skimmed over, such as CSRF protection. You should learn about other topics from other sources. Also note that "valid" usecases for JWTs at the end of the video can also be easily handled by other, better, and more secure tools. Specifically, PASETO.)

A related topic: Don't use localStorage (or sessionStorage) for authentication credentials, including JWT tokens: https://www.rdegges.com/2018/please-stop-using-local-storage/

The reason to avoid JWTs comes down to a couple different points:

  • The JWT specification is specifically designed only for very short-live tokens (~5 minute or less). Sessions need to have longer lifespans than that.
  • "stateless" authentication simply is not feasible in a secure way. You must have some state to handle tokens securely, and if you must have a data store, it's better to just store all the data. Most of this article and the followup it links to describes the specific issues: http://cryto.net/~joepie91/blog/2016/06/13/stop-using-jwt-for-sessions/
    • (Yes, people are doing it, and yes, their applications are flawed, and you should not repeat that mistake.)
  • JWTs which just store a simple session token are inefficient and less flexible than a regular session cookie, and don't gain you any advantage.
  • The JWT specification itself is not trusted by security experts. This should preclude all usage of them for anything related to security and authentication. The original spec specifically made it possible to create fake tokens, and is likely to contain other mistakes. This article delves deeper into the problems with the JWT (family) specification.

Rebuttals

But Google uses JWTs! Google does not use JWTs for user sessions in the browser. They use regular cookie sessions. JWTs are used purely as Single Sign On transports so that your login session on one server or host can be transferred to a session on another server or host. This is within the reasonable usecases for JWTs, and Google has the resources (security experts) to create and maintain a more secure JWT implementation. Their JWTs are effectively not the same as anyone else's.

But stateless is better! You can't securely have truly stateless authentication without having massive resources, see the cryto.net link above. Also, Stateless is a lie.

I don't know how to setup sessions! You don't regularly see articles explaining sessions because the technology isn't particularly new. You also shouldn't need third party information for setup. A session implementation's documentation should take you through the setup process by itself. Almost any web server framework will contain an implementation for sessions, and usually it's very easy to enable if it isn't enabled by default. Express and other Node.js frameworks are somewhat exceptions to this rule, primarily because they are highly modular and single purpose. For Express, you simply use the express-session middleware and a store connector which works with your store (I recommend connect-session-knex, to be used with Postgres, MySQL, or possibly SQLite).

Short term tokens

If you do need a short-lived, signed token for something, there is a better spec called PASETO which is designed to be secure. Just make sure you aren't using them for sessions.

How sessions work

I recommend checking out this gist by joepie91 to learn more how sessions work.

@seiwonpark
Copy link

seiwonpark commented Mar 17, 2023

Thank you for sharing this article. And here's the reason for Google does not use JWTs for user sessions in the browser. They use regular cookie sessions on its Security section

For example, cookies called ‘SID’ and ‘HSID’ contain digitally signed and encrypted records of a user’s Google Account ID and most recent sign-in time. The combination of these cookies allows Google to block many types of attack, such as attempts to steal the content of forms submitted in Google services.

@omidp
Copy link

omidp commented Apr 2, 2023

As far as I can remember I stopped using sessions because it was hard to scale. clustering your webserver is cumbersome even when you are lucky enough that your webserver implements it right that's why the extra layer of complexity of adding a distributed cache is required in large scale app. I prefer to use Hazelcast or Redis.

Cookie sessions and local storage also have their own downsides.

You don't have to put sensitive info in JWT. You can put an identifier / user id in a JWT token and retrieve those data from cache by using that identifier.

I wouldn't say Stop using JWT, I would say use it in a right way.

@samsch
Copy link
Author

samsch commented Apr 3, 2023

@omidp Refer here: http://cryto.net/%7Ejoepie91/blog/2016/06/19/stop-using-jwt-for-sessions-part-2-why-your-solution-doesnt-work/

If you can retrieve your user from the cache, you can retrieve a session for the same (probably cheaper, actually) "cost".

There is no right way to use JWTs for sessions. Like, it's not an opinion, it can be objectively and logically shown.

@dragosstancu
Copy link

funny how cryto.net is over http :)

@LrsK
Copy link

LrsK commented Apr 23, 2024

@Dev-Dul
Copy link

Dev-Dul commented Jun 8, 2025

My main issue with JWTs is just how manual they make everything, you literally have to do everything yourself. You have to issue access tokens, refresh token, validate this tokens, all while making sure your client sends its access token with every request and don't forget that you also have to tell it when where and how to send its refresh token to get a new access token. You don't get this headache from sessions.

@mindplay-dk
Copy link

JWTs should not be used for keeping your user logged in.

Thank you for maintaining this page, I've come back to this issue dozens of times over the years.

Could you maybe say something about what JWT's are for?

This page, and that "Stop using JWT for sessions, part 2" article with the diagram are great explaining why you shouldn't use them for sessions - but maybe some people would have an easier time getting the point, if you could also provide examples of when JWTs could be used?

I'm not a security expert, so to the best of my understanding, these tokens were mainly designed as a single sign-on solution, is that right? Like, "authenticate here, sign in there"? So you can log in to 100 different Google products in one place, but the JWT you receive is very short-lived and is literally just used the moment after you sign in, to pass to another product/server to prove that you signed in, and then from there on our your session with that product is a regular session-cookie. Do I have it right? 😅

@vcarl
Copy link

vcarl commented Oct 16, 2025

Close, your browser/client makes requests with a session identifier, the details of which get verified by the auth server. That verification returns a JWT, which can be inspected by internal services to verify that it was made with valid credentials. So your client maintains a classic session cookie (or other secure storage if not a browser), and the auth portion of your backed validates it and issues a JWT that can be statically verified from as other parts of the large and complex backend system are called on to complete the request.

The problem this solves is, if your request fans out into a bunch of calls to internal services, validating the identity of the requestor would require each of those requests to check for a valid session in the auth server. The JWT is used instead of calling back to the auth server to verify the contents of the session db, and since that proof only needs to last as long as the request takes to process, it can have a short lifetime.

@mindplay-dk
Copy link

The JWT is used instead of calling back to the auth server to verify the contents of the session db, and since that proof only needs to last as long as the request takes to process, it can have a short lifetime.

@vcarl so it's really more of an optimization for microservices than anything else? like, if a client (say, the youtube player) needs to go to 5 different services for all the bits and pieces that make up your personalized home page, it first obtains a short-lived JWT, and then submits that with every request to 5 different servers? which then don't need to reach out to a central auth server - so you validate once for a "group" of requests, likely related to the same operation, instead of each server needing to do it individually?

I can understand why this gets confusing, since really the only difference between that and what most people are doing, is the JWTs are very short-lived and reissued more frequently? 🤔

I mean, there's no reason you couldn't do this in a system with just one service, but it wouldn't really be an optimization. Unless maybe requests to that one service are super frequent, and you're happy with, say, auth happening every minute instead of every second?

But it's not going to make much sense in a monolith with a single backend, I guess? It would have the session store right there in the same DB as whatever you're requesting, so in that case, it's likely just useless complexity?

@samsch
Copy link
Author

samsch commented Oct 19, 2025 via email

@mindplay-dk
Copy link

in vcarl's example, the JWT isn't issued to the client. the client makes all its requests to a single service that authenticates the client using the session ID from the cookie, and then that server makes internal requests using a JWT to the other various servers to create the response.

yeah, that does sound extra pointless - though rereading his response, I can't tell if that's what he meant.

if you really want to get this message fully across to the world, I think sequence diagrams might be the safe bet? show examples of precise scenarios of The Good, The Bad and the Pointless™ - these things are way harder to precisely describe or take in as prose. 😅

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment