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!! 😊

Top comments (46)

Collapse
 
joelnet profile image
JavaScript Joel

I think there's way too much hype around it and people are using JWT because it's shiny!

As our industry shifts our paradigm from monoliths to microservices, knowing JWT is quickly becoming a requirement. Sometimes it is good to use the new shiny toy as an excuse to learn it. Even if your app serves less than 4,000 requests per minute and JWT would be overkill for your application, learning JWT and having practical knowledge of it is also of value.

Many articles will show you how to setup and login with JWT but they ignore the hard parts - Logging users out and blacklisting users

This is one area that is definitely overlooked and also complex. The most common solution for this is on the auth server itself, which will keep a list of users that are authenticated, expiring them when they log out. On each request the token will need to be checked against the auth server to see if the token is still valid. This adds significant overhead.

More commonly, most companies will decrease the expiry of the JWT, so it must be refreshed more often. This is due to the difficulty and overhead of managing a true logout experience.

“JWT is secure”

JWT is secure, but it is at the same time less secure than session based authentication. For example, the JWT is more vulnerable to hijacking and has to be designed to prevent hijacking. An unexpiring JWT can become a security risk.

You are also trusting the token signature cannot be compromised. This can happen if you are using weak encryption, encryption that becomes vulnerable in the future, or having the the private keys compromised. This vulnerability doesn't exist with sessions.

So while JWT is secure, it introduces new attack vectors that need to be considered.

There are valid reason to use JWTs:

  • API back end - Your site is static and your back end is an API.
  • Micro-service architecture - A very common way of authenticating across disconnected systems.
  • To Learn JWT - Implementing JWT in smaller projects is a good way to start learning JWTs.
  • Externalizing your authentication to a 3rd party provider like Auth0.

JWTs do not come without their own complixity:

  • Security is more complex and needs to be understood.
  • Just like how micro-services add complexity, JWT adds the same complexity as the auth is disconnected.
  • Simple things like logout become complex and might require changing your expectations and business requirements.
  • Doing JWT right is hard.
Collapse
 
madhadron profile image
Fred Ross

As our industry shifts our paradigm from monoliths to microservices, knowing JWT is quickly becoming a requirement.

Unless scaling your system has driven you to microservices as a point of last resort, they're usually the wrong answer. Over the next couple of years, people are going to figure that out the hard way.

I don't know why JWT would be a requirement for microservices, though. A user connects to an HTTP server, the JWT token is checked there, and the HTTP server connects to other microservices with the user information as part of its request. There's no reason to pass the JWT around.

Collapse
 
joelnet profile image
JavaScript Joel

Unless scaling your system has driven you to microservices as a point of last resort, they're usually the wrong answer

I am going to disagree with this statement. It sounds like you are suggesting JWT is a BAD option and should only be used as a last resort. JWT is just an option. And what you choose is will depend on your specific application and needs. So there are times when JWT is the BEST option.

To make a blanket statement saying JWT is always a BAD option is just not valid.

I don't know why JWT would be a requirement for microservices, though.

No technology is never a requirement, but an option... A tool available to us to choose and use.

There's no reason to pass the JWT around.

Blanket statements like show extreme bias and because of this bias become easily dismissed. To prove "no reason" is impossible. There's always a reason.

Thread Thread
 
madhadron profile image
Fred Ross

I wasn't saying that JWT was usually the wrong answer. I was saying that microservices are usually the wrong answer.

Can you provide a scenario where passing a JWT around among microservices is a good idea?

Thread Thread
 
joelnet profile image
JavaScript Joel • Edited

Can you provide a scenario where passing a JWT around among microservices is a good idea?

I'm starting to feel like I am being Sealioned here.

Sealioning

Thread Thread
 
madhadron profile image
Fred Ross

Not my intention. I genuinely would like to know of such a scenario, since I can't think of one.

Collapse
 
mykelswitzer profile image
Michael Switzer

Unless scaling your system has driven you to microservices as a point of last resort, they're usually the wrong answer. Over the next couple of years, people are going to figure that out the hard way.

I can't disagree with this sentiment more strongly. While I've seen many a distributed system setup incorrectly, I've never seen one worse than a monolithic architecture.

the HTTP server connects to other microservices with the user information as part of its request. There's no reason to pass the JWT around.

That's a hard shell, soft center security approach which is very vulnerable to attack.

Collapse
 
lofibytes profile image
Jillian S. Estrella (she/her) • Edited

Static site with API backend is not a use case for using JWT.

I use “static” frontends regularly with GraphQL backends and still use sessions with secure httpOnly cookies so they can’t be accessed by the browser.

IMO JWTs are okay for what I refer to as “loose” authentication (when you quickly want to hide some nonsensitive data behind a login using a service such as Auth0 but your not necessarily exposing sensitive data).

Always, when exposing potentially sensitive data, use sessions with secure httpOnly cookies.

Collapse
 
joelnet profile image
JavaScript Joel

Static site with API backend is not a use case for using JWT.

it is also not NOT a use case.

I like JWT when you have multiple distributed systems that need to share a single authentication that a single system doesn't have the authority to maintain itself.

Authentication as a Service.

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!

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
 
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.

 
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
 
mercxry profile image
mercxry

I've been fighting several days to convince the other developers of the startup that I work for that JWT is not the ideal solution for authentication, I'm happy that there are other people who are talking about this :D

Collapse
 
rhymes profile image
rhymes

I think there's way too much hype around it and people are using JWT because it's shiny!

I think they all suggest JWT because it's easy to setup and lots of tutorials about single page applications just re-iterate that since it can be controlled by JS.

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.

Exactly. That to me is the weakest argument

There was a really interesting discussion one year ago about using JWT and the local storage here if you're interested:

This is my favorite resource so far on the argument JWT and sessions: Stop using JWT for sessions, part 2: Why your solution doesn't work

ps. dev.to doesn't use JWT for the session, just to store additional user info.

 
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.

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
 
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.