fix: resolve iOS PWA session/CSRF issues causing forbidden errors
- Renew CSRF cookie on /auth/me (first call after iOS PWA resume) - Add try-catch + hex validation to CSRF middleware for corrupted tokens - Auto-retry state-changing requests on 403 by refreshing CSRF token - Add 200ms delay before SW controllerchange reload to prevent blank page on iOS
This commit is contained in:
+8
-1
@@ -22,7 +22,7 @@ function getCsrfToken() {
|
||||
* @param {RequestInit} options - Fetch-Optionen
|
||||
* @returns {Promise<any>} Geparstes JSON oder wirft einen Fehler
|
||||
*/
|
||||
async function apiFetch(path, options = {}) {
|
||||
async function apiFetch(path, options = {}, _retried = false) {
|
||||
const url = `${API_BASE}${path}`;
|
||||
|
||||
const method = options.method ?? 'GET';
|
||||
@@ -45,6 +45,13 @@ async function apiFetch(path, options = {}) {
|
||||
throw new Error('Sitzung abgelaufen.');
|
||||
}
|
||||
|
||||
// CSRF-Token-Desync (haeufig nach iOS-PWA-Resume): einmal GET /auth/me
|
||||
// ausfuehren um den CSRF-Cookie zu erneuern, dann den Request wiederholen.
|
||||
if (response.status === 403 && stateChanging && !_retried) {
|
||||
await fetch(`${API_BASE}/auth/me`, { credentials: 'same-origin', cache: 'no-store' });
|
||||
return apiFetch(path, options, true);
|
||||
}
|
||||
|
||||
const data = await response.json().catch(() => null);
|
||||
|
||||
if (!response.ok) {
|
||||
|
||||
@@ -12,12 +12,16 @@ if ('serviceWorker' in navigator) {
|
||||
});
|
||||
});
|
||||
|
||||
// Nahtloses Update: Neuer SW hat skipWaiting() + clients.claim() aufgerufen
|
||||
// → Controller wechselt → Seite neu laden für konsistenten Stand
|
||||
// SW-Update: Auf iOS-PWA fuehrt ein sofortiger Reload bei controllerchange
|
||||
// zu Timing-Problemen (leere Seite, verlorene Cookies). Stattdessen nur
|
||||
// nachladen wenn die Seite gerade nicht mitten im Initialisieren ist.
|
||||
let refreshing = false;
|
||||
navigator.serviceWorker.addEventListener('controllerchange', () => {
|
||||
if (refreshing) return;
|
||||
refreshing = true;
|
||||
window.location.reload();
|
||||
// Kurz warten damit der neue SW vollstaendig aktiviert ist und
|
||||
// clients.claim() abgeschlossen hat, bevor die Seite neu laedt.
|
||||
// Auf iOS-Standalone verhindert das den "leere Seite"-Bug.
|
||||
setTimeout(() => window.location.reload(), 200);
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user