mirror of
https://github.com/rgrgogu/new_starr.git
synced 2026-09-27 00:12:54 +08:00
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
83 lines
3.0 KiB
JavaScript
83 lines
3.0 KiB
JavaScript
/***********************************************************************************************************************************************************************
|
|
* 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 }; |