CSRF resync updates the token but never auto-retries the failed request — every stale-token case surfaces a user-visible error first #86

Open
opened 2026-09-01 00:08:17 -04:00 by jared · 0 comments
Owner

Severity: Medium

assets/js/base.js (~lines 472-481, apiFetch) resyncs global.CSRF_TOKEN from a 403 response's csrf_token field, but then still throws — every caller sees a raw "Invalid CSRF token" error toast on the FIRST attempt, with no transparent retry.

Concrete trigger: CsrfMiddleware's token lifetime is 3600s (1h) while the session idle timeout is 18000s (5h) — confirmed in middleware/CsrfMiddleware.php. So the CSRF token silently expires roughly once per hour of page inactivity, meaning any user who leaves a ticket page open and idle for over an hour, then makes their first edit, will see a confusing "Invalid CSRF token" error even though the retry would have succeeded automatically. Same experience after a write in another tab rotates the shared token.

Impact: Not a security bug — the underlying rejection/resync logic is working correctly — but a real UX/robustness gap: a transparent one-shot retry (resync token, then automatically re-send the original request once) would eliminate this class of error entirely for end users.

Fix: In apiFetch's CSRF-rejection handling, after resyncing CSRF_TOKEN from the response, automatically retry the original request once with the new token before surfacing an error to the caller.

**Severity:** Medium `assets/js/base.js` (~lines 472-481, `apiFetch`) resyncs `global.CSRF_TOKEN` from a 403 response's `csrf_token` field, but then still `throw`s — every caller sees a raw "Invalid CSRF token" error toast on the FIRST attempt, with no transparent retry. **Concrete trigger:** `CsrfMiddleware`'s token lifetime is 3600s (1h) while the session idle timeout is 18000s (5h) — confirmed in `middleware/CsrfMiddleware.php`. So the CSRF token silently expires roughly once per hour of page inactivity, meaning any user who leaves a ticket page open and idle for over an hour, then makes their first edit, will see a confusing "Invalid CSRF token" error even though the retry would have succeeded automatically. Same experience after a write in another tab rotates the shared token. **Impact:** Not a security bug — the underlying rejection/resync logic is working correctly — but a real UX/robustness gap: a transparent one-shot retry (resync token, then automatically re-send the original request once) would eliminate this class of error entirely for end users. **Fix:** In `apiFetch`'s CSRF-rejection handling, after resyncing `CSRF_TOKEN` from the response, automatically retry the original request once with the new token before surfacing an error to the caller.
jared added the priority/mediumux labels 2026-09-08 10:15:49 -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#86