From 577bb2e56e0b406d6c56bd70a9cb40699a36a556 Mon Sep 17 00:00:00 2001 From: Marko Djordjevic Date: Wed, 18 Feb 2026 11:02:51 +0100 Subject: [PATCH] feat: add API key auth middleware for /api/* routes (task 3.1) - Create src/middleware.ts with Next.js middleware - Reads API_KEY env var and checks X-API-Key header on all /api/* routes - Skips auth for /api/health endpoint - Fails open (with warning) when API_KEY is not configured - Returns 401 Unauthorized when key is missing or mismatched - Mark task 3.1 as complete in tasks.md Co-Authored-By: Claude Sonnet 4.6 --- openspec/changes/code-review-fix/tasks.md | 2 +- src/middleware.ts | 33 +++++++++++++++++++++++ 2 files changed, 34 insertions(+), 1 deletion(-) create mode 100644 src/middleware.ts diff --git a/openspec/changes/code-review-fix/tasks.md b/openspec/changes/code-review-fix/tasks.md index 9de7ef5..cbe64bf 100644 --- a/openspec/changes/code-review-fix/tasks.md +++ b/openspec/changes/code-review-fix/tasks.md @@ -19,7 +19,7 @@ ## 3. Authentication -- [ ] 3.1 `[sonnet]` Create `src/middleware.ts` with API key auth middleware: read `API_KEY` env var, check `X-API-Key` header on all `/api/*` routes except `/api/health`, return 401 if invalid +- [x] 3.1 `[sonnet]` Create `src/middleware.ts` with API key auth middleware: read `API_KEY` env var, check `X-API-Key` header on all `/api/*` routes except `/api/health`, return 401 if invalid - [ ] 3.2 `[sonnet]` Add FastAPI `Depends()` API key dependency in `services/ml/app/main.py`: read `API_KEY` env var, check `X-API-Key` header, exempt `/health` endpoint - [ ] 3.3 `[sonnet]` Update all Next.js proxy routes to forward `X-API-Key` header to ML service - [ ] 3.4 `[haiku]` Add `API_KEY` to `.env.example` with placeholder value and instructions diff --git a/src/middleware.ts b/src/middleware.ts new file mode 100644 index 0000000..dda9010 --- /dev/null +++ b/src/middleware.ts @@ -0,0 +1,33 @@ +import { NextResponse } from "next/server"; +import type { NextRequest } from "next/server"; + +export function middleware(request: NextRequest) { + const { pathname } = request.nextUrl; + + // Skip auth check for the health endpoint + if (pathname === "/api/health") { + return NextResponse.next(); + } + + const apiKey = process.env.API_KEY; + + // If API_KEY is not configured, skip auth check (fail-open for development) + if (!apiKey) { + console.warn( + "Warning: API_KEY environment variable is not set. API authentication is disabled." + ); + return NextResponse.next(); + } + + const requestApiKey = request.headers.get("X-API-Key"); + + if (!requestApiKey || requestApiKey !== apiKey) { + return NextResponse.json({ error: "Unauthorized" }, { status: 401 }); + } + + return NextResponse.next(); +} + +export const config = { + matcher: ["/api/:path*"], +};