How to Refresh Access Tokens in Node.js (Step-by-Step Guide)

When building a secure web app, you want to protect your users without making them log in every few minutes. That’s where access tokens and refresh tokens come in.

In this guide, we’ll walk through how to refresh access tokens using Node.js, JWT, and cookies, and explain a sample function called refreshAccessToken.


What Are Access and Refresh Tokens?

  • Access Token: A short-lived token (e.g., 15 minutes) that lets users access protected routes (like profile pages).

  • Refresh Token: A long-lived token (e.g., 7 days) that helps get a new access token after the old one expires.

Why use both?

  • Access tokens expire quickly for safety.

  • Refresh tokens let users stay logged in without typing their password again.


Tools Used

  • JWT: To create and verify tokens.

  • MongoDB: To store the user and their refresh token.

  • Express.js: Backend framework.

  • Cookies: To send/receive tokens securely.


The refreshAccessToken Function

Let’s look at the code that handles refreshing an access token:

const refreshAccessToken = asyncHandler(async(req, res) => {
  const inComingRefreshToken = req.cookies?.refreshToken || req.body.refreshToken;

  if (!inComingRefreshToken) {
    throw new ApiError(400, "Unauthorized request");
  }

  try {
    const decodedToken = jwt.verify(inComingRefreshToken, process.env.REFRESH_TOKEN_SECRET);
    const user = await User.findById(decodedToken?._id);

    if (!user) {
      throw new ApiError(400, "Invalid refresh token");
    }

    if (inComingRefreshToken !== user.refreshToken) {
      throw new ApiError(400, "Refresh token expired");
    }

    const options = {
      httpOnly: true,
      secure: true,
    };

    const { accessToken, refreshToken: newRefreshToken } = await generateAccessAndRefreshTokens(user._id);

    return res
      .status(200)
      .cookie("accessToken", accessToken, options)
      .cookie("refreshToken", newRefreshToken, options)
      .json(
        new ApiResponse(
          200,
          { newRefreshToken, accessToken },
          "Access Token refreshed successfully"
        )
      );
  } catch (error) {
    throw new ApiError(400, error?.message || "Invalid refresh token");
  }
});

How It Works (Step by Step)

1. Get the Refresh Token

const inComingRefreshToken = req.cookies?.refreshToken || req.body.refreshToken;
  • The refresh token is read from cookies or the request body.

  • It’s more secure to use cookies (especially httpOnly cookies) to prevent attacks.


2. Check If Token Exists

if (!inComingRefreshToken) {
  throw new ApiError(400, "Unauthorized request");
}
  • If there’s no token, we reject the request.

3. Verify the Token

const decodedToken = jwt.verify(inComingRefreshToken, process.env.REFRESH_TOKEN_SECRET);
  • This checks if the refresh token is real and not expired.

  • The secret key is stored in your .env file.


4. Find the User

const user = await User.findById(decodedToken?._id);
  • We look up the user based on the ID inside the token.

5. Match Token in Database

if (inComingRefreshToken !== user.refreshToken) {
  throw new ApiError(400, "Refresh token expired");
}
  • This checks if the token sent by the user matches the one stored in the database.

  • If someone tries to reuse an old or invalid token, it will be blocked.


6. Create New Tokens

const { accessToken, refreshToken: newRefreshToken } = await generateAccessAndRefreshTokens(user._id);
  • We generate a new access token and a new refresh token.

7. Send New Tokens in Cookies

.cookie("accessToken", accessToken, options)
.cookie("refreshToken", newRefreshToken, options)
  • We send the new tokens back using secure cookies:

    • httpOnly: Can’t be accessed by JavaScript.

    • secure: Only sent over HTTPS.


8. Return a Response

res.json({ newRefreshToken, accessToken });
  • The client gets both new tokens to keep the session going.

Best Practices

  • Always store tokens securely.

  • Use httpOnly cookies to prevent XSS attacks.

  • Replace the refresh token every time you use it (token rotation).

  • Consider blacklisting old tokens in a database or cache if needed.


Summary

With this setup, users stay logged in securely, and you can rotate tokens safely without requiring constant logins. This makes your app more secure and user-friendly.

If you’d like the full code or a GitHub repo with this setup, just let me know!

0
Subscribe to my newsletter

Read articles from Shusil Chaudhary directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Shusil Chaudhary
Shusil Chaudhary