O botão “Sair” comunica que o acesso terminou. Com JWT, cumprir essa expectativa exige decidir o que a API fará com um token emitido antes do logout. Apagá-lo do navegador encerra seu uso naquela interface; uma cópia da credencial ainda pode ser apresentada ao servidor.
Uma API que valida o token sem consultar o estado da sessão pode continuar aceitando essa cópia até o vencimento. A garantia do logout depende, portanto, de quanto tempo o acesso pode sobreviver e de quais eventos devem encerrá-lo: sair de um dispositivo, remover uma sessão ou recuperar uma conta comprometida. Essas decisões determinam o estado que o servidor precisa manter e consultar.
A assinatura responde só parte da pergunta
Um JWT assinado permite verificar a integridade das informações emitidas. Claims como iss, aud e exp descrevem emissor, destinatário e expiração. O jti, quando usado, identifica aquele token. Esses campos estão definidos na RFC 7519, mas o formato não cria automaticamente um cadastro de sessões ou um mecanismo de logout.
A validação também precisa seguir o contrato da aplicação: algoritmos permitidos, chave correta, emissor e audiência esperados, validade temporal e tipo de token aceito naquela rota. Decodificar o payload para ler sub não substitui essa verificação. A RFC 8725 organiza esses cuidados e o risco de aceitar um token em um contexto diferente daquele para o qual foi emitido.
Mesmo um JWT corretamente validado pode representar uma sessão já encerrada ou carregar uma permissão que mudou. Em um painel de atendimento com documentos de clientes, a assinatura confirma que o sistema emitiu o acesso de um atendente. Ela não informa, sozinha, que o dispositivo foi removido da conta há alguns segundos.
Escreva a garantia do logout antes de escolher a implementação
Há pelo menos três compromissos possíveis. Uma aplicação pode aceitar o token até sua expiração, consultar o estado da sessão a cada chamada ou consultar esse estado com um cache de curta duração. Cada escolha produz uma janela diferente entre o pedido de logout e a recusa de novas requisições.
Um access token com cinco minutos de validade pode continuar útil por aproximadamente quatro minutos se a pessoa sair um minuto depois da emissão e o servidor não consultar nenhum estado adicional. Apagar o refresh token evita novas renovações, mas não altera o access token que já está nas mãos de alguém.
Em um catálogo de baixo risco, essa janela pode ser uma escolha documentada. No painel com documentos de clientes, o produto pode exigir que novas chamadas sejam recusadas assim que a revogação for confirmada. A exigência muda a arquitetura: algum estado atual precisa participar da decisão de acesso, diretamente ou por um mecanismo de invalidação com garantia conhecida.
“Imediato” também precisa de um limite claro. Uma requisição que já foi autorizada pode estar em execução quando o logout ocorre. Uma exportação já iniciada pode continuar em um worker. Dados já entregues não voltam para o servidor. Para operações sensíveis, o desenho deve definir pontos adicionais de autorização antes de efetivar a ação ou disponibilizar o resultado.
Um identificador para a sessão, outro para o token
Uma forma de controlar o acesso por dispositivo é criar um registro de sessão para cada login. Ele guarda o usuário, o vencimento máximo e uma eventual data de revogação. Os access tokens emitidos durante aquele login carregam uma referência a esse registro. Essa referência é uma claim definida pela aplicação; não basta acrescentar um campo ao payload e esperar que a biblioteca implemente a regra.
Separar os identificadores resolve duas perguntas diferentes. O identificador do token permite bloquear uma credencial específica. O identificador da sessão permite encerrar todos os access tokens e refresh tokens associados àquele login. Uma nova sessão no segundo dispositivo permanece independente, se essa for a política do produto.
Uma autorização poderia seguir este fluxo conceitual. Os helpers representam contratos da aplicação, não APIs de uma biblioteca:
async function authorizeAccess(rawToken: string) {
const claims = await verifyAccessToken(rawToken);
// O helper verifica assinatura, claims obrigatórias e o contexto esperado.
const session = await sessionStore.find(claims.sessionId);
if (
!session ||
session.userId !== claims.sub ||
session.revokedAt !== null ||
session.expiresAt <= Date.now()
) {
throw new InvalidSessionError();
}
return { userId: session.userId, sessionId: session.id };
}
Nesse exemplo, expiresAt está em milissegundos no registro da aplicação. O helper de JWT trata exp conforme o formato do token. A autorização de cada recurso continua depois dessa etapa: ter uma sessão válida não concede acesso a qualquer chamado ou organização.
O logout marca o registro da sessão autenticada como revogado e impede renovações associadas a ele. A operação deve terminar de forma durável antes de o servidor confirmar o encerramento. Uma falha no armazenamento não pode virar uma confirmação falsa de que todas as cópias foram invalidadas.
O cache entra na promessa de segurança
Consultar a sessão em toda chamada acrescenta dependência e custo. Manter uma decisão positiva em cache reduz esse custo, mas permite que ela sobreviva à mudança no registro original. Se o cache vale por trinta segundos e a invalidação pode falhar, esse tempo faz parte da janela de revogação.
Há duas formas honestas de tratar a situação: aceitar e documentar a janela ou usar um caminho que consulte estado atualizado nas operações que exigem revogação mais rápida. Publicar uma mensagem de invalidação ajuda, mas a garantia depende de como cada instância recebe e aplica essa mensagem. Também é preciso considerar réplicas de leitura atrasadas.
Se o serviço de sessões ficar indisponível, o produto deve decidir onde falha de forma restritiva e onde admite uma alternativa limitada. Retornar indisponibilidade temporária pode ser mais correto do que classificar uma credencial como inválida. Aceitar todo token assinado durante a falha, por outro lado, suspende a garantia de revogação justamente quando ela pode ser necessária.
Em OAuth, a introspecção de tokens da RFC 7662 oferece um protocolo para consultar o estado atual de um token. Um cadastro de sessões próprio não é automaticamente um endpoint de introspecção. São desenhos distintos que compartilham a necessidade de consultar uma decisão atual; cachear a resposta também exige ponderar informação desatualizada.
Refresh token não substitui a revogação do access token
O refresh token permite obter novos access tokens sem repetir o login. Ele costuma ter uma vida útil maior e exige proteção própria. Em OAuth, a RFC 9700 prevê, para clientes públicos que recebem refresh tokens, mecanismos como vínculo ao remetente ou rotação para detectar repetição.
Essa credencial precisa permanecer protegida em trânsito e no armazenamento. No desenho da aplicação, evite registrar tokens em logs e imponha prazos de inatividade e duração máxima da sessão também no servidor. Renovar o access token não deve prolongar indefinidamente um login que o produto pretendia encerrar.
Na rotação, o uso de um refresh token o substitui por outro. O servidor mantém a relação entre os tokens emitidos. Quando um token antigo reaparece, há um sinal de possível comprometimento; a reação pode encerrar a cadeia de renovação afetada. Isso ainda não cancela magicamente access tokens que as APIs validam apenas pela assinatura.
Com a rotação vinculada à sessão, consumir o refresh token, criar seu sucessor e conferir que a sessão continua ativa precisam participar de uma operação coordenada. Caso contrário, uma renovação concorrente ao logout pode criar uma credencial nova depois da tentativa de encerramento. Se a API consulta o registro revogado em cada autorização, ela também consegue recusar essa credencial.
Duas abas renovando ao mesmo tempo merecem um tratamento explícito. O cliente pode coordenar a renovação e compartilhar o resultado. No servidor, uma política de tolerância à repetição legítima precisa ser curta, delimitada e compatível com a detecção de roubo. Aceitar indefinidamente o refresh token anterior elimina a propriedade que a rotação pretendia oferecer.
Sair deste dispositivo e sair de todos são operações diferentes
Para encerrar um dispositivo, revogue a sessão correspondente e sua capacidade de renovação. Para encerrar todas as sessões, o sistema pode marcar todos os registros ativos ou avançar uma versão de autenticação da conta. Nesse segundo desenho, os tokens carregam a versão de emissão e o servidor compara com a atual. A comparação continua exigindo acesso a estado atualizado.
Uma troca de senha ou recuperação de conta pode revogar todas as sessões, conforme a política do produto. Já remover alguém de uma organização exige atualizar a autorização sobre aquela organização. A pessoa pode continuar com uma sessão legítima e, ainda assim, perder acesso a um conjunto de dados. Colocar permissões em tokens longos sem uma forma de revisão adia essa mudança.
A OWASP recomenda expiração e invalidação no servidor, além de reautenticação após eventos de risco. Fechar a aba ou executar um timer no navegador pode melhorar a experiência, mas não substitui essas decisões. O servidor precisa recusar uma credencial reapresentada por outro cliente.
Se a aplicação usa cookies, o endpoint de logout e o de renovação também entram no modelo de proteção CSRF para SPAs e APIs. A forma de transportar a credencial e a forma de revogá-la resolvem partes diferentes do problema.
A identidade da revogação precisa sobreviver à representação
Um advisory do PyJWT publicado em setembro de 2026 documentou um desvio de revogação em uma combinação específica de biblioteca e aplicação. A falha não permitia inventar uma assinatura nem alterar as claims: a validação aceitava uma representação não canônica que mudava a string do token sem mudar seu significado autenticado. Uma lista indexada apenas por aquela string, ou por seu hash, podia reconhecer uma forma e deixar passar outra.
Além de manter a biblioteca corrigida, o caso favorece uma identidade semântica para o controle: por exemplo, o par emissor e jti, extraído somente depois da validação completa, ou o identificador da sessão vinculada ao usuário. O escopo evita tratar identificadores de emissores diferentes como se pertencessem ao mesmo cadastro. O exemplo não significa que toda lista baseada em hash tenha esse bypass; o comportamento documentado dependia da validação permissiva envolvida.
Antes de prometer um logout, vale acompanhar três situações no desenho: uma cópia do access token usada depois da revogação, uma renovação simultânea ao encerramento e uma instância com cache atrasado. A resposta esperada em cada uma delas define a garantia real. O botão “Sair” deve representar essa garantia, inclusive quando algum serviço falha no caminho.


