DEV Community

Masa Kudamatsu
Masa Kudamatsu

Posted on • Edited on • Originally published at Medium

JSON web tokens are NOT meant for authenticating the same user repeatedly: Use session tokens instead

Update on 17 April, 2021: Don't miss checking out all those comments posted to this article. They are all insightful, more than this article itself. I thank those of you who took time to write their insights. I'm now learning more about web security, to be able to digest these comments.

There's a countless number of articles across the web that compare session tokens and JSON web tokens (JWTs) as a method of user authentication. It's really confusing for someone who is trying to create his/her first full-stack web app (i.e. me myself). No one clearly explains pros and cons of each. (Maybe no one knows for sure).

Spending several days doing research on the topic, I've come to a conclusion that I will go for session tokens, because JWTs are not meant to be used for repeatedly authenticating the same user.

In a week's time, however, I'm sure I will forget why I've come to this conclusion. This topic is really complicated. This article is a memorandum for my future self and hopefully for you as well.

Disclaimer: I'm not a web security expert. What's written below may not be fully correct, although I do my best to get everything right. Post a comment if you think I'm wrong. I just want to know the truth.

For the uninitiated

I'll skip describing what session tokens and JWTs are. There are great explanations already out there.

For session tokens, I recommend reading Kamani (2017), who provides the most concise introduction to session tokens I've seen (and why cookies are necessary for them).

For JWTs, have a look at Calandra (2019), who very well explains the context in which JWTs were born.

To compare session tokens and JWTs on a level playing field, we first need to be clear about one thing about JWTs:

Don't store JWTs in localStorage

Many tutorials on JWTs that I've seen demonstrate how a front end app can store JWTs in localStorage, and these tutorials always attract comments saying, "Don't store JWTs in localStorage, which is susceptible to cross-site scripting (XSS) attacks". I find Chenkie (2020a) extremely clear about what these comments really mean, with a rare demonstration of how XSS attacks can be done. (It requires a sign up (free of charge), but this 2.5-minute video tutorial is really worth watching.)

So JWTs should be stored in a cookie, just like session tokens need to be stored in a cookie. Cookies are susceptible to another kind of attacks known as cross-site request forgery (CSRF), but we can deal with it (at least partially, if I understand correctly) by using a library like csurf (see Chenkie 2020b for detail).

NOTE: Vladimir and Gopal (2019) advocate storing JWTs in memory, which is neither susceptible to XSS nor CSRF. Maybe this approach is worth checking out.

So the relevant comparison for us is "the session tokens stored in a cookie" versus "the JWTs stored in a cookie".

Cons of session tokens

The major drawback of session tokens is that the logged-in users will get kicked out when either a different server deals with their request (when an app is served by several servers) or the same sever gets restarted (for fixing bugs etc.), because whether or not the user is authenticated is kept in a particular server. For detail, see Calandra (2019) for example.

