This commit is contained in:
rgrgogu
2026-05-06 14:15:04 +08:00
parent a8f10a25d7
commit d5fcc574a4
12 changed files with 389 additions and 219 deletions
+12 -15
View File
@@ -28,7 +28,7 @@ const bcrypt = require('bcryptjs');
const sequelize = require('../config/db.config')
const mdl_Users = require('../models/users/users.mdl');
const mdl_UserSessions = require('../models/users/user_sessions.mdl');
const { generateTokens, verifyRefreshToken, hashToken } = require('../utils/token.util');
const { generateTokens, verifyRefreshToken, hashToken, shouldRotateRefreshToken } = require('../utils/token.util');
const { generateOTP, getOTPExpiry, isOTPExpired } = require('../utils/otp.util');
const sendEmail = require('../services/email.service');
const R = require('../utils/response.util');
@@ -124,7 +124,7 @@ exports.verifyOTP = async (req, res) => {
sameSite: process.env.NODE_ENV === 'production' ? 'strict' : 'lax', // ← CSRF protection
maxAge: 7 * 24 * 60 * 60 * 1000, // 7 days
});
return R.success(res, 'Email verified successfully. You are now logged in.', {
accessToken,
refreshToken,
@@ -234,13 +234,9 @@ exports.refreshToken = async (req, res) => {
const refreshToken = req.cookies.refreshToken;
if (!refreshToken) return R.error(res, 'Refresh token is required.', 400);
console.log('[REFRESH] called, token tail:', refreshToken?.slice(-10))
console.log('[REFRESH] hash:', hashToken(refreshToken))
const decoded = verifyRefreshToken(refreshToken);
const tokenHash = hashToken(refreshToken);
console.log({ user_id: decoded.user_id, refresh_token_hash: tokenHash, is_active: true })
const session = await mdl_UserSessions.findOne({
where: { user_id: decoded.user_id, refresh_token_hash: tokenHash, is_active: true },
});
@@ -249,20 +245,21 @@ exports.refreshToken = async (req, res) => {
const user = await mdl_Users.findByPk(decoded.user_id);
if (!user || !user.is_active) return R.error(res, 'User not found or deactivated.', 401);
const tokens = generateTokens(user);
// Check if refresh token is expired
if (!shouldRotateRefreshToken(decoded)) {
const { accessToken } = generateTokens(user);
return R.success(res, 'Token refreshed.', { accessToken, user: safeUser(user) });
}
// Rotate refresh token
// ─── Rotate refresh token ───────────────────────────────────────────────────
const tokens = generateTokens(user);
await session.update({ refresh_token_hash: hashToken(tokens.refreshToken) });
console.log('[REFRESH] old token tail:', refreshToken?.slice(-10))
console.log('[REFRESH] new token tail:', tokens.refreshToken?.slice(-10))
console.log('[REFRESH] are they different:', refreshToken !== tokens.refreshToken)
res.cookie('refreshToken', tokens.refreshToken, {
httpOnly: true, // ← JS cannot read this
httpOnly: true,
secure: process.env.NODE_ENV === 'production',
sameSite: process.env.NODE_ENV === 'production' ? 'strict' : 'lax', // ← CSRF protection
maxAge: 7 * 24 * 60 * 60 * 1000, // 7 days
sameSite: process.env.NODE_ENV === 'production' ? 'strict' : 'lax',
maxAge: 7 * 24 * 60 * 60 * 1000,
});
return R.success(res, 'Token refreshed.', { ...tokens, user: safeUser(user) });