// ============================================================ // AUTHENTICATION & SESSION // ============================================================ /** * AUTHENTICATION & SESSION CONFIGURATION * * Security Model: * - Access tokens: Short-lived (15m), contain user identity, stored in httpOnly cookie * - Refresh tokens: Long-lived (7-90d), opaque tokens for getting new access tokens * - Token rotation: Each refresh invalidates old token and issues new pair * - Breach detection: Reusing invalidated token revokes entire token family * * Cookie Behavior: * - rememberMe = false: Session cookies (no maxAge) - expire when browser closes * - rememberMe = true: Persistent cookies (with maxAge) - survive browser restart * * Timing Decisions: * - 15m access: Balance between security (short exposure) and UX (not too frequent refreshes) * - 7d session: DB expiry for non-remember-me (cookie is session-only but accommodates users who keep browser open) * - 90d remember: Extended convenience for trusted devices (both DB and cookie persist) * - 5s reuse window: Handles race conditions in distributed systems */ export const AUTH_CONFIG = { // Access Token (JWT in cookie) ACCESS_TOKEN_EXPIRY: "15m" as const, // 15 minutes (short-lived) ACCESS_TOKEN_EXPIRY_DEV: "2m" as const, // 2 minutes for faster testing // Refresh Token (opaque token in separate cookie) REFRESH_TOKEN_EXPIRY_SHORT: "7d" as const, // 7 days (DB expiry for non-remember me - accommodates users who keep browser open) REFRESH_TOKEN_EXPIRY_LONG: "90d" as const, // 90 days (remember me - both DB and cookie persist) // Security Settings REFRESH_TOKEN_ROTATION_ENABLED: true, // Enable token rotation MAX_ROTATION_COUNT: 1000, // Max rotations before forcing re-login (1000 * 15m = 10.4 days in prod, 1000 * 2m = 33 hours in dev) REFRESH_TOKEN_REUSE_WINDOW_MS: 5000, // 5s grace period for race conditions // Session Cleanup (serverless-friendly opportunistic cleanup) SESSION_CLEANUP_INTERVAL_HOURS: 24, // Check for cleanup every 24 hours SESSION_CLEANUP_RETENTION_DAYS: 90, // Keep revoked sessions for 90 days (audit) // Other Auth Settings CSRF_TOKEN_MAX_AGE: 60 * 60 * 24 * 14, EMAIL_LOGIN_LINK_EXPIRY: "15m" as const, EMAIL_VERIFICATION_LINK_EXPIRY: "15m" as const, LINEAGE_JWT_EXPIRY: "14d" as const } as const; /** * Get access token expiry based on environment */ export function getAccessTokenExpiry(): string { return process.env.NODE_ENV === "production" ? AUTH_CONFIG.ACCESS_TOKEN_EXPIRY : AUTH_CONFIG.ACCESS_TOKEN_EXPIRY_DEV; } /** * Convert expiry string to seconds for cookie Max-Age * @param expiry - Expiry string like "15m", "7d", "90d" * @returns Seconds as number */ export function expiryToSeconds(expiry: string): number { if (expiry.endsWith("m")) { return parseInt(expiry) * 60; } else if (expiry.endsWith("h")) { return parseInt(expiry) * 60 * 60; } else if (expiry.endsWith("d")) { return parseInt(expiry) * 60 * 60 * 24; } throw new Error(`Invalid expiry format: ${expiry}`); } /** * Get access cookie maxAge based on environment (in seconds) */ export function getAccessCookieMaxAge(): number { return expiryToSeconds(getAccessTokenExpiry()); } /** * Get refresh cookie maxAge based on rememberMe preference (in seconds) */ export function getRefreshCookieMaxAge(rememberMe: boolean): number { return expiryToSeconds( rememberMe ? AUTH_CONFIG.REFRESH_TOKEN_EXPIRY_LONG : AUTH_CONFIG.REFRESH_TOKEN_EXPIRY_SHORT ); } /** * Type helper for token expiry strings */ export type TokenExpiry = | typeof AUTH_CONFIG.ACCESS_TOKEN_EXPIRY | typeof AUTH_CONFIG.REFRESH_TOKEN_EXPIRY_SHORT | typeof AUTH_CONFIG.REFRESH_TOKEN_EXPIRY_LONG; // ============================================================ // RATE LIMITING // ============================================================ export const RATE_LIMITS = { LOGIN_IP: { maxAttempts: 5, windowMs: 15 * 60 * 1000 }, LOGIN_EMAIL: { maxAttempts: 5, windowMs: 60 * 60 * 1000 }, PASSWORD_RESET_IP: { maxAttempts: 3, windowMs: 60 * 60 * 1000 }, REGISTRATION_IP: { maxAttempts: 3, windowMs: 60 * 60 * 1000 }, EMAIL_VERIFICATION_IP: { maxAttempts: 5, windowMs: 15 * 60 * 1000 } } as const; /** Rate limit store cleanup interval (5 minutes) */ export const RATE_LIMIT_CLEANUP_INTERVAL_MS = 5 * 60 * 1000; // ============================================================ // ACCOUNT SECURITY // ============================================================ export const ACCOUNT_LOCKOUT = { MAX_FAILED_ATTEMPTS: 5, LOCKOUT_DURATION_MS: 5 * 60 * 1000 } as const; export const PASSWORD_RESET_CONFIG = { TOKEN_EXPIRY_MS: 60 * 60 * 1000 } as const; // ============================================================ // COOLDOWN TIMERS (CLIENT-SIDE COOKIES) // ============================================================ export const COOLDOWN_TIMERS = { EMAIL_LOGIN_LINK_MS: 30 * 1000, EMAIL_LOGIN_LINK_COOKIE_MAX_AGE: 2 * 60, PASSWORD_RESET_REQUEST_MS: 5 * 60 * 1000, PASSWORD_RESET_REQUEST_COOKIE_MAX_AGE: 5 * 60, CONTACT_REQUEST_MS: 1 * 60 * 1000, CONTACT_REQUEST_COOKIE_MAX_AGE: 1 * 60, EMAIL_VERIFICATION_MS: 15 * 60 * 1000, EMAIL_VERIFICATION_COOKIE_MAX_AGE: 15 * 60 } as const; // ============================================================ // CACHE & DATA PERSISTENCE // ============================================================ export const CACHE_CONFIG = { BLOG_CACHE_TTL_MS: 24 * 60 * 60 * 1000, GIT_ACTIVITY_CACHE_TTL_MS: 10 * 60 * 1000, BLOG_POSTS_LIST_CACHE_TTL_MS: 15 * 60 * 1000, MAX_STALE_DATA_MS: 7 * 24 * 60 * 60 * 1000, GIT_ACTIVITY_MAX_STALE_MS: 24 * 60 * 60 * 1000, // Session activity tracking - only update DB if last update was > threshold SESSION_ACTIVITY_UPDATE_THRESHOLD_MS: 5 * 60 * 1000, // 5 minutes // Rate limit in-memory cache TTL (reduces DB reads) RATE_LIMIT_CACHE_TTL_MS: 60 * 1000, // 1 minute // Analytics batching - buffer writes in memory ANALYTICS_BATCH_SIZE: 10, // Write to DB every N events ANALYTICS_BATCH_TIMEOUT_MS: 30 * 1000 // Or every 30 seconds } as const; // ============================================================ // NETWORK & API // ============================================================ export const NETWORK_CONFIG = { EMAIL_API_TIMEOUT_MS: 15 * 1000, GITHUB_API_TIMEOUT_MS: 15 * 1000, GOOGLE_API_TIMEOUT_MS: 15 * 1000, MAX_RETRIES: 2, RETRY_DELAY_MS: 1000 } as const; // ============================================================ // UI/UX - TYPEWRITER COMPONENT // ============================================================ export const TYPEWRITER_CONFIG = { DEFAULT_SPEED: 30, FAST_SPEED: 80, SLOW_SPEED: 10, VERY_SLOW_SPEED: 100, EXTRA_SLOW_SPEED: 120, DEFAULT_KEEP_ALIVE_MS: 2000, LONG_KEEP_ALIVE_MS: 10000, DEFAULT_DELAY_MS: 500, CURSOR_FADE_DELAY_MS: 1000 } as const; // ============================================================ // UI/UX - COUNTDOWN TIMER COMPONENT // ============================================================ export const COUNTDOWN_CONFIG = { EMAIL_LOGIN_LINK_DURATION_S: 30, PASSWORD_RESET_DURATION_S: 300, CONTACT_FORM_DURATION_S: 60, PASSWORD_RESET_SUCCESS_DURATION_S: 5, DEFAULT_TIMER_SIZE_PX: 48, LARGE_TIMER_SIZE_PX: 200, DEFAULT_STROKE_WIDTH: 6, LARGE_STROKE_WIDTH: 12 } as const; // ============================================================ // UI/UX - RESPONSIVE BREAKPOINTS // ============================================================ export const BREAKPOINTS = { MOBILE_MAX_WIDTH: 768, TABLET_MAX_WIDTH: 1024, DESKTOP_MIN_WIDTH: 1025 } as const; // ============================================================ // UI/UX - ANIMATIONS & TRANSITIONS // ============================================================ export const ANIMATION_CONFIG = { TRANSITION_DURATION_MS: 300, FAST_TRANSITION_MS: 200, SLOW_TRANSITION_MS: 500, EXTRA_SLOW_TRANSITION_MS: 600, SIDEBAR_DURATION_MS: 500, MENU_TYPING_DELAY_MS: 140, MENU_INITIAL_DELAY_MS: 500, SUCCESS_MESSAGE_DURATION_MS: 3000, ERROR_MESSAGE_DURATION_MS: 5000, REDIRECT_DELAY_MS: 500 } as const; // ============================================================ // UI/UX - PDF VIEWER // ============================================================ export const PDF_CONFIG = { RENDER_SCALE: 1.5 } as const; // ============================================================ // UI/UX - 401 ERROR PAGE // ============================================================ export const ERROR_PAGE_CONFIG = { GLITCH_INTERVAL_MS: 300, GLITCH_DURATION_MS: 100, PARTICLE_COUNT: 45 } as const; // ============================================================ // UI/UX - MOBILE CONFIG // ============================================================ export const MOBILE_CONFIG = { SCROLL_THRESHOLD: 75, SWIPE_THRESHOLD: 50 } as const; // ============================================================ // UI/UX - TEXT EDITOR // ============================================================ export const TEXT_EDITOR_CONFIG = { CONTEXT_SIZE: 256, MAX_HISTORY_SIZE: 100, HISTORY_DEBOUNCE_MS: 2000, INFILL_DEBOUNCE_MS: 500, INFILL_MAX_TOKENS: 100, INFILL_TEMPERATURE: 0.3, MERMAID_VALIDATION_DEBOUNCE_MS: 500, LEGACY_MIGRATION_DELAY_MS: 50, INITIAL_HISTORY_CAPTURE_DELAY_MS: 200, INITIAL_LOAD_FALLBACK_DELAY_MS: 500, INITIAL_LOAD_DELAY_MS: 1000, SPINNER_INTERVAL_MS: 50, HIGHLIGHT_FADE_DELAY_MS: 400, HIGHLIGHT_REMOVE_DELAY_MS: 1000, REFERENCE_UPDATE_DELAY_MS: 500, SCROLL_TO_CHANGE_DELAY_MS: 100 } as const; // ============================================================ // VALIDATION // ============================================================ export const VALIDATION_CONFIG = { MIN_PASSWORD_LENGTH: 8, PASSWORD_REQUIRE_UPPERCASE: true, PASSWORD_REQUIRE_NUMBER: true, PASSWORD_REQUIRE_SPECIAL: false, MAX_CONTACT_MESSAGE_LENGTH: 500, MIN_PASSWORD_CONF_LENGTH_FOR_ERROR: 6 } as const; // ============================================================ // LINEAGE GAME (MOBILE APP) // ============================================================ export const LINEAGE_CONFIG = { DELETION_GRACE_PERIOD_MS: 24 * 60 * 60 * 1000, PVP_OPPONENTS_COUNT: 3 } as const; // ============================================================ // AUDIT & LOGGING // ============================================================ export const AUDIT_CONFIG = { DEFAULT_QUERY_LIMIT: 100, MAX_RETENTION_DAYS: 90 } as const; // ============================================================ // SESSION CLEANUP // ============================================================ export const SESSION_CLEANUP_CONFIG = { ENABLED: true, INTERVAL_HOURS: 24, RETENTION_DAYS: 90, RUN_ON_STARTUP: true } as const;