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
- Detectar um 401 – Qualquer aba que receba uma resposta não autorizada chama
navigator.locks.request('auth_token_refresh_lock', async lock => { … }). - 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.
- Atualizar uma única vez – O detentor do lock envia a requisição de refresh, armazena o novo access token e um timestamp no
localStoragee, em seguida, libera o lock automaticamente quando o callback termina. - 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 dolocalStorage. - 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(ousessionStoragese 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.
