import { z } from 'zod'; import type { SecurityOperation } from '@/types/auth'; import type { UserRole } from '@/hooks/useUserRole'; /** * Validation schemas for security operations */ export const sessionRevocationSchema = z.object({ sessionId: z.string().uuid('Invalid session ID'), requiresConfirmation: z.boolean().default(true), }); export const identityOperationSchema = z.object({ provider: z.enum(['google', 'discord']), redirectTo: z.string().url().optional(), }); export const mfaOperationSchema = z.object({ factorId: z.string().uuid('Invalid factor ID').optional(), code: z.string().length(6, 'Code must be 6 digits').regex(/^\d+$/, 'Code must be numeric').optional(), }); export const passwordChangeSchema = z.object({ currentPassword: z.string().min(1, 'Current password required'), newPassword: z.string() .min(8, 'Must be at least 8 characters') .max(128, 'Must be less than 128 characters') .regex(/[A-Z]/, 'Must contain uppercase letter') .regex(/[a-z]/, 'Must contain lowercase letter') .regex(/[0-9]/, 'Must contain number') .regex(/[^A-Za-z0-9]/, 'Must contain special character'), confirmPassword: z.string(), captchaToken: z.string().min(1, 'CAPTCHA verification required'), }).refine(data => data.newPassword === data.confirmPassword, { message: "Passwords don't match", path: ["confirmPassword"] }); /** * Determines if an operation requires CAPTCHA verification */ export function requiresCaptcha(operation: SecurityOperation): boolean { const captchaOperations: SecurityOperation[] = [ 'password_change', 'identity_disconnect', 'mfa_unenroll' ]; return captchaOperations.includes(operation); } /** * Determines if an operation requires MFA verification for the user's role */ export function requiresMFA( operation: SecurityOperation, userRoles: UserRole[] ): boolean { const privilegedRoles: UserRole[] = ['moderator', 'admin', 'superuser']; const hasPrivilegedRole = userRoles.some(role => privilegedRoles.includes(role)); if (!hasPrivilegedRole) return false; // MFA required for these operations if user is privileged const mfaOperations: SecurityOperation[] = [ 'password_change', 'session_revoke', 'mfa_unenroll' ]; return mfaOperations.includes(operation); } /** * Get rate limit parameters for a security operation */ export function getRateLimitParams(operation: SecurityOperation): { action: string; maxAttempts: number; windowMinutes: number; } { const limits: Record = { password_change: { action: 'password_change', maxAttempts: 3, windowMinutes: 60 }, identity_disconnect: { action: 'identity_disconnect', maxAttempts: 3, windowMinutes: 60 }, identity_connect: { action: 'identity_connect', maxAttempts: 5, windowMinutes: 60 }, session_revoke: { action: 'session_revoke', maxAttempts: 10, windowMinutes: 60 }, mfa_enroll: { action: 'mfa_enroll', maxAttempts: 3, windowMinutes: 60 }, mfa_unenroll: { action: 'mfa_unenroll', maxAttempts: 2, windowMinutes: 1440 }, // Phase 4: 2 per day }; return limits[operation] || { action: operation, maxAttempts: 5, windowMinutes: 60 }; }