"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>
26 lines
1.4 KiB
TypeScript
26 lines
1.4 KiB
TypeScript
import { describe, it, expect } from "vitest";
|
|
import { isPdfExportServiceRequest } from "@/lib/pdf-export-auth";
|
|
|
|
const TOKEN = "a".repeat(64);
|
|
|
|
describe("isPdfExportServiceRequest", () => {
|
|
it("allows the export route when the svc token matches", () => {
|
|
expect(isPdfExportServiceRequest("/api/po/cmqrug123/export", TOKEN, TOKEN)).toBe(true);
|
|
expect(isPdfExportServiceRequest("/api/po/cmqrug123/export/", TOKEN, TOKEN)).toBe(true); // trailing slash
|
|
});
|
|
|
|
it("denies when the token is missing, empty, or wrong", () => {
|
|
expect(isPdfExportServiceRequest("/api/po/x/export", TOKEN, undefined)).toBe(false); // service not configured
|
|
expect(isPdfExportServiceRequest("/api/po/x/export", null, TOKEN)).toBe(false); // no svc on request
|
|
expect(isPdfExportServiceRequest("/api/po/x/export", "", TOKEN)).toBe(false);
|
|
expect(isPdfExportServiceRequest("/api/po/x/export", "wrong", TOKEN)).toBe(false);
|
|
});
|
|
|
|
it("only matches the PO export route, not other paths", () => {
|
|
expect(isPdfExportServiceRequest("/api/po/x/export/extra", TOKEN, TOKEN)).toBe(false);
|
|
expect(isPdfExportServiceRequest("/api/po/x", TOKEN, TOKEN)).toBe(false);
|
|
expect(isPdfExportServiceRequest("/dashboard", TOKEN, TOKEN)).toBe(false);
|
|
expect(isPdfExportServiceRequest("/api/reports/spend", TOKEN, TOKEN)).toBe(false);
|
|
expect(isPdfExportServiceRequest("/api/po//export", TOKEN, TOKEN)).toBe(false); // empty id
|
|
});
|
|
});
|