A armadilha do token atingiu um site em produção quando cinco abas abertas por um único usuário tentaram atualizar um JWT expirado no mesmo milissegundo, inundando o backend com requisições de atualização duplicadas e invalidando instantaneamente a sessão. Todas as abas deslogaram o usuário, provando que uma solução de renovação de token para uma única aba não é mais suficiente.

Por que a armadilha do token é importante

Single-page apps modernas usam access tokens de curta duração e um refresh token de longa duração. Quando o access token expira, o cliente envia uma requisição de refresh, recebe um novo par de tokens e tenta novamente a chamada original. A maioria dos desenvolvedores protege esse fluxo com uma flag em memória (ex: isRefreshing = true) ou uma fila de requisições, testando-o em uma única aba. No mundo real, os usuários mantêm várias abas abertas: uma página de configurações, um dashboard de analytics e algumas visualizações de dados. Quando o access token expira, cada aba detecta o erro 401 de forma independente, cada uma dispara uma requisição de refresh, e o backend — especialmente quando impõe a rotação de refresh token — trata a segunda requisição como um replay e revoga toda a sessão.

O isolamento do JavaScript cria o problema

Cada aba do navegador executa seu próprio contexto JavaScript. Variáveis, timers e flags em memória são invisíveis para outras abas, mesmo quando compartilham a mesma origem. Uma flag que diz “uma atualização já está em progresso” vive apenas dentro da aba que a definiu. As outras abas não têm como saber que o token está sendo atualizado em outro lugar, então todas lançam sua própria chamada de rede. O problema não é um bug no interceptador; é uma limitação fundamental do isolamento de estado no lado do cliente.

Web Locks API ao resgate

Quando uma aba detém um lock, qualquer outra aba que solicite o mesmo lock deve esperar até que ele seja liberado.

Como funciona para o refresh de token

  1. Detectar um 401 – Qualquer aba que receba uma resposta não autorizada chama navigator.locks.request('auth_token_refresh_lock', async lock => { … }).
  2. Adquirir o lock – Se nenhuma outra aba detiver o lock, a aba atual prossegue; caso contrário, ela pausa até que o lock esteja livre.
  3. Atualizar uma única vez – O detentor do lock envia a requisição de refresh, armazena o novo access token e um timestamp no localStorage e, em seguida, libera o lock automaticamente quando o callback termina.
  4. Pular trabalho duplicado – Quando uma aba em espera finalmente obtém o lock, ela lê o timestamp do localStorage. Se o token foi atualizado dentro de uma janela configurável (ex: nos últimos segundos), a aba pula a chamada de rede e atualiza seu token em memória a partir do localStorage.
  5. Lidar com falhas – Se uma aba travar ou for fechada enquanto detém o lock, o navegador libera o lock, permitindo que outra aba tente a atualização novamente.

Benefícios de relance

  • Zero chamadas de rede redundantes – Apenas a primeira aba fala com o backend.
  • Sem destruição de sessão – A rotação de refresh token vê um único uso, mantendo a sessão viva.
  • Recuperação suave – A liberação de lock gerenciada pelo navegador evita deadlocks se uma aba desaparecer.

Checklist de implementação

  • Envolva a lógica de refresh no seu interceptador Axios (ou fetch) com uma requisição de lock.
  • Armazene o token atualizado e um timestamp em milissegundos no localStorage (ou sessionStorage se preferir dados por sessão).
  • Quando um lock for concedido, compare o timestamp armazenado com Date.now(). Se a diferença estiver abaixo do seu limite, leia o token do armazenamento em vez de chamar o backend.
  • Certifique-se de que o interceptador atualize os headers das requisições com o token recuperado do armazenamento antes de tentar novamente a chamada original da API.
  • Teste o fluxo com múltiplas abas, simulando latência de rede para verificar se apenas uma requisição de refresh chega ao servidor.

O que pode dar errado

O que observar a seguir

Conclusão

Trate cada aba do navegador como um nó em um pequeno sistema distribuído. Ao usar a Web Locks API para serializar as atualizações de JWT, você elimina chamadas duplicadas, protege a rotação de refresh token e mantém os usuários logados em todas as suas abas abertas.