JSON Web Tokens - JWT
JWT is a specification for storing in a stateless way the information about a user, and what permissions a user can have within a system. Since JWT is a JSON object, it provides a great deal of flexibility in how you store, manage and make use of this information.
Since JWTs can be signed and encrypted, it is also possible to store them on the client side. A JWT is a Base64 encoded string that consists of three parts - a header that describes a token, a body with the details of the user, their roles, token expiry information, and a third section that is a has that verifies the integrity of the token.
However, where and how to store your JWT, handling expiry and token refresh are still areas that need to be considered. Incorrect use of JWT can leave your tokens vulnerable to man in the middle attacks, cross-site forgery attacks. In the next section, we will discuss the security considerations of where to store your tokens, and how to handle refresh and/or revocation.
The options available for storing your JWTs are
- HTML5 sessionStorage/localStorage
When using sessionStorage or localStorage, once a user has authenticated the response body would contain the token, and you could persist this to local storage, and have it sent out with every request with your application.
However, HTML5 storage is vulnerable to XSS attacks where it is possible for a malicious script to read the the entirety of what is stored in localStorage, and is becoming recommended to not store sensitive information
One approach to work around this, is generating a token that can give access to an endpoint such as
/user/whoami which will contain the payload of the JWT in the response body. This can be used during the bootstrapping of your application.
Token Revoke & Expiry
Once a token is generated, it is not a good idea to let token live forever. If someone else is able to gain access to the token somehow, it can allow unauthorized users to gain and maintain access to the system. Another consideration is when a users roles and permissions have changed - you do not want to allow a stale token with now incorrect permissions to still have access to the system.
One approach is to have the access token have a shorter duration, and another refresh token that can be used to generate a new JWT to automatically refresh.
However, the implementation of this approach can be clunky, as once a request fails - you will need to issue a request for a new token, and then attempt the request again.
Another approach is to revoke the token. While this does add some state to your JWT tokens, it might be the appropriate compromise to help ensure the security of your system.
For example, you could attach a status field to the account attached to user, and when verifying the token - see if the user is still active and grant access depending on that.
Or, you could leverage the
jti - JWT ID Claim of the token.
jti should be a unique identifier for the token, and should also be stored in a database in a way that associates a user with the token. When a users access has been changed, or revoked - the
jti should be added to a blacklist of tokens, and prevent access to the system.