From e8fad5f3fe90a191d3f6852337d476c33b25cb2c Mon Sep 17 00:00:00 2001 From: 0X-SquidSol Date: Thu, 9 Apr 2026 12:44:48 -0400 Subject: [PATCH] fix: log auth failures in requireApiKey middleware MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Both 401 return paths (missing key, invalid key) were completely silent — no logger imported, no logging of any kind. Operators could not detect brute-force attempts or correlate auth failures with other suspicious activity. Adds structured warn-level logging with client IP, request path, and method for both failure modes. Follows the same pattern used by the rate-limit middleware. Co-Authored-By: Claude Opus 4.6 (1M context) --- src/middleware/auth.ts | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/src/middleware/auth.ts b/src/middleware/auth.ts index 4be6887..6b73a0f 100644 --- a/src/middleware/auth.ts +++ b/src/middleware/auth.ts @@ -1,5 +1,8 @@ import type { Context, Next } from "hono"; import { timingSafeEqual, createHash } from "node:crypto"; +import { createLogger } from "@percolator/shared"; + +const logger = createLogger("api:auth"); /** * C2: API key auth middleware for mutation endpoints. @@ -17,17 +20,22 @@ export function requireApiKey() { } return next(); } - + const provided = c.req.header("x-api-key"); if (!provided) { + logger.warn("Auth failure: missing x-api-key", { + path: c.req.path, + method: c.req.method, + ip: c.req.header("x-forwarded-for")?.split(",")[0]?.trim() ?? "unknown", + }); return c.json({ error: "Unauthorized: invalid or missing x-api-key" }, 401); } - + // Use timing-safe comparison to prevent timing attacks // Hash both values to ensure equal length for timingSafeEqual const providedHash = createHash("sha256").update(provided).digest(); const expectedHash = createHash("sha256").update(apiAuthKey).digest(); - + let isValid = false; try { isValid = timingSafeEqual(providedHash, expectedHash); @@ -35,11 +43,16 @@ export function requireApiKey() { // timingSafeEqual throws if buffers are different lengths (shouldn't happen with hashes) isValid = false; } - + if (!isValid) { + logger.warn("Auth failure: invalid x-api-key", { + path: c.req.path, + method: c.req.method, + ip: c.req.header("x-forwarded-for")?.split(",")[0]?.trim() ?? "unknown", + }); return c.json({ error: "Unauthorized: invalid or missing x-api-key" }, 401); } - + return next(); }; }