fix(immich): send x-csrf-token header on sync requests (fixes 'CSRF validation failed') #43

Merged
janpeter merged 1 commit from fix/immich-sync-csrf-token into main 2026-06-06 01:52:24 +02:00
Owner

Symptom: the Immich page renders, but clicking a sync button returns 403 'CSRF validation failed'.

Cause: proxy.ts (the Next.js middleware) enforces double-submit CSRF on mutating /api/* requests — the x-csrf-token header must equal the csrf_token cookie (set on GET, httpOnly:false). ImmichSyncButton did fetch(endpoint, { method: "POST" }) without the header. The library mutations work because they're server actions (path isn't /api/, so proxy.ts exempts them).

Fix: add readCsrfToken() (src/lib/csrf.ts) that reads the csrf_token cookie, and have ImmichSyncButton echo it in the x-csrf-token header. runImmichSyncRequest gains an optional csrfToken param so it stays unit-testable; added a test asserting the header is sent.

Verified: lint clean; build OK; button tests 3/3 (incl. the new CSRF-header case).

**Symptom:** the Immich page renders, but clicking a sync button returns **403 'CSRF validation failed'**. **Cause:** `proxy.ts` (the Next.js middleware) enforces **double-submit CSRF** on mutating `/api/*` requests — the `x-csrf-token` header must equal the `csrf_token` cookie (set on GET, `httpOnly:false`). `ImmichSyncButton` did `fetch(endpoint, { method: "POST" })` **without** the header. The library mutations work because they're **server actions** (path isn't `/api/`, so `proxy.ts` exempts them). **Fix:** add `readCsrfToken()` (`src/lib/csrf.ts`) that reads the `csrf_token` cookie, and have `ImmichSyncButton` echo it in the `x-csrf-token` header. `runImmichSyncRequest` gains an optional `csrfToken` param so it stays unit-testable; added a test asserting the header is sent. **Verified:** lint clean; build OK; button tests 3/3 (incl. the new CSRF-header case).
fix(immich): send x-csrf-token header on sync requests
Some checks failed
CI / docker-build (pull_request) Failing after 3s
751640db7b
proxy.ts (the Next.js middleware) enforces double-submit CSRF on mutating
/api/* requests: the x-csrf-token header must equal the csrf_token cookie, or
it returns 403 'CSRF validation failed'. ImmichSyncButton POSTed to
/api/immich/sync/* without the header, so the sync buttons failed. (Other
mutations are server actions, whose path isn't /api/, so they're exempt.)

Add a readCsrfToken() helper that reads the (httpOnly:false) csrf_token cookie,
and have ImmichSyncButton echo it in the x-csrf-token header. runImmichSyncRequest
takes an optional csrfToken so it stays unit-testable; added a test asserting the
header is sent.
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
janpeter/Media-Organizer!43
No description provided.