diff --git a/packages/csr/src/reactive-runtime.ts b/packages/csr/src/reactive-runtime.ts index 0478bdb5..12e7a62a 100644 --- a/packages/csr/src/reactive-runtime.ts +++ b/packages/csr/src/reactive-runtime.ts @@ -4138,13 +4138,14 @@ export const REACTIVE_RUNTIME = String.raw` } function callServerFunction(component, functionName, args) { - var csrf = document.querySelector('meta[name="wrnexus-csrf"]'); + var csrfMatch = /(?:^|;\s*)wire-csrf=([^;]+)/.exec(document.cookie || ""); + var csrf = csrfMatch ? decodeURIComponent(csrfMatch[1]) : ""; return fetch("/__wrnexus/rpc", { method: "POST", credentials: "same-origin", headers: { "content-type": "application/json", - "x-csrf-token": csrf ? csrf.getAttribute("content") || "" : "", + "x-csrf-token": csrf, }, body: JSON.stringify({ component: component, function: functionName, args: args || [] }), }).then(function (response) { diff --git a/packages/csr/test/reactive.test.ts b/packages/csr/test/reactive.test.ts index 8c233bc9..e0fe8683 100644 --- a/packages/csr/test/reactive.test.ts +++ b/packages/csr/test/reactive.test.ts @@ -801,6 +801,33 @@ test("an unbound native-named output does not re-enter its DOM handler", () => { expect(outputs).toBe(1); }); +test("component server calls send the CSRF cookie in the RPC header", async () => { + const win = mount( + `
` + + `` + + `
`, + ); + Object.defineProperty(win.document, "cookie", { + configurable: true, + value: "wire-csrf=rpc%20token", + }); + let request: RequestInit | undefined; + const globals = globalThis as Record; + const originalFetch = globals.fetch; + globals.fetch = async (_input: RequestInfo | URL, init?: RequestInit) => { + request = init; + return Response.json({ ok: true }); + }; + try { + (win.document.querySelector("button") as unknown as HTMLElement).click(); + await new Promise((resolve) => setTimeout(resolve, 0)); + } finally { + globals.fetch = originalFetch; + } + + expect(new Headers(request?.headers).get("x-csrf-token")).toBe("rpc token"); +}); + test("development runtime warns when a component binding names a missing function", () => { const win = new Window() as unknown as Window & Record; win.document.body.innerHTML =