Die Web Locks API kann verhindern, dass fünf geöffnete Tabs gleichzeitig einen Auth-Server mit Refresh-Token-Aufrufen bombardieren, und so Nutzer vor plötzlichen Logouts bewahren. Durch die Koordination der Refreshes über die Tabs hinweg ersetzt eine einzige Anfrage die Flut, die normalerweise einen Session-Kill auslöst, wenn Refresh Token Rotation verwendet wird.

Die versteckte Überlastung in einer Multi-Tab-Session

Eine typische Single-Page-App fügt einen Axios-Interceptor hinzu, der auf eine 401-Antwort wartet, ein Boolean-Flag isRefreshing setzt und alle ausgehenden Anfragen in eine Warteschlange stellt, bis ein neues JWT eintrifft. In einem einzelnen Tab getestet, funktioniert dieser Ablauf tadellos.

Öffnet man dieselbe App in fünf Tabs und lässt das Access Token ablaufen, bemerken alle fünf Tabs das 401 im selben Millisekundenbereich. Jeder Tab denkt, er müsse den Refresh durchführen, sodass fünf identische Refresh-Token-Anfragen zum Auth-Server eilen. Bei der Refresh Token Rotation – einer Sicherheitsmaßnahme, die das vorherige Refresh Token ungültig macht, sobald ein neues ausgestellt wird – wertet der Server die zweite Anfrage als Replay-Attacke, markiert die Session als kompromittiert und entzieht sie. Der Nutzer wird augenblicklich aus jedem Tab ausgeloggt.

Die Ursache liegt im Isolationsmodell von JavaScript. Eine Variable wie isRefreshing existiert nur in dem Tab, der sie gesetzt hat; andere Tabs haben keine Möglichkeit zu wissen, dass bereits ein Refresh läuft. Das Ergebnis ist ein klassisches Nebenläufigkeitsproblem (Concurrency Problem), wobei die „Prozesse“ Browser-Tabs statt Threads sind.

Warum ein Cross-Tab-Lock das richtige Werkzeug ist

Was wir benötigen, ist eine Möglichkeit für Tabs, über eine gemeinsame Ressource zu kommunizieren – in diesem Fall das frische JWT. Die Web Locks API, die über navigator.locks bereitgestellt wird, bietet genau das. Sie ermöglicht es Skripten, einen benannten Lock anzufordern, den der Browser über alle Kontexte derselben Origin hinweg erzwingt. Wenn ein Lock bereits gehalten wird, werden andere Aufrufer in eine Warteschlange gestellt, bis der Inhaber ihn freigibt oder der Browser ihn abbricht (zum Beispiel, wenn der Tab abstürzt). Kein externer Server, kein Polling, nur native Browser-Koordination.

Implementierung des Lock-basierten Refresh-Flows

  1. 401 erkennen – Der Axios-Interceptor fängt die nicht autorisierte Antwort wie zuvor ab.
  2. Einen exklusiven Lock anfordern – Der Tab ruft navigator.locks.request('auth_token_refresh_lock', async lock => { … }) auf. Nur ein Tab kann zurzeit den Callback ausführen.
  3. Vor dem Netzwerkaufruf doppelt prüfen – Lesen Sie innerhalb des Locks einen Zeitstempel (oder das Token selbst) aus dem localStorage. Wenn der Zeitstempel weniger als ein paar Sekunden alt ist, hat ein anderer Tab das Token bereits aktualisiert; der aktuelle Tab überspringt den Netzwerkaufruf und liest das neue JWT einfach aus dem Speicher.
  4. Bei Bedarf aktualisieren – Wenn der gespeicherte Zeitstempel veraltet ist, senden Sie die Refresh-Anfrage, speichern Sie das neue Token und die aktuelle Zeit im localStorage und geben Sie den Lock frei, indem Sie den Callback beenden.
  5. Warteschlangen-Anfragen fortsetzen – Alle anderen Tabs, die gewartet haben, erwerben den Lock nacheinander, sehen den frischen Zeitstempel und schließen den Vorgang ab, ohne eine weitere Anfrage zu stellen.
async function refreshIfNeeded() {
  await navigator.locks.request('auth_token_refresh_lock', async lock => {
    const lastRefresh = Number(localStorage.getItem('token_refreshed_at') || 0);
    const now = Date.now();
    if (now - lastRefresh < 5_000) return; // another tab already refreshed

    const newToken = await callRefreshEndpoint(); // actual network call
    localStorage.setItem('jwt', newToken);
    localStorage.setItem('token_refreshed_at', now.toString());
  });
}

Dieses Muster garantiert, dass – unabhängig davon, wie viele Tabs geöffnet sind – nur eine einzige Refresh-Anfrage den Server erreicht.

Messbare Vorteile

  • Netzwerkeffizienz – Eine Anfrage ersetzt fünf, was die Bandbreite und die Serverlast drastisch reduziert.
  • Sicherheit der Session – Bei der Refresh Token Rotation sieht der Server nur eine einzige Verwendung des alten Refresh Tokens und markiert die Session daher nie als kompromittiert.
  • Resilienz – Wenn der Tab, der den Lock hält, abstürzt, gibt der Browser den Lock automatisch frei. Dies verhindert einen Deadlock, der andernfalls alle Tabs blockieren würde.
  • Skalierbarkeit – Nutzer können Dutzende von Tabs öffnen, ohne ein Kaskaden-Logout zu riskieren, da die Koordination innerhalb des Browsers verbleibt.

Die Kehrseite: Browser-Unterstützung und Fallbacks

Die Web Locks API ist ein relativ neues Feature. Moderne Chromium-basierte Browser und aktuelle Firefox-Versionen unterstützen sie, aber ältere Browser und Safari verfügen über keine native Unterstützung. In Umgebungen, in denen die API nicht verfügbar ist, müssen Entwickler auf weniger zuverlässige Techniken zurückgreifen – wie das Senden eines benutzerdefinierten Events via localStorage oder die Verwendung eines Shared Workers –, um eine Cross-Tab-Signalisierung zu simulieren. Diesen Workarounds fehlt der automatische Deadlock-Schutz, den navigator.locks bietet, weshalb sie mit Vorsicht zu genießen sind.

Was als Nächstes zu beachten ist

  • Fortschritt bei der Standardisierung – Behalten Sie die Adoptionskurve der API im Auge; eine breitere Unterstützung wird den lock-basierten Ansatz zum Standard für jede tab-übergreifende Koordination machen.
  • Library-Wrapper – Einige Open-Source-Utilities abstrahieren bereits das Lock-Request-Muster, was die Integration in bestehende Axios-Interceptor erleichtert.
  • Sicherheitsaudits – Obwohl der Lock das Problem der Nebenläufigkeit löst, muss der Refresh-Endpoint weiterhin eine ordnungsgemäße Token-Rotation und Rate Limiting erzwingen, da ein einzelner bösartiger Tab den Server immer noch mit Anfragen überfluten könnte, falls der Lock umgangen wird.

Das Fazit ist einfach: Betrachten Sie eine Menge offener Tabs als ein verteiltes System und geben Sie ihnen ein natives Synchronisationsprimitiv. Indem man die Web Locks API in den Token-Refresh-Flow einbindet, eliminieren Entwickler die „Browser-Tab-Token-Falle“ und halten die Nutzer eingeloggt, egal wie viele Tabs sie gleichzeitig offen haben.