/** * Simple in-memory rate limiter for login attempts * Tracks login attempts per email address */ interface RateLimitEntry { attempts: number; resetTime: number; } const loginAttempts = new Map(); const ATTEMPT_LIMIT = 5; const TIME_WINDOW_MS = 15 * 60 * 1000; // 15 minutes export function checkRateLimit(email: string): { allowed: boolean; attempts: number; remainingTime: number; } { const now = Date.now(); const entry = loginAttempts.get(email); // No previous attempts if (!entry) { loginAttempts.set(email, { attempts: 1, resetTime: now + TIME_WINDOW_MS, }); return { allowed: true, attempts: 1, remainingTime: TIME_WINDOW_MS, }; } // Window has expired if (now > entry.resetTime) { loginAttempts.set(email, { attempts: 1, resetTime: now + TIME_WINDOW_MS, }); return { allowed: true, attempts: 1, remainingTime: TIME_WINDOW_MS, }; } // Still within the window const remainingTime = entry.resetTime - now; const allowed = entry.attempts < ATTEMPT_LIMIT; if (!allowed) { // Still increment attempts to track that we rejected this attempt return { allowed: false, attempts: entry.attempts, remainingTime, }; } // Increment attempts entry.attempts++; return { allowed: true, attempts: entry.attempts, remainingTime, }; } /** * Record a failed login attempt (call this on invalid credentials) * This increments the counter without checking the limit */ export function recordFailedAttempt(email: string): void { const now = Date.now(); const entry = loginAttempts.get(email); if (!entry || now > entry.resetTime) { loginAttempts.set(email, { attempts: 1, resetTime: now + TIME_WINDOW_MS, }); } else { entry.attempts++; } } /** * Clear all rate limit entries (useful for testing or admin reset) */ export function clearRateLimits(): void { loginAttempts.clear(); }