refactor: replace custom HMAC CSRF with hono/csrf middleware

Removes 38-line hand-rolled HMAC-SHA256 implementation in favour of
the built-in hono/csrf, which validates the Origin header natively.

- Delete src/utils/csrf.ts
- Replace custom CSRF middleware with hono/csrf (Origin-header check)
- Remove csrfToken from ContextVariableMap, layout(), forms, and JS fetch() calls
- Update admin tests: swap X-CSRF-Token for Origin header

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Julien Herr
2026-05-22 10:28:26 +02:00
parent 7d375693b9
commit a0415cdc41
4 changed files with 26 additions and 116 deletions
+16 -16
View File
@@ -3,20 +3,17 @@ import { Hono } from "hono";
import app from "./admin";
import { createMockEnv } from "../test/setup";
import { Env } from "../types";
import { generateCsrfToken } from "../utils/csrf";
describe("Admin Routes", () => {
let testApp: Hono;
let mockEnv: Env;
let csrfToken: string;
let request: (path: string, init?: RequestInit) => Promise<Response>;
let loginAndGetCookie: () => Promise<string>;
beforeEach(async () => {
beforeEach(() => {
mockEnv = createMockEnv() as unknown as Env;
testApp = new Hono();
testApp.route("/admin", app);
csrfToken = await generateCsrfToken("test-password");
request = (path, init = {}) =>
Promise.resolve(testApp.request(path, init, mockEnv));
loginAndGetCookie = async () => {
@@ -97,7 +94,7 @@ describe("Admin Routes", () => {
const res = await request("/admin", {
headers: {
Cookie: authCookie,
"X-CSRF-Token": csrfToken,
Origin: "https://test.getmynews.app",
},
});
expect(res.status).toBe(200);
@@ -143,7 +140,7 @@ describe("Admin Routes", () => {
method: "POST",
headers: {
Cookie: authCookie,
"X-CSRF-Token": csrfToken,
Origin: "https://test.getmynews.app",
},
body: formData,
});
@@ -180,7 +177,7 @@ describe("Admin Routes", () => {
method: "POST",
headers: {
Cookie: authCookie,
"X-CSRF-Token": csrfToken,
Origin: "https://test.getmynews.app",
},
body: formData,
});
@@ -201,7 +198,7 @@ describe("Admin Routes", () => {
headers: {
Cookie: authCookie,
"Content-Type": "application/json",
"X-CSRF-Token": csrfToken,
Origin: "https://test.getmynews.app",
},
body: JSON.stringify({ title: "", description: "desc" }),
});
@@ -248,7 +245,7 @@ describe("Admin Routes", () => {
method: "POST",
headers: {
Cookie: authCookie,
"X-CSRF-Token": csrfToken,
Origin: "https://test.getmynews.app",
},
body: formData,
});
@@ -267,7 +264,7 @@ describe("Admin Routes", () => {
method: "POST",
headers: {
Cookie: authCookie,
"X-CSRF-Token": csrfToken,
Origin: "https://test.getmynews.app",
},
});
@@ -300,7 +297,7 @@ describe("Admin Routes", () => {
method: "POST",
headers: {
Cookie: authCookie,
"X-CSRF-Token": csrfToken,
Origin: "https://test.getmynews.app",
},
body: formData,
});
@@ -320,7 +317,7 @@ describe("Admin Routes", () => {
headers: {
Cookie: authCookie,
Accept: "application/json",
"X-CSRF-Token": csrfToken,
Origin: "https://test.getmynews.app",
},
},
);
@@ -340,7 +337,10 @@ describe("Admin Routes", () => {
formData.append("description", "Test");
const createRes = await request("/admin/feeds/create", {
method: "POST",
headers: { Cookie: authCookie, "X-CSRF-Token": csrfToken },
headers: {
Cookie: authCookie,
Origin: "https://test.getmynews.app",
},
body: formData,
});
expect(createRes.status).toBe(302);
@@ -361,7 +361,7 @@ describe("Admin Routes", () => {
const bulkDeleteRes = await request("/admin/feeds/bulk-delete", {
method: "POST",
headers: { Cookie: authCookie, "X-CSRF-Token": csrfToken },
headers: { Cookie: authCookie, Origin: "https://test.getmynews.app" },
body: bulkForm,
});
@@ -490,7 +490,7 @@ describe("Admin Routes", () => {
method: "POST",
headers: {
Cookie: authCookie,
"X-CSRF-Token": csrfToken,
Origin: "https://test.getmynews.app",
},
body: formData,
});
@@ -540,7 +540,7 @@ describe("Admin Routes", () => {
headers: {
Cookie: authCookie,
Accept: "application/json",
"X-CSRF-Token": csrfToken,
Origin: "https://test.getmynews.app",
},
},
);