/** * API Key Authentication Middleware * * Allows external tools (like NotiBot) to authenticate via API key * instead of session cookies. Supports: * - Header: X-API-Key: * - Header: Authorization: Bearer * - Query param: ?apiKey= * * If an API key is present and valid, the request is authenticated * as that user (req.user is set, req.isAuthenticated() returns true). * If no API key is present, falls through to normal session auth. */ import { Request, Response, NextFunction } from "express"; import { storage } from "./storage.js"; import { User } from "../shared/schema.js"; export function apiKeyAuth() { return async (req: Request, _res: Response, next: NextFunction) => { // Skip if already authenticated via session if (req.isAuthenticated && req.isAuthenticated()) { return next(); } // Extract API key from various sources let apiKey: string | undefined; // 1. X-API-Key header const xApiKey = req.headers["x-api-key"]; if (typeof xApiKey === "string") { apiKey = xApiKey; } // 2. Authorization: Bearer if (!apiKey) { const auth = req.headers["authorization"]; if (typeof auth === "string" && auth.startsWith("Bearer ")) { apiKey = auth.substring(7); } } // 3. Query parameter if (!apiKey && typeof req.query.apiKey === "string") { apiKey = req.query.apiKey; } if (!apiKey) { return next(); // No API key, fall through to session auth } try { const user = await storage.getUserByApiKey(apiKey); if (user && user.isActive) { // Attach user to request (mimics passport behavior) (req as any).user = user; (req as any).isAuthenticated = () => true; } } catch (err) { console.error("[API Key Auth] Error:", err); } next(); }; }