DEV Community

Cover image for Authentication and Authorization using AccessTokens - RefreshTokens | Part 2
Manish Kumar Sahu
Manish Kumar Sahu

Posted on • Edited on

Authentication and Authorization using AccessTokens - RefreshTokens | Part 2

Authorization

Session based authorization

Earlier developers were using session based authorization.
After the user logs in, a session Id is sent to client in form of a cookie and when there is a user request, the server checks for that cookie and gives access and response.

JWT

Drawbacks

  • The problem is sessions are stored in the server, and when the app scalability increases, user requests also increase which eventually put loads on the server.
  • It doesn't support multiple servers. For example, suppose user logs in to the 'Amazon' website then the session id is passed to the client, but if the same user wants to open 'Amazon Prime', the user has to log in once again to create a session id.

JWT based authorization

In this process, when the user logs in, the user information is signed with a secret (which is stored in the server) and a JSON web token is generated and it is sent to the client. Generally, it is stored in browser localStorage, whenever a user sends a request to the server in which the authorization token is present in the headers of the request , the server just verifies that JWT token with the secret it has by unsigning that JWT token and gives users access.

cookie sessions

Why JWT?

  • All the user state is stored on the client-side, so there is no load on the server.
  • It supports multiple servers by just passing the secret to multiple servers for verification.

Implementation using AccessToken and RefreshToken

  • RefreshToken is used to generate new AccessToken when it expires. AccessToken contains the user state or information and RefreshToken.
  • When user logs in, server creates AccessToken and RefreshToken stores the userId or something which when unsigns by someone, the person would not understand the context, and it returns response to the client. Usually AccessToken expires after a short time and RefreshToken after a long time.
router.post('/signin', async (req, res) => {
  try {
    const user = await req.body;
    const { email, password } = user;
    const userEmail = email;

    await User.findOne({ email: userEmail })
      .exec((err, user) => {
        if (err || user === null) {
          return res.status(400).json({
            message: "user does not exists!",
          });
        } else if (!user.authenticate(password)) {
          return res.status(401).json({
            message: "please enter the correct password!",
          });
        }
        const accessToken = jwt.sign(
          { user },
          process.env.ACCESS_TOKEN_SECRET,
          {
            expiresIn: "15m",
          }
        );
        const refreshToken = jwt.sign(
          { userId: user._id },
          process.env.REFRESH_TOKEN_SECRET,
          {
            expiresIn: "7d",
          }
        );
        res.json({ user, accessToken, refreshToken });
      });
  } catch (error) {
    res.status(400).json({
      message: error.message,
    });
  }
})
Enter fullscreen mode Exit fullscreen mode
  • Then we can make a middleware for the verification of the token and put it in any route for authorization.
exports.authorizeToken = async (req, res, next) => {
  if (
    !req.headers["authorization"] &&
    typeof req.headers["authorization"] !== "string"
  ) {
    return res.status(401).json({
      message: "No tokens found",
    });
  }

  try {
    const accessToken = req.headers["authorization"].split(" ")[1];
    const { user } = jwt.verify(accessToken, process.env.ACCESS_TOKEN_SECRET);
    req.user = user;
    return next();
  } catch (error) {
    res.status(401).json({
      message: "token cannot be verified! please check it again.",
    });
  }
};
Enter fullscreen mode Exit fullscreen mode
  • Before accessToken gets expire, we generate another accessToken in server from the refreshToken we got from the client's request. Then we send both the tokens to the client.
router.post('/token/access', (req, res) => {
  if (
    !req.headers["refresh-token"] &&
    typeof req.headers["refresh-token"] !== "string"
  ) {
    return res.status(401).json({
      message: "No refresh tokens found",
    });
  }

  try {
    const oldRefreshToken = req.headers["refresh-token"].split(" ")[1];
    const { userId } = jwt.verify(
      oldRefreshToken,
      process.env.REFRESH_TOKEN_SECRET
    );
    const refreshToken = jwt.sign(
      { userId: userId },
      process.env.REFRESH_TOKEN_SECRET,
      {
        expiresIn: "7d",
      }
    );
    const accessToken = jwt.sign(
      { user },
      process.env.ACCESS_TOKEN_SECRET,
      {
        expiresIn: "15m",
      }
    );
    res.json({ accessToken, refreshToken });
  } catch (error) {
    res.status(401).json({
      message: "refresh token cannot be verified! please check it again.",
    });
  }
})
Enter fullscreen mode Exit fullscreen mode
  • The refreshToken is generally stored in the localStorage of the browser and the accessToken is stored in the headers of the API requests.

Voila! your app is now secured with the authentication and authorization.
Hope you find this useful.

references
cover image : https://jwt.io/
youtube: https://www.youtube.com/watch?v=7Q17ubqLfaM&t=332s

Top comments (0)