/*********************************************************************************************************************************************************************** * File Name: token.util.js * Type of Program: Utility * Description: JWT access & refresh token helpers. * - generateTokens() → produces { accessToken, refreshToken } * - verifyAccessToken() → validates and returns decoded payload * - verifyRefreshToken() → validates the long-lived refresh token * - hashToken() → SHA-256 hash used to store refresh tokens in DB * Author: rgrgogu * Date Created: Oct. 6, 2025 *********************************************************************************************************************************************************************** * HOW TO USE: * const { generateTokens, verifyAccessToken } = require('../utils/token.util'); * const { accessToken, refreshToken } = generateTokens(user); ***********************************************************************************************************************************************************************/ const jwt = require('jsonwebtoken'); const crypto = require('crypto'); /** * Generates a signed access token and a signed refresh token. * @param {object} user - Sequelize User instance * @returns {{ accessToken: string, refreshToken: string }} */ const generateTokens = (user) => { const payload = { user_id: user.user_id, email: user.email, acc_type: user.acc_type, reg_type: user.reg_type, }; const accessToken = jwt.sign(payload, process.env.JWT_SECRET, { expiresIn: process.env.JWT_EXPIRES_IN || '1d', }); const refreshToken = jwt.sign( { user_id: user.user_id }, process.env.JWT_REFRESH_SECRET, { expiresIn: process.env.JWT_REFRESH_EXPIRES_IN || '7d' } ); return { accessToken, refreshToken }; }; /** * Verifies an access token. * @param {string} token * @returns {object} decoded payload * @throws if invalid / expired */ const verifyAccessToken = (token) => jwt.verify(token, process.env.JWT_SECRET); /** * Verifies a refresh token. * @param {string} token * @returns {object} decoded payload */ const verifyRefreshToken = (token) => jwt.verify(token, process.env.JWT_REFRESH_SECRET); /** * SHA-256 hash a token string for safe DB storage. * @param {string} token * @returns {string} hex digest */ const hashToken = (token) => crypto.createHash('sha256').update(token).digest('hex'); /** * SHA-256 hash a token string for safe DB storage. * @param {string} decoded * @param {number} thresholdDays * @returns {string} if expired or not */ const shouldRotateRefreshToken = (decoded, thresholdDays = 1) => { const now = Math.floor(Date.now() / 1000); const timeLeft = decoded.exp - now; const threshold = thresholdDays * 24 * 60 * 60; return timeLeft <= threshold; }; module.exports = { generateTokens, verifyAccessToken, verifyRefreshToken, hashToken, shouldRotateRefreshToken };