JWT are usually created during logins returned by the server on success and saved in clients in local storage or session or mostly in cookies. Its used when client tries to access protected route. JWT is not replacement for authentication, it just verifies that this is the client who logged in.
Properties/Features of JWT
- Its a Base64 Encoded URL safe string.
- It is Encoded and NOT Encrypted
- Should not save sensitive information inside a JWT.
- Typical JWT: xxxx.yyyy.zzzz (Header.Payload.Signature)
- Header: Algorithm used (HMAC SHA256) and type of token (JWT).
- Payload: Contains claims
- Signature: Used to verify if the token wasn’t changed along the way.
Token Flow
First, just know there are two types of tokens,
- Refresh tokens which expires in one year, it is used to regenerate new set of access and refresh tokens.
- Access tokens expires based on requirement 1hr to 24h usually. It is used to access protected routes.
In the below approach,
- Client tries to make a Login/Register request with username & password
- Server checks if it is valid.
- If valid, it sends Access-Token & Refresh-Token
- For accessing protected route, client will his send his Access-Token as Authorization Header to server
- Server verifies whether the authorization token is valid.
- If valid, server will respond saying its valid
- Otherwise it will send “not valid” with 401 Forbidden.
- If client gets, Acccess-Token has expired from server, it will send request with refresh token in the request body.
- If the Refresh token is authorized, server will send back new Access-Token and new Refresh-Token.
- If the Refresh-Token is invalid, we send back not valid and 403 Unauthorized state.
Packages beings used are
Programs
Below program is run on two scenarios
- Generate the initial Refresh token and Access token which are saved into .env file
- If and When, the Refresh token and Access token are compromised, run this program to regenerate tokens and update .env file
Below are the contents of .env file
All the codes are available in Github. In this sample application, user data is stored in a JSON file during registration and during login the same file is read. We will just discuss only the main part of the codes.
Below is the code used to generate access token during login/register. NodeJS code for access and refresh token are almost same just the expiresIn changes. In access its 15s here below quick testing and refresh can be set from range 7d(7 days) to 1y(1 year). It follows vercel/ms
or zeit/ms
convention.
Don’t store any authorized/secret information in payload or options object as they are only encoded not encrypted.
In the below, i have sent post request to login route(left-bottom). On the right it has generated a acccess token.
You can copy the above access token from above enter it in site jwt.io to decode the string and get all the payload information.
This access token can be stored in cookie/session/localStorage, it upto to the user. Its mostly stored in the cookie, so its sent to server on each request. So, one of the recommendation here is to keep the payload small as possible.
JWT Error handling
There are 3 types of error codes,
-
TokenExpiredError
- Has only one message - ‘jwt expired’
-
JsonWebTokenError
- This has multiple messages and passing these messages out to client might open a security hole. So in the code, we are just mentioning it as ‘Unauthorized’
- ‘jwt malformed’
- ‘jwt signature is required’
- ‘invalid signature’
- ‘jwt audience invalid. expected: [OPTIONS AUDIENCE]’
- ‘jwt issuer invalid. expected: [OPTIONS ISSUER]’
- ‘jwt id invalid. expected: [OPTIONS JWT ID]’
- ‘jwt subject invalid. expected: [OPTIONS SUBJECT]’
- This has multiple messages and passing these messages out to client might open a security hole. So in the code, we are just mentioning it as ‘Unauthorized’
-
NotBeforeError
- Has only one message - ‘jwt not active’
In the below test code, the refresh and access tokens are just retured back to the client
Below is the register code, it is similar to above login route but there few differences like
- In the login route,
users.json
file is just read, here it is read and does few things extra - Encrypting password using bcrypt. bcrypt is another library which is used here, it has nothing to do with JWT. Just a little learning. Encryption is one-way meaning, you can only encrypt and while login you compare password string with hashing password string stored in file/server.
New learning from NodeJS perspective. next()
, when its
next()
- call will be made to the next middleware in code.next(error)
- if there is a parameter, it will be considered as error and code will try to execute the error handler.
Note - error-handling functions have four arguments instead of three: (err, req, res, next). More details here on error handling.