La trappola del token ha colpito un sito in produzione quando cinque schede aperte da un singolo utente hanno tentato tutte di rinnovare un JWT scaduto nello stesso millisecondo, inondando il backend con richieste di refresh duplicate e invalidando istantaneamente la sessione. Ogni scheda ha effettuato il logout dell'utente, dimostrando che una soluzione valida solo per una singola scheda per il rinnovo del token non è più sufficiente.

Perché la trappola del token è importante

Le moderne single-page app utilizzano access token a breve durata e un refresh token a lunga durata. Quando l'access token scade, il client invia una richiesta di refresh, riceve una nuova coppia di token e riprova la chiamata originale. La maggior parte degli sviluppatori protegge questo flusso con un flag in memoria (ad es. isRefreshing = true) o una coda di richieste, testandolo in una singola scheda. Nel mondo reale, gli utenti tengono aperte diverse schede: una pagina di impostazioni, una dashboard di analisi e alcune visualizzazioni dati. Quando l'access token scade, ogni scheda rileva l'errore 401 indipendentemente, ognuna avvia una richiesta di refresh e il backend — specialmente quando applica la rotazione del refresh token — tratta la seconda richiesta come un replay e revoca l'intera sessione.

L'isolamento di JavaScript crea il problema

Ogni scheda del browser esegue il proprio contesto JavaScript. Variabili, timer e flag in memoria sono invisibili alle altre schede, anche quando condividono la stessa origin. Un flag che indica "un refresh è già in corso" vive solo all'interno della scheda che lo ha impostato. Le altre schede non hanno modo di sapere che il token viene rinnovato altrove, quindi avviano tutte la propria chiamata di rete. Il problema non è un bug nell'interceptor; è un limite fondamentale dell'isolamento dello stato lato client.

Web Locks API al salvataggio

Quando una scheda detiene un lock, qualsiasi altra scheda che richieda lo stesso lock deve attendere che venga rilasciato.

Come funziona per il refresh del token

  1. Rileva un 401 – Qualsiasi scheda che riceve una risposta non autorizzata chiama navigator.locks.request('auth_token_refresh_lock', async lock => { … }).
  2. Acquisisci il lock – Se nessun'altra scheda detiene il lock, la scheda corrente procede; altrimenti si mette in pausa finché il lock non è libero.
  3. Esegui il refresh una sola volta – Il titolare del lock invia la richiesta di refresh, memorizza il nuovo access token e un timestamp in localStorage, e poi rilascia automaticamente il lock quando la callback termina.
  4. Salta il lavoro duplicato – Quando una scheda in attesa ottiene finalmente il lock, legge il timestamp da localStorage. Se il token è stato rinnovato entro una finestra configurabile (ad es. gli ultimi secondi), la scheda salta la chiamata di rete e aggiorna il proprio token in memoria tramite localStorage.
  5. Gestisci i crash – Se una scheda crasha o viene chiusa mentre detiene il lock, il browser rilascia il lock, consentendo a un'altra scheda di riprovare il refresh.

Vantaggi in breve

  • Zero chiamate di rete ridondanti – Solo la prima scheda comunica con il backend.
  • Nessuna distruzione della sessione – La rotazione del refresh token vede un unico utilizzo, mantenendo viva la sessione.
  • Ripristino fluido – Il rilascio del lock gestito dal browser evita deadlock se una scheda scompare.

Checklist di implementazione

  • Avvolgi la logica di refresh nel tuo interceptor Axios (o fetch) con una richiesta di lock.
  • Memorizza il token rinnovato e un timestamp in millisecondi in localStorage (o sessionStorage se preferisci dati per sessione).
  • Quando un lock viene concesso, confronta il timestamp memorizzato con Date.now(). Se la differenza è inferiore alla tua soglia, leggi il token dallo storage invece di chiamare il backend.
  • Assicurati che l'interceptor aggiorni gli header delle richieste con il token recuperato dallo storage prima di riprovare la chiamata API originale.
  • Testa il flusso con più schede, simulando la latenza di rete per verificare che solo una richiesta di refresh raggiunga effettivamente il server.

Cosa potrebbe andare storto

Cosa monitorare in seguito

In sintesi

Tratta ogni scheda del browser come un nodo in un piccolo sistema distribuito. Utilizzando la Web Locks API per serializzare i refresh dei JWT, elimini le chiamate duplicate, proteggi la rotazione del refresh token e mantieni gli utenti connessi in tutte le loro schede aperte.