Initial commit
This commit is contained in:
94
lib/auth/rate-limit.ts
Normal file
94
lib/auth/rate-limit.ts
Normal file
@@ -0,0 +1,94 @@
|
||||
/**
|
||||
* Simple in-memory rate limiter for login attempts
|
||||
* Tracks login attempts per email address
|
||||
*/
|
||||
|
||||
interface RateLimitEntry {
|
||||
attempts: number;
|
||||
resetTime: number;
|
||||
}
|
||||
|
||||
const loginAttempts = new Map<string, RateLimitEntry>();
|
||||
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();
|
||||
}
|
||||
Reference in New Issue
Block a user