DEV Community

Darragh O'Riordan
Darragh O'Riordan

Posted on • Originally published at darraghoriordan.com on

Be careful of the JWT hype train

So many articles, courses and project “starters” on GitHub suggest using JSON Web Token (JWT) on your client facing API as a session token.

There’s way too much hype around JWT as a technology and it’s become a default for all authentication tokens instead of being used where it makes sense.

🔐 What is JWT? 🔐

JWT is a token format where a JSON object with some standardised fields that gets signed in a standardised way. The token also includes information on how it is signed so a client can validate the token.

This signed object can then be sent to the user through any mechanism. It’s usually passed in the body of an HTTP response or in some header encoded with base64, similar to a cookie. A client sends the JWT to a server where you check the signature and perform an action provided if it’s a valid token.

In a frontend client application you can store the JWT in local storage or a cookie and send it with each request to your API. The action in this case is returning some data from your API.

The benefit of a JWT is that token has all the details about the user and their permissions for resources stored in it. Your API doesn’t have to hit another resource like a database to get permission data when the user sends a valid JWT.

This is in comparison to using a simple session ID. Using this mechanism the Session ID is sent to the client, usually in a cookie. The server validates the session ID against a list cache it keeps. Then it looks up whatever it needs to about the current user to fulfil the request. In this scenario all state apart from the session ID remains on the backend server/database.

JWT is a fantastic solution for authorization if you have multiple services to call in a “fan-out” scenario. With session IDs, each service would have to call something to get session data and user data. With JWT all required permissions are in the request and a service can just verify the signature.

This doesn’t mean JWT is the best solution for simple web client -> backend sessions on a web application.

“It will make my API stateless”

Yes JWT can remove the need for a stateful session store in your backend. This is when you plan to put all the user data and permissions etc in the token so you don’t have to make a database call to get user data on your API. Sounds great but…

It's probably premature optimisation

If you’re building a client/server web application and you expect less than ummm say 4,000 requests per minute to the database, then the LOWEST paid tier Postgres and a cheap $5 or $10 digital ocean droplet can handle that for you no problem. You don’t need stateless anything and you don’t even need memcached or Redis.

Unless you’re expecting serious scale then for most company’s products you can have sessions and scale up hardware until you have enough cash and engineering bandwidth to add something else like distributed services and JWT. Don’t prematurely optimise for problems that don’t exist.

Is your API truly stateless for user data?

It’s VERY difficult to avoid state in a useful client to server web application. Do you really retrieve nothing else about your user from the DB on each request? No role changes or payment status changes might have occurred since the JWT was issued? No intersections between the user and the specific request at all?

It’s possible that this is the case but unlikely in general. If you’re using a request that requires authentication then you probably need to do some kind of lookup based on the user.

You can't implement stateless basic account administration

Many articles will show you how to setup and login with JWT but they ignore the hard parts - Logging users out and blacklisting users. Acceptable solutions for these features involve maintaining state.

If you want to legitimately have a user “log out” then you need to keep a list of JWTs that have been invalidated by the user. Now you have state that is checked on every request.

If you use a salt to sign each users token so you can later change the salt to log a user out, then you have to check a list of the salts each time the user makes a request and now you have state that is checked on every request.

If you want to be able to block a user because their account is in debt, deleted, or they are up to no good then now you need to keep a list of blocked users and you have state that is checked on every request.

You can use short lived access tokens and refresh tokens to get around these but now you have two tokens and you have to verify the refresh token against a list of invalidated refresh tokens and now you have state that is checked on every request.

Stateful JWT - "I just store the user Id in my JWT"

You can put a user identifier encoded into a property in a JWT and use it like a session cookie but now you’re recreating server sessions except with JWT.

Why bother with the JWT wrapper hassle? Instead just use some kind of session library and skip the JWT.

“JWT is supported in all these frameworks and works better across both browsers and mobile clients”

So are cookies. Cookies are just an HTTP header. Any HTTP client can read and set headers. The cookies header also has 20+ years of security and functionality built in to it for browsers (HTTPS only, expiration, site scope, blocking access from JavaScript) and there are well known and understood fixes for issues like CSRF tokens for forgery.

Every back end web framework supports HTTP headers and in fact probably has first class support for cookies, with a sessions library (via a generated id) tied to a data store of some kind.

“JWT is secure”

The signing and verification is pretty secure. However many articles and courses describe storing your JWT in local storage. This is not secure. Any JavaScript on the page can read it and use it.

