Files
fusion/packages/dashboard/src/auth-middleware.ts
2026-04-15 04:01:02 -07:00

130 lines
3.5 KiB
TypeScript

/**
* Bearer token authentication middleware for daemon mode.
*
* Provides secure constant-time token validation to protect API endpoints
* while allowing unauthenticated access to health checks.
*/
import { timingSafeEqual } from "node:crypto";
import type { Request, Response, NextFunction } from "express";
/** Paths that are exempt from authentication (liveness probes). */
const EXEMPT_PATHS = ["/api/health"];
/**
* Check if daemon auth should be active.
* Auth is enabled when FUSION_DAEMON_TOKEN env var is set OR daemon options are provided.
*/
export function isDaemonAuthActive(options?: { daemon?: { token: string } }): boolean {
// Check explicit daemon option
if (options?.daemon?.token) {
return true;
}
// Check environment variable
if (process.env.FUSION_DAEMON_TOKEN) {
return true;
}
return false;
}
/**
* Get the daemon token from options or environment.
*/
function getDaemonToken(options?: { daemon?: { token: string } }): string | undefined {
if (options?.daemon?.token) {
return options.daemon.token;
}
return process.env.FUSION_DAEMON_TOKEN;
}
/**
* Check if a request path is exempt from authentication.
*/
function isExemptPath(path: string): boolean {
return EXEMPT_PATHS.some((exempt) => path === exempt || path.startsWith(exempt + "/"));
}
/**
* Create Express middleware that enforces bearer token authentication.
*
* Uses constant-time comparison to prevent timing attacks.
* Exempts /api/health and paths starting with /api/health/ from auth.
*
* @param token - The valid bearer token
* @returns Express middleware function
*/
export function createAuthMiddleware(token: string) {
const expectedBuffer = Buffer.from(token, "utf8");
return function authMiddleware(req: Request, res: Response, next: NextFunction): void {
// Always allow exempt paths
if (isExemptPath(req.path)) {
next();
return;
}
// Extract Authorization header
const authHeader = req.headers.authorization;
if (!authHeader) {
res.status(401).json({
error: "Unauthorized",
message: "Valid bearer token required",
});
return;
}
// Parse Bearer scheme
if (!authHeader.startsWith("Bearer ")) {
res.status(401).json({
error: "Unauthorized",
message: "Valid bearer token required",
});
return;
}
const providedToken = authHeader.slice(7); // Remove "Bearer " prefix
// Fast path: check length first to avoid unnecessary crypto calls
if (providedToken.length !== expectedBuffer.length) {
res.status(401).json({
error: "Unauthorized",
message: "Valid bearer token required",
});
return;
}
// Constant-time comparison to prevent timing attacks
try {
const providedBuffer = Buffer.from(providedToken, "utf8");
// Ensure buffers are the same length (they should be due to length check above)
if (providedBuffer.length !== expectedBuffer.length) {
res.status(401).json({
error: "Unauthorized",
message: "Valid bearer token required",
});
return;
}
if (!timingSafeEqual(providedBuffer, expectedBuffer)) {
res.status(401).json({
error: "Unauthorized",
message: "Valid bearer token required",
});
return;
}
} catch {
// Buffer encoding issues or other crypto errors
res.status(401).json({
error: "Unauthorized",
message: "Valid bearer token required",
});
return;
}
// Token is valid
next();
};
}