I want to send the cookie that stores the refresh token to the server to request for a new access token.
This is the server code to resend the access token to the client by validating the refresh token.
It works in development, but in production, it’s giving 401 error. I have found that the cookies are not sent to the server when in production. How would I solve this?
const handleRefreshToken = async (req, res) => {
const cookies = req.cookies;
process.env.NODE_ENV === "development" &&
console.log("Cookies at refreshRequest: ", cookies);
if (!cookies?.jwt) {
return res.sendStatus(401);
}
const refreshToken = cookies.jwt;
res.clearCookie("jwt", {
httpOnly: true,
secure: process.env.NODE_ENV === "production", // true if in production
sameSite: "None", // 'None' allows cross-origin requests with cookies
maxAge: 24 * 60 * 60 * 1000, // 1 day
});
try {
const foundUser = await prisma.user.findFirst({
where: {
refreshToken: {
has: refreshToken,
},
},
});
//Detected refresh token reuse
if (!foundUser) {
jwt.verify(
refreshToken,
process.env.REFRESH_TOKEN_SECRET,
async (err, decoded) => {
if (err) {
return res.sendStatus(403);
}
process.env.NODE_ENV === "development" &&
console.log("Attempted Refresh Token Reuse");
const hackedUser = await prisma.user.update({
where: {
username: decoded.username,
},
data: {
refreshToken: [],
},
});
process.env.NODE_ENV === "development" && console.log(hackedUser);
}
);
return res.sendStatus(403); //Unauthrorized
}
const newRefreshTokenArray = foundUser.refreshToken.filter(
(rt) => rt !== refreshToken
);
//Evaluate JWT
jwt.verify(
refreshToken,
process.env.REFRESH_TOKEN_SECRET,
async (err, decoded) => {
if (err) {
process.env.NODE_ENV === "development" &&
console.log("Expired Refresh Token: ");
await prisma.user.update({
where: { id: foundUser.id },
data: { refreshToken: newRefreshTokenArray },
});
return res.sendStatus(403);
}
if (foundUser.username !== decoded.username) {
return res.sendStatus(403);
}
//Refresh token is still valid
const accessToken = jwt.sign(
{
UserInfo: {
username: foundUser.username,
id: foundUser.id,
},
},
process.env.ACCESS_TOKEN_SECRET,
{ expiresIn: "15m" }
);
const newRefreshToken = jwt.sign(
{
username: foundUser.username,
},
process.env.REFRESH_TOKEN_SECRET,
{ expiresIn: "1d" }
);
// Update user with the new refresh token
await prisma.user.update({
where: { id: foundUser.id },
data: { refreshToken: [...newRefreshTokenArray, newRefreshToken] },
});
// Set the new refresh token as a cookie
res.cookie("jwt", newRefreshToken, {
httpOnly: true,
sameSite: "Lax",
secure: process.env.NODE_ENV === "production" ? true : false,
maxAge: 24 * 60 * 60 * 1000,
});
res.json({ accessToken: accessToken });
}
);
} catch (error) {
process.env.NODE_ENV === "development" &&
console.error("Error handling refresh token:", error);
res.sendStatus(500);
}
};
module.exports = {
handleRefreshToken,
};
I have tried setting “SameSite to None”. It’s giving 500 error. I am using axios to send the request to the server from client. ‘withCredentials’ header is set to ‘true’ in axios.
import axios from "../api/axios";
import useAuth from "./useAuth";
const refreshPath = import.meta.env.VITE_API_REFRESH_PATH;
const useRefreshToken = () => {
const { setAuth } = useAuth();
const refresh = async () => {
try {
const response = await axios.get(refreshPath, {
withCredentials: true,
});
setAuth((prev) => {
import.meta.env.DEV && console.log("AT Before: ", JSON.stringify(prev));
import.meta.env.DEV &&
console.log("AT After: ", response.data.accessToken);
return {
...prev,
accessToken: response.data.accessToken,
};
});
return response.data.accessToken;
} catch (error) {
import.meta.env.DEV &&
console.error("Failed to fetch refresh token", error);
throw new Error(error); // Re-throw the error to handle it in the calling function
}
};
return refresh;
};
export default useRefreshToken;
This is the useRefreshToken hook the fetches a new access token using refresh token in the cookie
Rohith A is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.