I am baffled. I am trying to follow a tutorial to create a api with authentication using refresh and access tokens.
I have it working when the access token doesn’t need to be updated/refreshed. However, as soon as it does I run into an issue.
I can’t check if the refreshToken is valid because I am storing refresh token in a database (containing the userId and the refresh token). However, I can’t fetch it, because I can’t get the userId from req.user.userId since the access token is expired meaning that the userId is now undefined. Therefore I can’t see if the refresh token is valid, and can’t create a new access token
/*ISSUE: I need to get userID here to check if the refresh token is valid via database,
but req.user is undefined since the access token expired */
const storedRefreshToken = await findRefreshToken(req.user.userId);
if (!storedRefreshToken) {
return res.status(401).json({ error: 'Invalid refresh token' });
}
Maybe I am completely misunderstanding the guide I am following because this makes no sense, feel free to tell me I am completely doing everything wrong
authentication Middleware:
const { verify } = require('jsonwebtoken');
const { findRefreshToken} = require('./userModel');
const { generateAccessToken} = require('./authHelper');
const tokenValidation = async (req, res, next) => {
const accessToken = req.header('Authorization') ? req.header('Authorization').replace('Bearer ', '') : null;
if (!accessToken) {
return res.status(401).json({ error: 'Access token not found' });
}
try {
const decoded = verify(accessToken, process.env.JWT_SECRET);
req.user = decoded;
next();
} catch (error) {
if (error.name === 'TokenExpiredError') {
try {
const storedRefreshToken = await findRefreshToken(req.user.userId /*I NEED TO GET THE userID here to check if the refresh token is valid but can't*/);
if (!storedRefreshToken) {
return res.status(401).json({ error: 'Invalid refresh token' });
}
const newAccessToken = generateAccessToken(newRefreshToken);
req.user = jwt.verify(newAccessToken, process.env.JWT_SECRET);
res.setHeader('Authorization', `Bearer ${newAccessToken}`);
next();
} catch (error) {
return res.status(401).json({ error: 'Failed to refresh token' });
}
} else {
return res.status(401).json({ error: 'Invalid token' });
}
}
};
module.exports = tokenValidation;
User Model for storing/fetching refresh tokens:
const storeRefreshToken = async (userId, refreshToken) => {
const [result] = await db.execute(
'INSERT INTO refresh_tokens (userId, refreshToken) VALUES (?, ?) ON DUPLICATE KEY UPDATE refreshToken = VALUES(refreshToken)',
[userId, refreshToken]
);
return result;
};
const findRefreshToken = async (userId) => {
const [rows] = await db.execute('SELECT * FROM refreshToken WHERE userId = ?', [userId]);
return rows[0];
};
Generating tokens:
const { sign } = require('jsonwebtoken');
const generateAccessToken = (userId) => {
return sign({ userId }, process.env.JWT_SECRET, { expiresIn: '10s' }); /*10s for testing*/
};
module.exports = {
generateAccessToken
};