JWT Access and Refresh Token

How we can prevent the user from login again and again, within a secure environment.

ยท

4 min read

JWT(Json web tokens) is a library or npm package which provide us two types of token one is access and seconded one is refreshed. to maintain the login functionality of user with authentication and authorization. provided token is a form of encrypted string like this "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c" which have header, payload or data and hash encoded form.

Define jwt token in your model like this:
1: install jwt "npm i jsonwebtoken" and follow the following steps:

import jwt from "jsonwebtoken";

Define/add methods from mongoose schema for access and refresh token. which will be used whenever we need to generate these token from our end points. Now these methods will be the part of you model and we can access these methods like the property of our model.
jwt.sign() take 3 parameters , one is payload or data which we need to send back, 2nd one is your secret key which used to generate the hash and third one is duration of expiry. expiry and secret key stored in .env file just for a good practise.

userSchema.methods.generateAccessToken = function () {
  return jwt.sign(
    {
      _id: this._id, // this._id coming from DB
      email: this.email,
      username: this.userName,
      fullName: this.fullName,
    },
    process.env.ACCESS_TOKEN_SECRET,
    {
      // take expiry of tokan
      expiresIn: process.env.ACCESS_TOKEN_EXPIRY,
    }
  );
};

now we can call the above function in our endpoint with our model name like this :

const accessToken = User.generateAccessToken();

in the same way we can generate the refresh token too but according to our need of payload and secret key and duration. Like in refresh token method our payload is just id because we have to get the refresh token again and again whenever we required if access token is expired. By this method we do not need to ask user for login again and again.

userSchema.methods.generateRefreshToken = function () {
  return jwt.sign(
    {
      _id: this._id, // this._id coming from DB
    },
    process.env.REFRESH_TOKEN_SECRET,
    {
      expiresIn: process.env.REFRESH_TOKEN_EXPIRY,
    }
  );
};

so, now we have defined our both functions for access token and refresh token. when user logged in we will store the value of refreshed token in our database. so, once a user logged in our application he/she do not need to login again and again even after expiry of access token or session.

Insert the value of refresh token in our DB.

const generateAccessAndRefreshToken = aysnc(userID)=>{
// get user 
const getUserById = await User.findById(userID);
const accessToken = getUserById.generateAccessToken();
const refreshToken = getUserById.generateRefreshToken();
    // insert into db refresh token
    getUserById.refreshToken = refreshToken;
    await getUserById.save({ validateBeforeSave: false });
// now return both tokens
    return { accessToken, refreshToken };

now we will set the secure cookies for authentication, I am using cookie-parser middleware to set the cookies and send to client, these cookies can be modified just form server side:

 const options = {
  httpOnly: true,
  secure: true,
};
return res
        .status(201)
        .cookie("accessToken", accessToken, options)
        .cookie("refreshToken", refreshToken, options)
        .json(
          new ApiResponce(
            200,
            {
              user: loggedInUser,
              accessToken,
              refreshToken,
            },
            "Login successfully.๐Ÿ˜Š"
          )
        );

Now, we will discuses if access token is expired and user wants to access any endpoint of our website so how we will authenticate him. so for this purpose we will make an endpoint when user hit that end point he will get the refresh token and we will authenticate him and give him access. rather to direct him logged in again.

We get the user's token from cookie and verify it from jwt.verify() method. Because user's token is in encrypted form and we have to decoded it from verify method to compare the user token and that token which is stored in our DB. In decoded token we have the payload and from payload we will access the _id.
if Token matched than generate a new refresh token from the above method and return back the necessary data and set the cookie also with both(access , refresh) tokens.

const getRefreshAccessToken = asyncHandler(async (req, res) => {
// get user token from cookie
  const tokenReceiveFromUser = req.cookie.refreshToken || req.body.refreshToken;
  if (!tokenReceiveFromUser) {
    throw new ApiErrorHandler(401, "Invalid Token from user");
  }
  try {
    // verify user's token from jwt method , 
    const decodedToken = jwt.verify(
      tokenReceiveFromUser,
      process.env.REFRESH_TOKEN_SECRET
    );
    // now we hv decode token, match with refresh token store in DB
    const userAndRefreshTokenDb = await User.findById(decodedToken?._id);
    if (!userAndRefreshTokenDb) {
      throw new ApiErrorHandler(401, "Invalid refresh token ");
    }
    if (tokenReceiveFromUser !== userAndRefreshTokenDb?.refreshToken) {
      throw new ApiErrorHandler(401, "refresh token is expired or used");
    }
    // otherwise , generate a new token for user
    const { accessToken, refreshToken } = await generateAccessAndRefreshToken(
      decodedToken?._id
    );

    return res
      .status(200)
      .cookie("accessToken", accessToken, options)
      .cookie("refreshToken", refreshToken, options)
      .json(
        new ApiResponce(
          201,
          { accessToken, refreshToken },
          "access token refreshed!๐Ÿ˜Ž"
        )
      );
  } catch (error) {
    throw new ApiErrorHandler(401, error?.message || "Refresh Token failed");
  }
});

I hope this will be helpful!!. Here i will dedicate this article to sir Hitesh Choudhary , just because of this person i am able to learn JWT and this is my first article in my life which i try to write.

regards
Al-Rehman solution provider

ย