Give /api/preview-link its own Bearer secret; constant-time token checks
preview-link mints working form links for any contact, but was gated by HEALTH_TOKEN (the diagnostics secret) passed in the query string, where it lands in access logs. README already documented PREVIEW_ADMIN_TOKEN as a Bearer header; make the code match. - PREVIEW_ADMIN_TOKEN, when set, is required as Authorization: Bearer and is never accepted from the URL. - Until it is set, HEALTH_TOKEN still works (Bearer or legacy ?token=) so the support runbook in EMAIL_DELIVERY.md keeps working. - preview-link and /api/health compare tokens in constant time. - redact() also scrubs PREVIEW_ADMIN_TOKEN and STAFF_REPORT_KEY.
This commit is contained in:
@@ -22,6 +22,7 @@ import { NextResponse } from "next/server";
|
||||
import { civi, verifyChecksum } from "@/lib/civicrm";
|
||||
import { ACTIVITY_TYPE_NAME, FORM_CONTACT_RELATIONSHIP } from "@/config/form";
|
||||
import { appEnv } from "@/lib/env";
|
||||
import { safeEqual } from "@/lib/staff-token.mjs";
|
||||
|
||||
interface CheckResult {
|
||||
check: string;
|
||||
@@ -362,7 +363,7 @@ export async function GET(req: Request) {
|
||||
if (!e.healthToken) {
|
||||
return new NextResponse("Not Found", { status: 404 });
|
||||
}
|
||||
if (url.searchParams.get("token") !== e.healthToken) {
|
||||
if (!safeEqual(url.searchParams.get("token") ?? "", e.healthToken)) {
|
||||
return new NextResponse("Not Found", { status: 404 });
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user