You almost certainly have JavaScript on the page that you didn’t write that came from an NPM package or a CDN. Vulnerability injection in this way has been done before and will happen again.

The alternative to local storage is storing the JWT in a cookie. So now you need to protect the cookie just like you would with an old school session Id.

On the backend you need to make sure you’re using HTTPS and you need to make sure you’re not vulnerable to XSS attacks. You also have to verify the JWT signature and expiry on every request.

So what should you do?

Well if you’re building an http client application and backend then you probably don’t need JWT.

JWT has its uses for permissions in distributed systems but there’s a good chance it’s actually the wrong solution for your simple application and it’s making things more complicated or insecure than a session store with Ids and cookies.

So just be sure you know why you’re using JWT and understand its limitations before adding it to your awesome new app!! 😊

Latest comments (46)

Collapse
 
alialp profile image
Ali Alp

A brand new approach regarding the usage of JWT, which is the combination of the two very famous security mechanisms JWT and TOTP for the first time in their digital lives.

check it out

Collapse
 
dangolant profile image
Daniel Golant

No role changes or payment status changes might have occurred since the JWT was issued?

Can't we revoke JWT in that case?

By no means a JWT advocate btw.

Collapse
 
madhadron profile image
Fred Ross

Then you have a stateful revocation list you have to make available to every server and JWT is no longer stateless.

Collapse
 
dangolant profile image
Daniel Golant

Oh right, didn't think of that.

Collapse
 
johnnybit profile image
Hubert Kowalski

Article about JWT. Ctrl-F "PASETO". "No results found".

Am dissapoint.

github.com/paragonie/paseto

paseto.io

Collapse
 
fluffynuts profile image
Davyd McColl

You don't need JWT, so don't take it that way; but I think you've perhaps had some difficulties and you're perhaps missing a thing or two:

  • on transience: a session cookie has transience too -- and it can be stolen just as effectively as a jwt. Anything which has "physical" access to the document can hijack cookies or localStorage. Anything "on the page" can steal my auth cookie too. Cookies won't save you here.
    • naturally, the fix is to make your jwt have a short lifespan and include a renew token which is automatically used when a web call encounters a 401. Handling expired tokens isn't really your problem -- there are already good frameworks to deal with this, and good auth providers (like Auth0) so you can have someone else deal with this complexity and the security of your user data in a professional manner.
  • No-one said you had to include the JWT as a bearer token. You can use it however you like, including as a cookie
  • There must be no secret data in a JWT. If there is, you're doing it wrong. The point of the JWT is to allow the front-end to make display decisions based on the user's scopes. Your backend still has to implement real security. The front-end should be able to validate the JWT, if it really wants to, by hitting and endpoint. NO JWT SECRETS MUST BE AT THE CLIENT. You should be using something like the OpenID Connect flow, where your login and secrets are done server-side, giving back a token to the web client. The other flows are meant for server-to-server processing, so if you're using them in the client, yes, you're doing it wrong and yes you're not secure at all

All of this is not to say you can't (or shouldn't) use session-based auth -- go ahead! JWTs just make it easier to:

  • make client-side display decisions for the user (eg if she doesn't have access to the admin area, don't show the links)
  • centralize your authentication and authorization in one service which doesn't have to be duplicated across web apps. This service can be queried for token validation as and when necessary.

Doing JWTs properly is hard. I've done it a few times -- and wrong a few of those times! But where I'm standing, the friction to get them working with something like IdentityServer is so low and the benefits for "heavier" client-side web applications outweigh the small setup cost.

Collapse
 
kspeakman profile image
Kasey Speakman • Edited

there are well known and understood fixes for issues like CSRF tokens for forgery.

These also add a stateful component to the API. If you want people to still be logged in after node restart or if you ever need to scale beyond one node, then you have to make sure your sessions/CSRF tokens are stored in a shared database (e.g. Redis). This creates extra failure modes to handle (session store down or overloaded) vs verifying the JWT signature. Session cookies are a great solution for many cases, but it has trade-offs too.

 
uclaeamsavino profile image
uclaeamsavino • Edited

JWT allows a way to not have to check a central DB for every request to say an API. Albeit not w/o drawbacks as covered by this piece. So instead of -- 1) receive request, 2) look up user's session for info and to determine if it's valid, 3) serve user request -- with JWT you can eliminate step 2.

