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