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!
Subscribe to my newsletter
Read articles from Shusil Chaudhary directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
