De Web Locks API kan voorkomen dat vijf geopende tabbladen tegelijkertijd een auth-server bestoken met refresh-token-aanroepen, waardoor gebruikers worden behoed voor plotselinge uitlogsessies. Door refreshes over tabbladen heen te coördineren, vervangt één enkele aanroep de vloedgolf die normaal gesproken een sessiebeëindiging triggert wanneer Refresh Token Rotation in gebruik is.

De verborgen overbelasting in een sessie met meerdere tabbladen

Een typische single-page app voegt een Axios-interceptor toe die let op een 401-respons, een Boolean isRefreshing-vlag omzet en alle uitgaande verzoeken in een wachtrij plaatst totdat een nieuw JWT arriveert. Getest in één tabblad werkt de flow vlekkeloos.

Open dezelfde app in vijf tabbladen, laat het access token verlopen, en alle vijf de tabbladen merken de 401 in hetzelfde milliseconde op. Elk tabblad denkt dat het moet refreshen, waardoor vijf identieke refresh-token-verzoeken naar de auth-server racen. Met Refresh Token Rotation — een beveiligingsmaatregel die het vorige refresh token ongeldig maakt zodra er een nieuwe wordt uitgegeven — ziet de server het tweede verzoek als een replay-aanval, markeert de sessie als gecompromitteerd en trekt deze in. De gebruiker wordt in een oogwenk uit elk tabblad uitgelogd.

De kernoorzaak is het isolatiemodel van JavaScript. Een variabele zoals isRefreshing leeft alleen in het tabblad waarin deze is ingesteld; andere tabbladen hebben geen manier om te weten dat een refresh al gaande is. Het resultaat is een klassiek concurrency-probleem, maar de "processen" zijn browser-tabbladen in plaats van threads.

Waarom een cross-tab lock het juiste hulpmiddel is

Wat we nodig hebben is een manier voor tabbladen om met elkaar te communiceren over een gedeelde resource — in dit geval het verse JWT. De Web Locks API, beschikbaar via navigator.locks, biedt precies dat. Het stelt scripts in staat om een genaamd lock aan te vragen dat de browser afdwingt over alle contexten die tot dezelfde origin behoren. Als een lock al in gebruik is, worden andere aanroepers in een wachtrij geplaatst totdat de houder deze vrijgeeft of de browser deze afbreekt (bijvoorbeeld wanneer het tabblad crasht). Geen externe server, geen polling, alleen native browser-coördinatie.

Het implementeren van de lock-gebaseerde refresh-flow

  1. Detecteer de 401 – De Axios-interceptor vangt de ongeautoriseerde respons op zoals voorheen.
  2. Vraag een exclusieve lock aan – Het tabblad roept navigator.locks.request('auth_token_refresh_lock', async lock => { … }) aan. Slechts één tabblad kan op een bepaald moment de callback betreden.
  3. Dubbelcheck voordat je het netwerk raakt – Lees binnen de lock een timestamp (of het token zelf) uit localStorage. Als de timestamp minder dan een paar seconden oud is, heeft een ander tabblad het token al ververst; het huidige tabblad slaat de netwerkoproep over en leest simpelweg het nieuwe JWT uit de opslag.
  4. Refresh indien nodig – Als de opgeslagen timestamp verouderd is, verstuur dan het refresh-verzoek, sla het nieuwe token en de huidige tijd op in localStorage, en laat de lock vervolgens vrij door de callback te beëindigen.
  5. Hervat wachtrij-verzoeken – Alle andere tabbladen die stonden te wachten, verkrijgen de lock één voor één, zien de verse timestamp en ronden het proces af zonder een nieuw verzoek te doen.
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());
  });
}

Dit patroon garandeert dat, ongeacht hoeveel tabbladen er open zijn, slechts één refresh-verzoek de server bereikt.

Voordelen die je kunt meten

  • Netwerkefficiëntie – Eén verzoek vervangt er vijf, wat de bandbreedte en serverbelasting drastisch vermindert.
  • Sessieveiligheid – Met Refresh Token Rotation ziet de server slechts één gebruik van het oude refresh token, waardoor de sessie nooit als gecompromitteerd wordt gemarkeerd.
  • Veerkracht – Als het tabblad dat de lock vasthoudt crasht, laat de browser de lock automatisch vrij, wat een deadlock voorkomt die anders alle tabbladen zou blokkeren.
  • Schaalbaarheid – Gebruikers kunnen tientallen tabbladen openen zonder het risico op een cascade-uitlogsessie, omdat de coördinatie binnen de browser blijft.

De keerzijde: browserondersteuning en fallbacks

De Web Locks API is een relatief nieuwe functie. Moderne Chromium-gebaseerde browsers en recente versies van Firefox implementeren het, maar oudere browsers en Safari missen native ondersteuning. In omgevingen waar de API niet beschikbaar is, moeten ontwikkelaars terugvallen op een minder betrouwbare techniek — zoals het uitzenden van een custom event via localStorage of het gebruik van een shared worker — om cross-tab signalering te benaderen. Deze workarounds missen de automatische deadlock-bescherming die navigator.locks biedt, dus ze moeten met voorzichtigheid worden gebruikt.

Wat je hierna moet bekijken

  • Voortgang van de standaardisatie – Houd de adoptiecurve van de API in de gaten; bredere ondersteuning zal de lock-gebaseerde aanpak de standaard maken voor alle coördinatie tussen tabbladen.
  • Library wrappers – Een aantal open-source utilities abstraheren al het lock-aanvraagpatroon, waardoor het gemakkelijker te integreren is in bestaande Axios-interceptors.
  • Beveiligingsaudits – Hoewel de lock het concurrency-probleem oplost, moet de refresh-endpoint nog steeds correcte tokenrotatie en rate limiting afdwingen, aangezien een enkel kwaadaardig tabblad de server nog steeds met verzoeken kan overspoelen als de lock wordt omzeild.

De kernboodschap is simpel: behandel een reeks open tabbladen als een gedistribueerd systeem en geef ze een native synchronisatie-primitive. Door de Web Locks API te koppelen aan de token-refresh flow, elimineren ontwikkelaars de "browser tab token trap" en houden ze gebruikers ingelogd, ongeacht hoeveel tabbladen ze tegelijkertijd gebruiken.