Notification bell uses raw fetch() instead of lt.api, breaking CSRF token resync #57

Open
opened 2026-08-31 21:40:10 -04:00 by jared · 0 comments
Owner

Severity: Medium

views/layout_footer.php (~lines 237-258) — both loadNotifications() and the "mark all read" handler call fetch() directly instead of lt.api.*, violating the project's own documented convention (README Dev Notes #20: "No raw fetch() ... The wrapper auto-adds ... X-CSRF-Token ... auto-parses JSON").

Concretely: api/bootstrap.php rotates the CSRF token on every successful write and returns the new token in the response's csrf_token field; lt.api.* reads that field and updates window.CSRF_TOKEN (base.js ~lines 478/2728) — but this raw-fetch mark-read POST never does.

Impact: After a user clicks "Mark all read," the server has rotated its token but the client's cached window.CSRF_TOKEN is now stale. The user's next write anywhere else in the app (comment, status change, etc., via lt.api) sends the stale token, gets a 403 "Invalid CSRF token" on the first attempt, and only resyncs from that rejection — a reproducible one-off failure on every session that clicks "mark all read" then immediately tries another action.

Fix: Replace both fetch() calls with lt.api.get/lt.api.post so the CSRF token stays in sync like every other write in the app.

**Severity:** Medium `views/layout_footer.php` (~lines 237-258) — both `loadNotifications()` and the "mark all read" handler call `fetch()` directly instead of `lt.api.*`, violating the project's own documented convention (README Dev Notes #20: "No raw fetch() ... The wrapper auto-adds ... X-CSRF-Token ... auto-parses JSON"). **Concretely:** `api/bootstrap.php` rotates the CSRF token on every successful write and returns the new token in the response's `csrf_token` field; `lt.api.*` reads that field and updates `window.CSRF_TOKEN` (base.js ~lines 478/2728) — but this raw-fetch mark-read POST never does. **Impact:** After a user clicks "Mark all read," the server has rotated its token but the client's cached `window.CSRF_TOKEN` is now stale. The user's *next* write anywhere else in the app (comment, status change, etc., via `lt.api`) sends the stale token, gets a 403 "Invalid CSRF token" on the first attempt, and only resyncs from that rejection — a reproducible one-off failure on every session that clicks "mark all read" then immediately tries another action. **Fix:** Replace both `fetch()` calls with `lt.api.get`/`lt.api.post` so the CSRF token stays in sync like every other write in the app.
jared added the priority/mediumux labels 2026-09-08 10:15:46 -04:00
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: LotusGuild/tinker_tickets#57