chore: relocate backend into apps/api ahead of monorepo merge

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-24 12:20:40 +08:00
co-authored by Claude Sonnet 5
parent af44598df6
commit eaa6d2276a
388 changed files with 0 additions and 13998 deletions
+83
View File
@@ -0,0 +1,83 @@
/***********************************************************************************************************************************************************************
* 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 };