// src/config.js // === ADDED FOR RATE LIMITING === // Environment variables for rate limiting (used in middleware/rateLimitMiddleware.js): // - REDIS_URL: Full Redis connection URL (e.g., redis://localhost:6379) // OR use REDIS_HOST and REDIS_PORT separately // - REDIS_HOST: Redis host (default: localhost) // - REDIS_PORT: Redis port (default: 6379) // - REDIS_PASSWORD: Redis password (optional) // - OTP_REQ_PHONE_10MIN_LIMIT: Max OTP requests per phone per 10 min (default: 3) // - OTP_REQ_PHONE_DAY_LIMIT: Max OTP requests per phone per 24 hours (default: 10) // - OTP_REQ_IP_10MIN_LIMIT: Max OTP requests per IP per 10 min (default: 20) // - OTP_REQ_IP_DAY_LIMIT: Max OTP requests per IP per 24 hours (default: 100) // - OTP_VERIFY_MAX_ATTEMPTS: Max verification attempts per OTP (default: 5) // - OTP_VERIFY_FAILED_PER_HOUR_LIMIT: Max failed verifications per phone per hour (default: 10) // - OTP_TTL_SECONDS: OTP validity in seconds (default: 120, i.e., 2 minutes) // // === SECURITY HARDENING: JWT KEY ROTATION === // - JWT_ACTIVE_KEY_ID: Key ID to use for signing new tokens (default: '1') // - JWT_KEYS_JSON: JSON object mapping key IDs to secrets, e.g. '{"1":"secret1","2":"secret2"}' // OR use legacy: JWT_ACCESS_SECRET, JWT_REFRESH_SECRET // - JWT_REFRESH_KEY_ID: Key ID for refresh tokens (default: same as active key) // - JWT_ISSUER: Issuer claim (iss) for tokens (default: 'farm-auth-service') // - JWT_AUDIENCE: Audience claim (aud) for tokens (default: 'mobile-app') // // === SECURITY HARDENING: IP/DEVICE RISK === // - BLOCKED_IP_RANGES: Comma-separated CIDR blocks to block (e.g., '10.0.0.0/8,172.16.0.0/12') // - REQUIRE_OTP_ON_SUSPICIOUS_REFRESH: Require OTP re-verification on suspicious refresh (default: false) // // === SECURITY HARDENING: ACCESS TOKEN REPLAY MITIGATION === // - STEP_UP_OTP_WINDOW_MINUTES: Time window for "recent" OTP verification (default: 5) // // === SECURITY HARDENING: CORS === // - CORS_ALLOWED_ORIGINS: Comma-separated list of allowed origins (REQUIRED in production) // Example: 'https://app.example.com,https://api.example.com' // WARNING: Never use '*' when credentials or tokens are involved require('dotenv').config(); const REQUIRED_ENV = [ 'DATABASE_URL', 'JWT_ACCESS_SECRET', 'JWT_REFRESH_SECRET', ]; const missing = REQUIRED_ENV.filter((key) => !process.env[key]); if (missing.length > 0) { throw new Error( `Missing required environment variables: ${missing.join(', ')}` ); } const corsAllowedOrigins = (process.env.CORS_ALLOWED_ORIGINS || '') .split(',') .map((origin) => origin.trim()) .filter(Boolean); const isProduction = process.env.NODE_ENV === 'production'; const refreshMaxIdleMinutes = Number( process.env.REFRESH_MAX_IDLE_MINUTES || 4320 ); if (isProduction && corsAllowedOrigins.length === 0) { throw new Error( 'CORS_ALLOWED_ORIGINS must be set (comma-separated) in production' ); } module.exports = { databaseUrl: process.env.DATABASE_URL, jwtAccessSecret: process.env.JWT_ACCESS_SECRET, jwtRefreshSecret: process.env.JWT_REFRESH_SECRET, jwtAccessTtl: process.env.JWT_ACCESS_TTL || '15m', jwtRefreshTtl: process.env.JWT_REFRESH_TTL || '7d', corsAllowedOrigins, isProduction, refreshMaxIdleMinutes, };