JWTs and refresh tokens
A few notes on JWTs and refresh tokens whilst it is fresh in my mind. I am likely to forget in the months between touching the relevant systems.
JWTs
JWTs (JSON Web Tokens) carry information necessary to access a resource directly. They are short-lived. They can be verified without needing to be checked against a database (for example).
Refresh Tokens
Refresh tokens carry information to get new access tokens. They are usually just large strings of random characters.
How does it work?
- The client sends credentials (email, password) to the server.
- If valid, the server creates a JWT and a refresh token and sends them back to the client, along with the JWT expiry time.
- The client stores the JWT in memory and the refresh token in an HttpOnly cookie.
- Before the JWT expires, the refresh token is sent to the server to obtain a new JWT. The refresh token could also be regenerated at this time.
- If at any point we do not have a JWT in memory we can use the refresh token to obtain a new one.
Should the JWT expire without a new one being generated the server we have two options. I am not sure which I prefer.
- Have the server use the refresh token to obtain a new JWT and send that back with whatever payload was expected. The client would check every response for a new JWT and store it.
- Send back a 401 with the message
expired_token
, the client requests a new JWT using the refresh token and retries the request.
Of course, if at any point the refresh token is refused, the user is logged out or refused access to the resource.
Best Practises
- Always have an algorithm. The JWT specification allows for an
alg
of'none'
which removes the need for a signature. An attacker could strip the signature from the payload and change the algorithm. - Add auth roles to JWTs, this way the consuming resource can check for permissions just from the token itself.
- Keep expiry times short, refresh tokens should have longer expiry times.
- Store refresh tokens in HTTP-only cookies. No exceptions.
- Stored refresh tokens could be purged after a certain amount of time, though this would require a timestamp be stored with them, not so easy in Redis (for example).