I'm trying to implement access and refresh tokens when a user logs into the node.js web server via ajax requests from the web browser.
I have it so when they login, it creates a new access and refresh token and saves it to the user's cookie, access token expires in 1 hour, refresh token expires in 30 days.
Then the tokens in the cookie get authenticated when the user does something, such as views the index page (get) or wants to update their profile (post):
- If the access token is invalid, it looks to the refresh token
- If the access token is valid, it continues with the request
- If the refresh token is invalid, it sends the user back to the login page
- If refresh token is valid, it generates a new access token and a new refresh token and continues with the request
My token verification middleware:
exports.verifyAccessToken = (redirect) => (req, res, next) => {
const accessToken = req.cookies.accessToken;
const refreshToken = req.cookies.refreshToken;
log.debug("verifying, cookies:", req.cookies);
if (!accessToken) {
return redirect ? res.redirect("/login") : res.status(401).json({ message: 'Verification Error: Access Token missing' });
}
jwt.verify(accessToken, process.env.ACCESS_TOKEN_SECRET, (err, decoded) => {
if (err) {
if (!refreshToken) {
return redirect ? res.redirect("/login") : res.status(400).json({ message: 'Verification Error: Access Token invalid/expired, Refresh Token missing' });
}
jwt.verify(refreshToken, process.env.REFRESH_TOKEN_SECRET, (err, decoded) => {
if (err) {
return redirect ? res.redirect("/login") : res.status(403).json({ message: 'Verification Error: Refresh Token invalid/expired' });
}
helper.setNewAccessToken(res, decoded.email);
helper.setNewRefreshToken(res, decoded.email);
req.user = decoded;
next();
});
} else {
req.user = decoded;
next();
}
});
};
Is this a valid way to handle access/refresh tokens? I just don't see the point if the refresh token gets stolen anyways, the whole thing is redundant.
Am I supposed to store the refresh token somewhere else or is it supposed to be in the cookie along with the access token?
What's the proper way to implement this? I want it so the user doesn't have to continuously log back in every hour while also making sure everything is secure.
Your flow is not correct as you should not be sending the access token and the refresh token together from the client side when accessing the Resource. In fact you should have different endpoints for a resource flow (using access token), auth flow (using login credentials) and a refresh flow (using the refresh token). So when client accesses the Resource, you should only be checking the access token.
At the client side, one way to store the tokens is in cookies but the access token and refresh token should be separated as much as possible. You can use httpOnly secure cookies to different endpoints so that the tokens do not get exposed. So for example the refresh token will only be accessible at the refresh endpoint using SSL and it should ideally not be accessible through javascript at client to reduce attack surface.