/** * CSRF token utilities * Generates and validates CSRF tokens to prevent cross-site request forgery */ import crypto from 'crypto'; const CSRF_TOKEN_LENGTH = 32; const CSRF_TOKEN_EXPIRY = 3600000; // 1 hour in milliseconds // In-memory token store (for production, use Redis or database) const tokenStore = new Map(); // Cleanup expired tokens periodically setInterval(() => { const now = Date.now(); for (const [token, data] of tokenStore.entries()) { if (data.expiresAt < now) { tokenStore.delete(token); } } }, 300000); // Clean every 5 minutes /** * Generate a new CSRF token * @param {string} sessionId - Unique identifier for the user session (e.g., cookie ID) * @returns {string} The generated CSRF token */ export function generateCsrfToken(sessionId) { const token = crypto.randomBytes(CSRF_TOKEN_LENGTH).toString('hex'); const expiresAt = Date.now() + CSRF_TOKEN_EXPIRY; tokenStore.set(token, { sessionId, expiresAt, }); return token; } /** * Validate a CSRF token * @param {string} token - The token to validate * @param {string} sessionId - The session ID to validate against * @returns {boolean} True if token is valid, false otherwise */ export function validateCsrfToken(token, sessionId) { if (!token || !sessionId) { return false; } const data = tokenStore.get(token); if (!data) { return false; } // Check expiry if (data.expiresAt < Date.now()) { tokenStore.delete(token); return false; } // Check session ID match if (data.sessionId !== sessionId) { return false; } // Token is valid - consume it (one-time use) tokenStore.delete(token); return true; } /** * Get token store size (for monitoring) */ export function getTokenStoreSize() { return tokenStore.size; }