There are many technical articles about JSON web tokens (JWT) on the interwebs, but I haven’t found one that explains the problem and key aspects of the solution to my satisfaction.
The problem: You’re building a web service that clients access — typically, using REST APIs. You want to make sure only the right parties have access to the right APIs. How do you do that?
The classic way of doing that is:
- Authenticate user (say using username / password)
- Create a session object and store it in the DB. Session object contains information that can be used to determine what operations the logged-in user can do. E.g. session object may store that this user is an admin and therefore can delete stuff. Session object also has an expiry time.
- The session object is assigned a random id and this session id is returned to the client. This is supposed to be hard to guess.
- The client presents the session id along with every API request. This may be done via cookies or via custom HTTP headers.
- The service looks up the session object using the id presented and checks if this user can perform the operation. E.g. non-admin user trying to delete stuff. Returns error code if not allowed, otherwise pass the request through.
- If the session object has expired, redirect user to authentication page
This solution is not a bad one, but has drawbacks. Every API request requires accessing the DB to fetch session object. This can put a lot of pressure on your DB. Expired sessions need to be cleaned up once in a while or your sessions table could grow.
One great advantage of this approach is that all the session state lives in the server — it is possible to force a user off the system by deleting their session object. This can come in handy if it seems like there’s some nasty stuff going on.
Can We Do Better?
If the contents of session state were to be stored on the client side, the server won’t need to keep a sessions table and clean up expired sessions. The problem is — malicious clients can tamper with this information. Example, a malicious user may simply set a is_admin flag to true and do bad things.
What if the session information were stored on the client side in a tamper-proof manner and presented with every request?
In the above flow, the key change would be that the service returns entire contents of session object to the client. The client presents the entire contents of the session object along with every request. The service can do exactly the same checks to determine if the client is authorized to perform the operation.
It all comes down to: how can we ensure that the session object has not been tampered with by the client? The service can generate a hash of the session object using a special function with a secret key known only to the service (see MAC for details). This is sent back to the client along with the session object and is part of every request as well. Upon receiving the session object, the service can verify integrity of the session object by recomputing the MAC using the same function and matching the codes.
Thus, the key aspects of JSON web tokens are:
- Computing MAC for session object on server side and sending it to client
- Client sending session object + MAC code with every request
- Server verifying integrity of session object and doing auth checks
With JWT, the server doesn’t need to store session object in the DB. This keeps your DB nice and happy.
For readers who’ve made it this far, here’s a little teaser. Let’s say we want to build a safety mechanism to force all users to re-authenticate themselves. With the DB solution, we can simply delete all session objects. How can we do that with JWTs?