From 7497ede2fd41da4a0a4b893ec371747f8cc3cf45 Mon Sep 17 00:00:00 2001 From: "diana.dolgolyova" Date: Tue, 17 Mar 2026 17:57:49 +0300 Subject: [PATCH] fix: auto-issue CSRF cookie for existing sessions Sessions from before CSRF was added lack the bh-csrf-token cookie, causing 403 on first save. Middleware now auto-generates the cookie if the user is authenticated but missing it. Co-Authored-By: Claude Opus 4.6 (1M context) --- src/proxy.ts | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/src/proxy.ts b/src/proxy.ts index 8e8a71a..2cf0afc 100644 --- a/src/proxy.ts +++ b/src/proxy.ts @@ -5,6 +5,14 @@ const CSRF_COOKIE_NAME = "bh-csrf-token"; const CSRF_HEADER_NAME = "x-csrf-token"; const STATE_CHANGING_METHODS = new Set(["POST", "PUT", "DELETE", "PATCH"]); +function generateCsrfToken(): string { + const array = new Uint8Array(32); + crypto.getRandomValues(array); + let binary = ""; + for (const b of array) binary += String.fromCharCode(b); + return btoa(binary).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, ""); +} + export async function proxy(request: NextRequest) { const { pathname } = request.nextUrl; @@ -24,6 +32,21 @@ export async function proxy(request: NextRequest) { return NextResponse.redirect(new URL("/admin/login", request.url)); } + // Auto-issue CSRF cookie if missing (e.g. session from before CSRF was added) + const hasCsrf = request.cookies.has(CSRF_COOKIE_NAME); + if (!hasCsrf) { + const csrfToken = generateCsrfToken(); + const response = NextResponse.next(); + response.cookies.set(CSRF_COOKIE_NAME, csrfToken, { + httpOnly: false, + secure: process.env.NODE_ENV === "production", + sameSite: "strict", + path: "/", + maxAge: 60 * 60 * 24, + }); + return response; + } + // CSRF check on state-changing API requests if (pathname.startsWith("/api/admin/") && STATE_CHANGING_METHODS.has(request.method)) { const csrfCookie = request.cookies.get(CSRF_COOKIE_NAME)?.value;