"Email PO to vendor" (issue #14) relies on PdfService fetching /api/po/<id>/export?...&svc=<token> WITHOUT a user session, authenticating with a `svc` token that matches PDF_SERVICE_TOKEN. The route handler validates that token, but the auth middleware runs first and its matcher doesn't exempt the export route — so every unauthenticated fetch was redirected to /login (307) and the svc bypass never executed. Net effect: the feature could never render a real PDF on any deployed env, even with the service configured. Fix: middleware now lets exactly `/api/po/<id>/export` through when its `svc` query param matches `process.env.PDF_SERVICE_TOKEN` (the route handler still re-validates it — defense in depth). Everything else stays auth-gated. The match lives in a dependency-free, edge-safe, unit-tested helper (lib/pdf-export-auth.ts); middleware already reads server env at runtime via auth()/NEXTAUTH_SECRET, so reading PDF_SERVICE_TOKEN there is consistent. Verified on a running build: correct svc + real PO -> 200, correct svc + bogus PO -> 404 (handler ran), wrong/no svc -> 307 (still gated). 324 unit tests green; tsc clean. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
24 lines
1 KiB
TypeScript
24 lines
1 KiB
TypeScript
// Service-token auth for the PO export route, shared by the auth middleware and
|
|
// (conceptually) the export route handler.
|
|
//
|
|
// PdfService ("Email PO to vendor", issue #14) fetches `/api/po/<id>/export`
|
|
// WITHOUT a user session, authenticating with a `svc` query param that must equal
|
|
// PDF_SERVICE_TOKEN. The route handler validates that token, but the auth
|
|
// middleware runs first and would otherwise redirect the unauthenticated request
|
|
// to /login — so the middleware uses this to let exactly that one route through
|
|
// when the token matches.
|
|
//
|
|
// Kept dependency-free so it's safe to import into the Edge middleware and easy to
|
|
// unit-test. `token` is `process.env.PDF_SERVICE_TOKEN` (undefined when the PDF
|
|
// service isn't configured → always denied).
|
|
const EXPORT_PATH = /^\/api\/po\/[^/]+\/export\/?$/;
|
|
|
|
export function isPdfExportServiceRequest(
|
|
pathname: string,
|
|
svc: string | null | undefined,
|
|
token: string | undefined
|
|
): boolean {
|
|
if (!token || !svc) return false;
|
|
if (svc !== token) return false;
|
|
return EXPORT_PATH.test(pathname);
|
|
}
|