// app/utils/refreshSession.util.js const crypto = require("crypto"); const { generateRefreshToken, verifyRefreshToken, } = require("./jwt.util"); const refreshSessions = new Map(); // Default = 7 days const REFRESH_SESSION_TTL = 7 * 24 * 60 * 60 * 1000; const hashRefreshToken = (token) => { return crypto .createHash("sha256") .update(token) .digest("hex"); }; const createRefreshSession = (userId) => { // Unique session ID const sessionId = crypto.randomUUID(); // Create raw Refresh JWT const refreshToken = generateRefreshToken({ sub: userId, sid: sessionId, }); // Hash raw refresh token const tokenHash = hashRefreshToken( refreshToken ); // Expiration time const expiresAt = Date.now() + REFRESH_SESSION_TTL; // Save only HASH in RAM refreshSessions.set( sessionId, { userId, tokenHash, expiresAt, } ); return { refreshToken, sessionId, }; }; const validateRefreshSession = ( refreshToken ) => { if (!refreshToken) { return null; } let decoded; try { decoded = verifyRefreshToken( refreshToken ); } catch (error) { return null; } const sessionId = decoded.sid; const userId = decoded.sub; if (!sessionId || !userId) { return null; } // Get session from RAM const session = refreshSessions.get( sessionId ); if (!session) { return null; } // Check session expiration if ( Date.now() > session.expiresAt ) { refreshSessions.delete( sessionId ); return null; } // Hash received refresh token const receivedHash = hashRefreshToken( refreshToken ); // Compare stored hash if ( receivedHash !== session.tokenHash ) { return null; } // Extra user check if ( session.userId !== userId ) { return null; } return { userId, sessionId, }; }; const deleteRefreshSession = ( sessionId ) => { refreshSessions.delete( sessionId ); }; const deleteAllUserSessions = ( userId ) => { for ( const [sessionId, session] of refreshSessions.entries() ) { if ( session.userId === userId ) { refreshSessions.delete( sessionId ); } } }; module.exports = { createRefreshSession, validateRefreshSession, deleteRefreshSession, deleteAllUserSessions, };