JSON Web Token (JWT) is an open standard (RFC 7519) that defines a compact and self-contained way for securely transmitting information between parties as a JSON object. This information can be verified and trusted because it is digitally signed.
The following text is copied from jwt.io.
Firstly, visit the jwt.io website to have a look at some sample JWT token values, and how that value is constructed.
In its compact form, JSON Web Tokens consist of three parts separated by dots (.), which are:
- Header
- Payload
- Signature
Therefore, a JWT typically looks like the following.
xxxxx.yyyyy.zzzzz
The header typically consists of two parts: the type of the token, which is JWT, and the hashing algorithm being used, such as HMAC SHA256 or RSA.
For example:
{
"alg": "HS256",
"typ": "JWT"
}
Then, this JSON is Base64Url encoded to form the first part of the JWT.
The second part of the token is the payload, which contains the claims. Claims are statements about an entity (typically, the user) and additional data
An example payload could be:
{
"name": "John Doe",
"admin": true
}
The payload is then Base64Url encoded to form the second part of the JSON Web Token.
To create the signature part you have to take the encoded header, the encoded payload, a secret, the algorithm specified in the header, and sign that.
For example if you want to use the HMAC SHA256 algorithm, the signature will be created in the following way:
HMACSHA256(
base64UrlEncode(header) + "." +
base64UrlEncode(payload),
secret)
The signature is used to verify the message wasn't changed along the way, and, in the case of tokens signed with a private key, it can also verify that the sender of the JWT is who it says it is.
Note that you need a secret to sign and verify JWT tokens. This secret should be stored securely on the server side and only accessible to the application using this secret.
We have some simple codes to demo the construction of a JWT token
We have been using this term a few times above. What exactly is this thing? Why do we need it?
This is one algorithm that converts binary data (or text data) into a format that can be carried in HTTP request URL or headers.
According to the specification of HTTP protocol, there are certain characters (such as +
and =
) that are not allowed to apppar as part of URL or request/response header. On the other hand, people usually include JWT tokens as part of their HTTP requests (as we will show below), so it's important to make sure JWT token value do not contain those forbidden
characters.
People created this base64url encoding scheme for this purpose.
In authentication, when the user successfully logs in using their credentials, a JSON Web Token can be returned to the client side (e.g. the mobile app or browser). The client side saves the token (e.g in memory, or cookies) and submit it to server again in subsequent requests.
Whenever the user wants to access a protected route or resource, the JWT token in the requests help to identify who the user is, and the server side application can decide if the request on the resource should be allowed (this is called authorization).
There are two ways for the client side to submit JWT tokens to the server side:
- Using HTTP request header. There is one HTTP header called
Authorization
. The content of the header should look like the following:
Authorization: Bearer <token>
- If a JWT token is saved in cookies, the cookie is sent automatically together with the request when never the user visit the same server that issues the cookie.
We have a demo project which shows you how to issue JWT token from server side and verify it in subsequent requests.
In the app.js
file:
- The handler for
/signin
generates a JWT token and send it back in response body. - The handler for
/secret
verifies the JWT token and only display secret text if the token can be verified
A library called jsonwebtoken to sign and verify JWT tokens.
You clone the project, start the server, and test the API using REST API clients like Insomia.
As JWTs are self-contained, all the necessary information is there, reducing the need to query the database.
To be specific, the user information is obtained from the JWT token instead of looking up in database on the server side. So it helps to make the authorization process faster and stateless.
This is beneficial, because when there are lots of users of a website, we can add more instance of web servers in the backend, and if the requests are stateless, that means a request an be served by any instance of web server. This make it very easy to scale the capacity of the website by adding more servers.
On the server side, there is one secret that is used to sign/verify JWT tokens. Make sure this token is stored safely (in a file or database) that only the application itself has access.
If this secret is leaked, you have to change the secret, but that means all the tokens signed with the previous secret cannot be verified anymore. The users being affected have to do authentication again to acquire new tokens.
If you use JWT, you need to decide where to store the tokens at browser side. You have two possible choices:
- Storing JWT token in memory only
- Storing JWT token in cookies
There are limitations and concerns to be addressed for each storage choice:
- If you store JWT tokens in browser memory only, that token is lost whenever the user refresh the browser page (and they need to authenticate again with the server side)
- If you store JWT tokens in cookies, you need to worry about security attacks like CSRF. The good new is, this CSRF attack can be resolved by using some middleware on Express application, or setting the "SameSite=strict" flag in the session cookie (however, some browsers don't support this SameSite attribute).
There are two other choices for storing JWT tokens but they are not recommended:
- You can use browser's local storage
- You can use browser's session storage
There are security concerns of storing the JWT token in browser's local storage or session storage. For example, here are some issues for local storage. JavaScript has access to the browser storage. If a hacker injects some malicious script in to a website you are viewing, the script can load the tokens from your browser's storage and send them to the hacker. Then hacker can use your tokens!
You can find some comparison here on different choices of storing JWT tokens. This article also provides more details on why storing JWT tokens in cookies is better than storing them in browsers' storage.
Another important thing to remember is by default the content in JWT token is not encrypted. So don't put sensitive data into JWT tokens or you have to use JSON Web Encryption (JWE).
When a JWT token is issued by the server side and sent back to the browser side, the token is transmitted on the Internet. If the connection is HTTP, any computer in the middle of the network transmission can get a copy of the token. Then some hacker can impersonate as other people by stealing other's JWT tokens.
This kind of attack is called "man-in-the-middle" attack.
To prevent this kind of attack, HTTPS should be used to encrypt the request/response data during transmission.
It's a good practice to set an expiration time for each JWT token when they are issued, and the server side application should check for expiration when it gets a JWT token.
There are still some cases when you need to revoke/invalidate a JWT token before it's expired. For example, some employee leaves a company, then he should lose access to internal website of the company immediately. If the website is protected using JWT tokens, then the tokens issued to this ex-employee need to be revoked/invalidated immediately.
Unfortunately, there is no easy way to do that for JWT tokens. There are some solutions but the cost of implementing those solutions makes JWT less appealing.
For example, you can maintain a blacklist of users on the server side. The server side application needs to check if the user (identified by the JWT token) is in this black list before granting the user to access protected resources.
This solution works, however, if you do this, there is not much benefit of using JWT compared with session cookies.
If you use JWT token for session tracking, all the session information is in the JWT token. When a user logout, your client side application needs to remove this token from its memory.
If the JWT token is saved in a cookie, the logout
route handler on the server side needs to delete the cookie that stores JWT token upon user logout. That can be done via the response.clearCookie() provided by Express framework.