As the author describes for most small and medium sites, validating a request against a central session store is really not that onerous. It's only when you get into Netflix territory - then you have to worry about how to distribute all that state where it's always available and accurate in real time for any service that needs to authenticate the user.

 
joelnet profile image
JavaScript Joel

Can you tell me again, why we need JWT encryption?

First of all JWT is not encryption. It is authentication and authorization.

I can't tell you why you need JWT because I do not know anything about your application. But I can tell you why I am using it.

The environment I am working in consists of multiple disconnected systems, each maintained by different departments. JWT allows us to to create an authentication service that is disconnected and also works across multiple systems.

Ok, here is the key and it is unique, if it is stolen, then you are screwed. I will crc your key (and encrypt the crc) just for security.

This is a complete misunderstanding of JWT and realky has nothing to do with JWT.

Thread Thread
 
uclaeamsavino profile image
uclaeamsavino

Encryption (checking the token contents against the signature) works to ensure that everything in your JWT token is valid and hasn't been altered. So it's public, but you can trust it.

Thread Thread
 
philnash profile image
Phil Nash

That’s not encryption, that’s just signing. The data in a JWT is base64-url encoded, but otherwise easily readable.

There is a standard for JWT encryption, but that’s a whole extra level of pain.

Collapse
 
darraghor profile image
Darragh O'Riordan

Thanks for all the responses. Love the community here. Some interesting points and discussion.

I just hope this article might make people consider if they are using JWT to solve a specific problem for their application rather than just using it by default :) Especially people new to web development using a "starter" or doing a course that only shows them JWT in local storage.

Quoting joelnet - "Doing JWT right is hard." !

Collapse
 
joelnet profile image
JavaScript Joel

How can you avoid any curious customer don't call console.log() to show the jwt-secret?.

You don't keep secrets in the JWT. Everything in a JWT should be considered public.

 
steelwolf180 profile image
Max Ong Zong Bao • Edited

Yeah you are assuming that it identifies the user who are using it but the SSL only identify the computer itself is being used but the problem is that you can have multiple ppl using the same computer. So it is not that specific to the point of that it identify the correct information the user is using at the specific point of time

Collapse
 
fuelsean profile image
Brian Hill

The Jwt is for your back end, not your UI. The web client doesn't need to validate it, only pass it along with it's requests.

 
steelwolf180 profile image
Max Ong Zong Bao

Nah pretty sure SSL doesn't work this way.

Since that technology just provides you a encrypted tunnel.

It does not identify you as who you are as a user besides basic information of the computer you are using.

Collapse
 
steelwolf180 profile image
Max Ong Zong Bao • Edited

Actually, it's assumed that generation of JWT and exchange of it has to be conducted through SSL for security reasons.

So I don't really think JWT is redundant since it's use case is more suited for API gateway backend to authenticate API services of a given user.

Collapse
 
whileloop profile image
Anthony Alves

I think a big part of JWT should be some type of API Gateway.

In an example case (microservices) we take an API key from the client and the API Gateway creates a signed JWT with relevant user info. This is where blocked/logout checks will be handled.

With this, the JWT gets passed to the corresponding API service and it won't have to check again with the API Gateway on the given JWT. The JWT can also be passed to subsequent services if needed on behalf of the parent service.

1 call to user info is needed in this scenario. And the N microservices within the API request scope doesn't not need to check user auth.

Collapse
 
da_lion_619 profile image
LioneL Chetty

API Gateway? Sounds more like an auth server.
"The JWT can also be passed to subsequent services if needed on behalf of the parent service.", that's a risk not a feature.

Collapse
 
muhamadomr profile image
Mohamed Omar

i havent dealt with this situation before
but what the auth api gateway .. will hand to the services ? the user id right ? will that be on the header ?
thank you

Collapse
 
yaron profile image
Yaron Levi

Good article, but I can't agree on the ideas you brought up. An http cookie is a technology that originated from the browser world. JWT is agnostic and is not bounded to any platform or technology. We use JWT to store userId only. Sure both cookies and JWTs can be used in similar ways, but it's all about the intention of the code/solution you are building. And when using JWT the intention is a universal token that can be used anywhere and stored any where and not a cookie.

Collapse
 
deanius profile image
Dean Radcliffe

And the security of localStorage is not the joke that cookies are. blog.meteor.com/why-meteor-doesnt-... But when your entire argument is based on 'its probably good enough', then the presentation of new evidence is unlikely to change a thing. So have fun with your cookies!