UNPKG

cui-server

Version:

Web UI Agent Platform based on Claude Code

113 lines 3.75 kB
import { ConfigService } from '../services/config-service.js'; import { createLogger } from '../services/logger.js'; const logger = createLogger('AuthMiddleware'); // Rate limiting storage (in-memory for simplicity) const rateLimitStore = new Map(); const RATE_LIMIT_WINDOW = 1000; // 1 second const MAX_ATTEMPTS = 10; /** * Clean up expired rate limit entries */ function cleanupRateLimit() { const now = Date.now(); for (const [ip, data] of rateLimitStore.entries()) { if (now >= data.resetTime) { rateLimitStore.delete(ip); } } } /** * Clear all rate limit entries (for testing) */ export function clearRateLimitStore() { rateLimitStore.clear(); } /** * Check if IP has exceeded rate limit */ function isRateLimited(ip) { cleanupRateLimit(); const data = rateLimitStore.get(ip); if (!data) { return false; } return data.attempts >= MAX_ATTEMPTS && Date.now() < data.resetTime; } /** * Record failed auth attempt */ function recordFailedAttempt(ip) { cleanupRateLimit(); const now = Date.now(); const data = rateLimitStore.get(ip); if (!data || now >= data.resetTime) { rateLimitStore.set(ip, { attempts: 1, resetTime: now + RATE_LIMIT_WINDOW }); } else { data.attempts++; } } /** * Creates authentication middleware for API endpoints * @param tokenOverride - Optional token to use instead of config token * @returns Express middleware function */ export function createAuthMiddleware(tokenOverride) { return (req, res, next) => { authMiddlewareImpl(req, res, next, tokenOverride); }; } /** * Authentication middleware for API endpoints * Validates Bearer token against config * Disabled in test environment unless ENABLE_AUTH_IN_TESTS is set */ export function authMiddleware(req, res, next) { authMiddlewareImpl(req, res, next); } function authMiddlewareImpl(req, res, next, tokenOverride) { // Skip auth in test environment unless explicitly enabled for auth tests if (process.env.NODE_ENV === 'test' && !process.env.ENABLE_AUTH_IN_TESTS) { next(); return; } const clientIp = req.ip || req.connection.remoteAddress || 'unknown'; // Check rate limiting if (isRateLimited(clientIp)) { logger.warn('Rate limit exceeded', { ip: clientIp }); res.status(429).json({ error: 'Too many authentication attempts. Try again later.' }); return; } try { // Extract Bearer token from Authorization header const authHeader = req.headers.authorization; if (!authHeader || !authHeader.startsWith('Bearer ')) { recordFailedAttempt(clientIp); logger.debug('Missing or invalid Authorization header', { ip: clientIp }); res.status(401).json({ error: 'Unauthorized' }); return; } const token = authHeader.substring(7); // Remove 'Bearer ' prefix // Get expected token from config or use override const expectedToken = tokenOverride ?? ConfigService.getInstance().getConfig().authToken; // Validate token if (token !== expectedToken) { recordFailedAttempt(clientIp); logger.warn('Invalid auth token', { ip: clientIp }); res.status(401).json({ error: 'Unauthorized' }); return; } // Token is valid, proceed logger.debug('Authentication successful', { ip: clientIp }); next(); } catch (error) { recordFailedAttempt(clientIp); logger.error('Authentication error', { error, ip: clientIp }); res.status(401).json({ error: 'Unauthorized' }); } } //# sourceMappingURL=auth.js.map