This problem can be solved by using a "session store" library such as connect-redis (see express-session's documentation for the list of session store libraries for Express.js). The idea is to keep session tokens in a dedicated database, and each time the user makes an HTTP request with their session token, the server receiving the request will consult this database to learn whether the request comes from the authenticated user or not. Performance suffers consequently, as retrieving data from a database always takes some time.

Alt TextUsing session tokens requires a Redis or a database. Image source: Awad (2019)

JWTs solve this issue by making a server "stateless": the user’s authentication status is embedded in the token so the server doesn’t need to remember about it. Another server or a restarted server can tell whether the user is authenticated or not, by decoding the JWT sent along with each HTTP request. So the logged-in user won’t get kicked out. We don't need any database to store user authentication status. Performance won't suffer consequently.

Cons of JWTs

The downside of JWTs, however, is that the server, once issuing a JWT, cannot control when the token expires. Even after the user has logged out, an app cannot kill it with the server code or with the front-end code (as JWTs should be stored in a HTTP-only cookie for security).

The longer the token lives, the higher the risk of getting it stolen. So we want to set a JWT to expire in a short period of time, say, 15 minutes. Then the user experience suffers: each user will be forced to log out while using the app every 15 minute. That's terrible.

Extending its expiration date (i.e. refreshing the token) requires a "refresh token" to be issued to the user as well as being stored in a database. When the token expires, the server will check the refresh token sent with a HTTP request and verify it against the one stored in the database.
Alt TextJSON web tokens require a Redis or a database for storing refresh tokens. Image: adapted from Awad (2019)

This sounds familiar... Yes, it's the same as session tokens, which need to be stored in a database for different servers to verity the user authentication status. JWTs should have been a solution to this problem, but now we're back to the same situation: accessing database to verify the user authentication status. And it's more complicated: we're now not checking the token itself, but checking the token issued to refresh the original token... This is why Awad (2019) says he uses session tokens, not JWTs, for a simple web app.

On the other hand, session tokens can be destroyed in the server code, say, when the user logs out. Extending the validity of sessions is straightforward, just by setting the rolling option to be true with express-session.

Summing up

So the major benefit of using JWTs, improving performance by ditching the database of session tokens, is completely overturned by the need of a refresh token database to avoid forcing the user to log out every now and then. Well, not completely, to be exact. Awad (2019) points out that the frequency of database access is minimized with JWTs: the server needs to consult the refresh token database only when a JWT expires while it needs to access to the session token database each time the user sends a HTTP request. But this difference becomes smaller and smaller with a shorter lifetime of JWTs for strengthening security.

Once I understand all this, then I finally figure out what Slootweg (2016) means by saying JWTs are not meant to be used to manage user sessions. By design, JWTs are not suitable for repeatedly authenticating users. The benefits of JWTs will be compromised once we start dealing with user sessions.

That's how I have interpreted the difference between session tokens and JWTs. If I miss something, please let me know. I'm eager to learn more.

Before we go…

This article is published in a publication called Web Dev Survey from Kyoto. The idea is to hold a virtual conference on web development in Kyoto City, the ancient capital of Japan, where I’m living now. After each article (which acts as a seminar talk), we take you to the tour around the city.
Today the famous cherry blossoms are mostly fallen, unfortunately. However, kerria is now blooming in brilliant yellow across many places in Kyoto City:

Daikaku-ji Temple (photographed by the author on 5 April 2021)
Alt Text

Matuno'o Shrine (photographed by the author on 10 April 2021)
Alt Text

References

Ben Awad (2019) "Why I haven't been using JWT tokens for Authentication", YouTube, Apr. 21, 2019.

Mariano Calandra (2019) "Why do we need the JSON Web Token (JWT) in the modern web?", Start It Up, Sep. 6, 2019.

Ryan Chenkie (2020a) “Steal a JSON Web Token”, React Security Fundamentals, May 2020.

Ryan Chenkie (2020b) "Add a Cross-site Request Forgery Token", React Security Fundamentals, May 2020.

Soham Kamani (2017) “Web security essentials — Sessions and cookies”, sohamkamani.com, Jan. 8, 2017.

Sven Slootweg (2016) “Stop using JWT for sessions”, joepie91’s Ramblings, Jun. 13, 2016.

Vladimir and Tanmai Gopal (2019) "The Ultimate Guide to handling JWTs on frontend clients (GraphQL)", Hasura Blog, Sep. 9, 2019.

Latest comments (53)

Collapse
 
dezfowler profile image
Derek Fowler

Once you get past the title of this article (which I think really needs to change to something more like "Which is best for a simple website: token or session auth?" but I get the desire to be clickbaity) the quote which I think is the core of this argument is:

This is why Awad (2019) says he uses session tokens, not JWTs, for a simple web app.

and that's pretty reasonable especially if, as the rest of the post suggests, you intend to implement the token auth yourself rather than using a service.

I think a big piece missing from this is that session auth is not a standard and is often a roll-your-own kinda deal where you need a user database with passwords and some session["isLoggedIn"] thing going on but token auth is a standard and the more usual way to implement that is to use a library or an external token service. The advantage here being that token auth is more secure by default and if you use an external service you don't have concerns about secure password storage, refresh tokens, etc someone else already solved that problem.

From that point of view token auth via an external service is actually a better choice for a simple website because you write zero code and zero code means zero bugs and that you can spend that time implementing the features you can't just buy off the shelf.

Collapse
 
mdelong42 profile image
Matt DeLong

U can store data withing the jwt that when decoded could provide expiry dates. Having a redis server or making additional calls db is not worth it for a small / medium sized project

Collapse
 
lordofcodes profile image
Sujeet Agrahari • Edited

Ben Awad seriously? It would have been worth reading if you would have referenced owasp guidelines on JWT auth .

cheatsheetseries.owasp.org/cheatsh...

One more thing, the whole point of JWT is to make auth stateless and should not be stored on server.
It fulfils REST constraints that requires requests to be stateless.

Collapse
 
leob profile image
leob

The advice that I've always heard is: DON'T implement security yourself! It's far too easy for the average (web) dev "to do it wrong". So, use a good library instead, e.g. Passport.js in the Node.js world to mention a well-known one.

Now, choosing between session tokens or JWT's might not count as "implement your own security", but it gets close. Arguably the average mainstream web dev does not have enough knowledge/insight to make this decision.

So, again, use a library or framework (probably you should use the most popular / most well-supported), and go with what they support, or what they advise (for a given use case).

(note: I am not saying that it isn't a good idea to learn and understand about concepts like session tokens, JWT, XSS, CSRF and so on - it's a great idea to study this stuff and to have a general understanding of it ... but don't reinvent the wheel and implement your own auth or security)

Collapse
 
darkwiiplayer profile image
𒎏Wii 🏳️‍⚧️ • Edited

Arguing that you shouldn't store access tokens in local storage because XSS is akin to arguing you shouldn't store your underwear in wooden drawers in case your house burns down. If you have a XSS vulnerability, you're screwed, and so are your users if you don't notice it quickly and shut everything down.

Local storage as least as long as everything else on your site is safe. If local storage is compromised, your house is on fire.

As for quick expiry: what's the point when an attack can happen in a matter of seconds after a token has been exfiltrated? The moment the access information gets to a malicious server, count to 3 and you've bought ten washing machines delivered to india, rated 30k products with 5 stars and sent an 20 messages with scam links to each of your contacts. Using short-lived tokens is only an option for invalidating old sessions reasonably quickly without hammering the database for every request; but it doesn't add much security beyond this.

If you think you need safer technology for your access tokens, you're probably looking at the wrong place, and should instead consider adding other layers of security: Asking for a password, or at least running much more secure checks on important actions (Buy things, delete things, transfer ownership of things, etc.); Give users an option to invalidate all of their sessions and do this automatically if they change their password, email, or other such information; send out emails for certain actions, if not asking for confirmation, then at least to inform the user and possibly giving them a way to revert whatever changes were made.

Collapse
 
dbroadhurst profile image
David Broadhurst

If you really understand security then you wouldn't consider building your own auth system. It's good to know the fundamentals but it's rarely worth the time and effort to build your own.

Collapse
 
mufassirkazi profile image
Mufassir Kazi

Here's a great article on the subject: supertokens.io/blog/the-best-way-t...

Collapse
 
frankszendzielarz profile image
Frank Szendzielarz

From my point of view this is comparing apples to oranges. Presumably OAUTH access tokens could even be implemented as session tokens if you so chose. I think it is brave to make public notes like this while at the early stages of understanding, so I commend you for that. I think there is much to learn on this topic and putting yourself out there like this is a good way to go about it. As they say: fail, but fail fast.

It is also good you added strong caveats that this is a note to yourself, not guidance.

Collapse
 
x8er profile image
Maxim

Author, please remove the security hashtag. This article should be taken as a joke and nothing more.

Collapse
 
noclat profile image
Nicolas Torres • Edited

Bookmarked for the very instructive comments.

Collapse
 
frankszendzielarz profile image
Frank Szendzielarz

It is not instructive. It is the author's notes to self while still in a very naive position on the topic.

Collapse
 
noclat profile image
Nicolas Torres

I meant the comments are instructive, not the article!

Thread Thread
 
frankszendzielarz profile image
Frank Szendzielarz

Ah. OK :-)