import { createHash, timingSafeEqual } from "node:crypto";
import type { NextFunction, Request, Response } from "express";
/**
* Authentication middleware for HTTP transport.
*
* Security: Prevents CWE-306 (Missing Authentication for Critical Function) by
* requiring API key authentication when SYNAPSE_API_KEY is configured.
*
* If SYNAPSE_API_KEY is not set, authentication is disabled and all requests are allowed.
* This provides backward compatibility and allows local development without authentication.
*
* When enabled, requires X-API-Key header that matches SYNAPSE_API_KEY exactly.
* Uses timing-safe comparison to prevent timing attacks.
*
* @see https://cwe.mitre.org/data/definitions/306.html
*
* @example
* ```typescript
* // Disable authentication (development/local only)
* // No SYNAPSE_API_KEY env var set
*
* // Enable authentication
* process.env.SYNAPSE_API_KEY = "my-secret-key-12345";
* app.use('/mcp', authMiddleware);
*
* // Client request
* fetch('/mcp', {
* headers: { 'X-API-Key': 'my-secret-key-12345' }
* });
* ```
*/
export function authMiddleware(req: Request, res: Response, next: NextFunction): void {
const configuredKey = process.env.SYNAPSE_API_KEY;
// If no API key is configured, skip authentication (allow all)
if (!configuredKey) {
next();
return;
}
// Get provided API key from header (case-insensitive)
const providedKey = req.headers["x-api-key"] as string | undefined;
// Reject if no key provided or key is empty/whitespace
if (!providedKey || providedKey.trim().length === 0) {
res.status(401).json({
error: "Unauthorized",
message: "Missing or invalid X-API-Key header",
});
return;
}
// Use timing-safe comparison to prevent timing attacks
// Both strings must be same length for timingSafeEqual, so hash both
const providedHash = createHash("sha256").update(providedKey).digest();
const configuredHash = createHash("sha256").update(configuredKey).digest();
const isValid = timingSafeEqual(providedHash, configuredHash);
if (!isValid) {
res.status(401).json({
error: "Unauthorized",
message: "Missing or invalid X-API-Key header",
});
return;
}
// Authentication successful
next();
}