diff --git a/middleware.ts b/middleware.ts index 990ac9f..76c1499 100644 --- a/middleware.ts +++ b/middleware.ts @@ -19,6 +19,10 @@ * * Health/metrics are exempt (they are also outside the matcher below, but the * exemption is kept explicit for robustness). + * + * FIX (2026-08-31): matcher now covers BOTH /api/0pt (exact) and /api/0pt/:path* + * to close the gap where a bare GET /api/0pt (no trailing path) was routed around + * the middleware on Vercel's Edge runtime. */ import { NextResponse, type NextRequest } from 'next/server'; @@ -30,7 +34,7 @@ assertAuthConfigSafe(); // Only guard the 0pt capability surface. The matcher keeps this middleware off // the UI, auth, and payment routes entirely. export const config = { - matcher: ['/api/0pt/:path*'], + matcher: ['/api/0pt', '/api/0pt/:path*'], }; const EXEMPT_PATTERNS: RegExp[] = [ @@ -39,12 +43,26 @@ const EXEMPT_PATTERNS: RegExp[] = [ /^\/api\/admin\/metrics(\/|$)/, ]; +// Module-level diagnostics counter — first 5 auth requests in a process lifetime +// log their outcome so the production auth gap (S1) can be traced to root cause. +let _authDiagCount = 0; + export async function middleware(req: NextRequest): Promise { const path = req.nextUrl.pathname; if (EXEMPT_PATTERNS.some((re) => re.test(path))) { return NextResponse.next(); } + if (_authDiagCount < 5) { + _authDiagCount += 1; + const hasAuth = !!req.headers.get('authorization'); + console.log( + `[0pt-auth] middleware path=${path} auth_header=${hasAuth} ` + + `auth_enforce=${process.env.AUTH_ENFORCE ?? 'unset'} ` + + `node_env=${process.env.NODE_ENV ?? 'unset'} request#${_authDiagCount}`, + ); + } + const decision = await resolveAuthContext(req.headers); if (!decision.ok) {