import { Request, Response, NextFunction } from 'express'; import config from '../services/config.js'; import logger from '../services/logger.js'; interface AuthenticatedRequest extends Request { apiKeyType?: 'frontend' | 'external'; } export const apiKeyAuth = ( req: AuthenticatedRequest, res: Response, next: NextFunction ): void => { if (req.path === '/health') { next(); return; } const apiKey = req.header('X-API-Key') || (req.query.apikey as string); if (!apiKey) { logger.warn('API request without API key', { ip: req.ip, path: req.path }); res.status(401).json({ error: 'Unauthorized', message: 'API key is required', }); return; } // Check frontend API key if (apiKey === config.apiKeys.frontend) { // Validate origin for frontend API key const origin = req.headers.origin || req.headers.referer; const allowedOrigins = config.frontendAllowedOrigins; // If no origin/referer header, check if it's a same-origin request by checking Host header if (!origin) { const host = req.headers.host; const protocol = req.protocol || 'http'; const requestOrigin = `${protocol}://${host}`; // Check if the request comes from an allowed origin based on Host header const isSameOriginAllowed = allowedOrigins.some(allowed => requestOrigin.startsWith(allowed) ); if (!isSameOriginAllowed) { logger.warn('Frontend API key used without origin/referer header', { ip: req.ip, path: req.path, host: host, requestOrigin: requestOrigin, userAgent: req.headers['user-agent'], }); res.status(403).json({ error: 'Forbidden', message: 'Origin header required for frontend API key', }); return; } // Same-origin request allowed req.apiKeyType = 'frontend'; next(); return; } const isOriginAllowed = allowedOrigins.some(allowed => origin.startsWith(allowed) ); if (!isOriginAllowed) { logger.warn('Frontend API key used from invalid origin', { ip: req.ip, origin: origin, path: req.path, allowedOrigins: allowedOrigins, }); res.status(403).json({ error: 'Forbidden', message: 'Invalid origin for frontend API key', }); return; } req.apiKeyType = 'frontend'; next(); return; } // Check external API keys if (config.apiKeys.external.includes(apiKey)) { req.apiKeyType = 'external'; next(); return; } logger.warn('API request with invalid API key', { ip: req.ip, path: req.path, apiKey: apiKey.substring(0, 8) + '...', }); res.status(401).json({ error: 'Unauthorized', message: 'Invalid API key', }); }; export default apiKeyAuth;