UNPKG

@ordojs/security

Version:

Security package for OrdoJS with XSS, CSRF, and injection protection

175 lines 5.16 kB
/** * CSRF Session Manager * Manages CSRF sessions and token storage */ export class CSRFSessionManager { sessions = new Map(); config; cleanupInterval = null; constructor(config) { this.config = config; this.startCleanupTimer(); } /** * Create a new CSRF session */ createSession(sessionId) { const session = { id: sessionId, tokens: new Map(), createdAt: Date.now(), lastActivity: Date.now() }; this.sessions.set(sessionId, session); return session; } /** * Get an existing session or create a new one */ getOrCreateSession(sessionId) { let session = this.sessions.get(sessionId); if (!session) { session = this.createSession(sessionId); } else { // Update last activity session.lastActivity = Date.now(); } return session; } /** * Add a token to a session */ addToken(sessionId, token) { const session = this.getOrCreateSession(sessionId); session.tokens.set(token.value, token); // Clean up expired tokens for this session this.cleanupSessionTokens(session); } /** * Validate a token for a session */ validateSessionToken(sessionId, tokenValue) { const session = this.sessions.get(sessionId); if (!session) { return { valid: false, error: 'Session not found' }; } const token = session.tokens.get(tokenValue); if (!token) { return { valid: false, error: 'Token not found in session' }; } // Check if token has expired if (Date.now() > token.expiresAt) { // Remove expired token session.tokens.delete(tokenValue); return { valid: false, error: 'Token expired', expired: true }; } // Update session activity session.lastActivity = Date.now(); return { valid: true }; } /** * Remove a token from a session (for one-time use tokens) */ consumeToken(sessionId, tokenValue) { const session = this.sessions.get(sessionId); if (!session) { return false; } return session.tokens.delete(tokenValue); } /** * Get all active tokens for a session */ getSessionTokens(sessionId) { const session = this.sessions.get(sessionId); if (!session) { return []; } // Clean up expired tokens first this.cleanupSessionTokens(session); return Array.from(session.tokens.values()); } /** * Remove a session and all its tokens */ removeSession(sessionId) { return this.sessions.delete(sessionId); } /** * Clean up expired tokens for a specific session */ cleanupSessionTokens(session) { const now = Date.now(); const expiredTokens = []; for (const [tokenValue, token] of session.tokens) { if (now > token.expiresAt) { expiredTokens.push(tokenValue); } } expiredTokens.forEach(tokenValue => { session.tokens.delete(tokenValue); }); } /** * Clean up expired sessions and tokens */ cleanup() { const now = Date.now(); const sessionTimeout = this.config.tokenExpiry * 2; // Sessions expire after 2x token expiry const expiredSessions = []; for (const [sessionId, session] of this.sessions) { // Remove sessions that haven't been active for too long if (now - session.lastActivity > sessionTimeout) { expiredSessions.push(sessionId); continue; } // Clean up expired tokens in active sessions this.cleanupSessionTokens(session); } // Remove expired sessions expiredSessions.forEach(sessionId => { this.sessions.delete(sessionId); }); } /** * Start automatic cleanup timer */ startCleanupTimer() { // Run cleanup every 15 minutes this.cleanupInterval = setInterval(() => { this.cleanup(); }, 15 * 60 * 1000); } /** * Stop cleanup timer */ destroy() { if (this.cleanupInterval) { clearInterval(this.cleanupInterval); this.cleanupInterval = null; } this.sessions.clear(); } /** * Get session statistics */ getStats() { let totalTokens = 0; let activeSessions = 0; const now = Date.now(); const sessionTimeout = this.config.tokenExpiry * 2; for (const session of this.sessions.values()) { totalTokens += session.tokens.size; if (now - session.lastActivity <= sessionTimeout) { activeSessions++; } } return { totalSessions: this.sessions.size, totalTokens, activeSessions }; } } //# sourceMappingURL=session-manager.js.map