This is an excerpt of an original post published on blog.hasura.io
JWTs (JSON Web Token, pronounced 'jot') are becoming a popular way of handling auth. This post aims to demystify what a JWT is, discuss its pros/cons and cover best practices in implementing JWT on the client-side, keeping security in mind. We’ve kept the examples especially relevant to GraphQL clients.
- Introduction to JWT
- GraphQL client setup with JWT
- Silent refresh
- Persisting sessions
- Force logout
- Server side rendering (SSR)
For a detailed, technical description of JWTs refer to this article.
For the purposes of auth, a JWT is a token that is issued by the server. The token has a JSON payload that contains information specific to the user. This token can be used by clients when talking to APIs (by sending it along as an HTTP header) so that the APIs can identify the user represented by the token, and take user specific action.
Good question! That’s why a JWT also contains a signature. This signature is created by the server that issued the token (let’s say your login endpoint) and any other server that receives this token can independently verify the signature to ensure that the JSON payload was not tampered with, and has information that was issued by a legitimate source.
But if I have a valid and signed JWT and someone steals it from the client, can’t they use my JWT forever?
Yes! If a JWT is stolen, then the thief can can keep using the JWT. An API that accepts JWTs does an independent verification without depending on the JWT source so the API server has no way of knowing if this was a stolen token! This is why JWTs have an expiry value. And these values are kept short. Common practice is to keep it around 15 minutes, so that any leaked JWTs will cease to be valid fairly quickly. But also, make sure that JWTs don’t get leaked.
That’s why it’s also really important not to store JWT on the client, say via cookies or localstorage. Doing so you make your app vulnerable to CSRF & XSS attacks, by malicious forms or scripts to use or steal your token lying around in cookies or localstorage.
A JWT looks something like this, when it's serialized:
If you decode that base64, you'll get JSON in 3 important parts: header, payload and signature.
The serialized form is in the following format:
[ base64UrlEncode(header) ] . [ base64UrlEncode(payload) ] . [signature ]
A JWT is not encrypted. It is based64 encoded and signed. So anyone can decode the token and use its data. A JWT's signature is used to verify that it is in fact from a legitimate source.
Here is the diagram of how a JWT is issued(
/login) and then used to make an API call to another service(
/api) in a nutshell:
This is a painful discussion on the Internet. Our short (and opinionated answer) is that backend developers like using JWTs because a) microservices b) not needing a centralized token database.
In a microservices setup, each microservice can independently verify that a token received from a client is valid. The microservice can further decode the token and extract relevant information without needing to have access to a centralized token database.
This is why API developers like JWTs, and we (on the client-side) need to figure out how to use it. However, if you can get away with a session token issued by your favorite monolithic framework, you’re totally good to go and probably don’t need JWTs!