77 lines
2.1 KiB
TypeScript
77 lines
2.1 KiB
TypeScript
import crypto from "crypto";
|
|
|
|
// Simple in-memory CAPTCHA store (in production, use Redis or database)
|
|
const captchaStore: Map<string, { code: string; createdAt: number; attempts: number }> = new Map();
|
|
|
|
const CAPTCHA_TTL = 5 * 60 * 1000; // 5 minutes
|
|
const MAX_ATTEMPTS = 5;
|
|
const CLEANUP_INTERVAL = 10 * 60 * 1000; // 10 minutes
|
|
|
|
// Cleanup old captchas periodically
|
|
setInterval(() => {
|
|
const now = Date.now();
|
|
for (const [key, value] of captchaStore.entries()) {
|
|
if (now - value.createdAt > CAPTCHA_TTL) {
|
|
captchaStore.delete(key);
|
|
}
|
|
}
|
|
}, CLEANUP_INTERVAL);
|
|
|
|
export function generateCaptcha(identifier: string): { id: string; code: string } {
|
|
const code = Math.random().toString().slice(2, 8); // 6-digit code
|
|
const id = crypto.randomBytes(16).toString("hex");
|
|
|
|
captchaStore.set(id, {
|
|
code,
|
|
createdAt: Date.now(),
|
|
attempts: 0,
|
|
});
|
|
|
|
return { id, code };
|
|
}
|
|
|
|
export interface CaptchaVerificationResult {
|
|
success: boolean;
|
|
error?: string;
|
|
attemptsRemaining?: number;
|
|
}
|
|
|
|
export function verifyCaptcha(id: string, code: string): CaptchaVerificationResult {
|
|
const captcha = captchaStore.get(id);
|
|
|
|
if (!captcha) {
|
|
return { success: false, error: "CAPTCHA not found or expired. Please reload and try again." };
|
|
}
|
|
|
|
// Check if expired
|
|
if (Date.now() - captcha.createdAt > CAPTCHA_TTL) {
|
|
captchaStore.delete(id);
|
|
return { success: false, error: "CAPTCHA code has expired. Please request a new one." };
|
|
}
|
|
|
|
// Check attempts
|
|
if (captcha.attempts >= MAX_ATTEMPTS) {
|
|
captchaStore.delete(id);
|
|
return { success: false, error: "Too many incorrect attempts. Please request a new CAPTCHA code." };
|
|
}
|
|
|
|
// Verify code
|
|
if (captcha.code === code) {
|
|
captchaStore.delete(id);
|
|
return { success: true };
|
|
}
|
|
|
|
// Increment attempts
|
|
captcha.attempts++;
|
|
const remaining = MAX_ATTEMPTS - captcha.attempts;
|
|
return {
|
|
success: false,
|
|
error: `Incorrect CAPTCHA code. ${remaining} attempt${remaining === 1 ? "" : "s"} remaining.`,
|
|
attemptsRemaining: remaining,
|
|
};
|
|
}
|
|
|
|
export function getMaxAttempts(): number {
|
|
return MAX_ATTEMPTS;
|
|
}
|