Autenticacao JWT: Guia Completo para Desenvolvedores Web (2026)

Publicado em marco de 2026 • 10 min de leitura

O que e Autenticacao JWT?

JSON Web Token (JWT, pronunciado "jot") e um padrao compacto e autocontido para transmitir informacoes de autenticacao e autorizacao entre partes como um token assinado digitalmente. Uma vez que o usuario faz login, o servidor emite um JWT contendo claims sobre aquele usuario. O cliente armazena o token e o envia com cada requisicao subsequente. O servidor verifica a assinatura do token para confirmar a autenticidade — sem necessidade de consulta ao banco de dados.

JWTs sao definidos pela RFC 7519 e sao o mecanismo de autenticacao dominante para APIs REST, aplicacoes single-page e arquiteturas de microsservicos. Sao stateless por design: o servidor nao precisa armazenar dados de sessao, tornando a escalabilidade horizontal simples.

Como JWT Funciona

O fluxo completo de autenticacao JWT funciona assim:

  1. Login: O usuario envia suas credenciais (usuario + senha) para o endpoint de login do servidor.
  2. Emissao do token: O servidor valida as credenciais no banco de dados. Em caso de sucesso, assina um JWT contendo o ID do usuario, roles e um timestamp de expiracao, e retorna o token ao cliente.
  3. Armazenamento do token: O cliente armazena o token (em memoria, localStorage ou um cookie httpOnly — mais sobre isso adiante).
  4. Requisicoes autenticadas: Para cada requisicao API subsequente, o cliente inclui o JWT no header Authorization: Authorization: Bearer <token>.
  5. Verificacao: O servidor extrai o token do header, verifica a assinatura usando sua chave secreta, verifica a expiracao e le os claims — tudo sem consulta ao banco de dados.
  6. Resposta: Se o token for valido, o servidor processa a requisicao. Se nao (expirado, adulterado, ausente), retorna 401 Unauthorized.

Estrutura do JWT Explicada

Um JWT consiste em tres partes codificadas em Base64URL separadas por pontos:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiIxMjM0NTYiLCJlbWFpbCI6InVzZXJAZXhhbXBsZS5jb20iLCJyb2xlIjoiYWRtaW4iLCJpYXQiOjE3MDkzMDAwMDAsImV4cCI6MTcwOTMwMzYwMH0.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c

As tres partes sao:

1. Header

{
  "alg": "HS256",   // Algoritmo de assinatura (HS256, RS256, ES256)
  "typ": "JWT"      // Tipo do token
}

2. Payload (Claims)

{
  "userId": "123456",
  "email": "user@example.com",
  "role": "admin",
  "iat": 1709300000,   // Emitido em (Unix timestamp)
  "exp": 1709303600    // Expiracao (1 hora apos emissao)
}

Nomes de claims padroes: iss (emissor), sub (assunto), aud (audiencia), exp (expiracao), iat (emitido em), jti (ID JWT para revogacao). O payload e codificado em Base64URL, nao criptografado — qualquer pessoa pode decodifica-lo. Nunca armazene senhas, numeros de cartao de credito ou outros dados sensiveis no payload.

3. Assinatura

HMACSHA256(
  base64UrlEncode(header) + "." + base64UrlEncode(payload),
  secretKey
)

A assinatura prova que o token foi emitido pelo seu servidor e nao foi adulterado. Se qualquer caractere no header ou payload mudar, a verificacao da assinatura falha.

Implementando JWT no Node.js

Instale o pacote jsonwebtoken:

npm install jsonwebtoken bcryptjs express

Crie um exemplo completo de login e rota protegida:

// auth.js
const jwt = require('jsonwebtoken');
const bcrypt = require('bcryptjs');
const express = require('express');
const router = express.Router();

const JWT_SECRET = process.env.JWT_SECRET; // Deve ser uma string longa e aleatoria
const JWT_EXPIRES_IN = '1h';               // Mantenha access tokens com vida curta

// Rota de login — emite um JWT em autenticacao bem-sucedida
router.post('/login', async (req, res) => {
  const { email, password } = req.body;

  // 1. Buscar o usuario no banco de dados
  const user = await User.findOne({ email });
  if (!user) {
    return res.status(401).json({ error: 'Invalid credentials' });
  }

  // 2. Comparar senha com hash armazenado
  const validPassword = await bcrypt.compare(password, user.passwordHash);
  if (!validPassword) {
    return res.status(401).json({ error: 'Invalid credentials' });
  }

  // 3. Assinar o JWT — inclua apenas claims nao sensiveis
  const token = jwt.sign(
    { userId: user._id, email: user.email, role: user.role },
    JWT_SECRET,
    { expiresIn: JWT_EXPIRES_IN, algorithm: 'HS256' }
  );

  res.json({ token, expiresIn: 3600 });
});

// Middleware para verificar JWT em rotas protegidas
function authenticateToken(req, res, next) {
  const authHeader = req.headers['authorization'];
  const token = authHeader && authHeader.split(' ')[1]; // "Bearer TOKEN"

  if (!token) {
    return res.status(401).json({ error: 'Access token required' });
  }

  try {
    // Especifique explicitamente o algoritmo para prevenir ataques de confusao de algoritmo
    const decoded = jwt.verify(token, JWT_SECRET, { algorithms: ['HS256'] });
    req.user = decoded;
    next();
  } catch (err) {
    if (err.name === 'TokenExpiredError') {
      return res.status(401).json({ error: 'Token expired' });
    }
    return res.status(403).json({ error: 'Invalid token' });
  }
}

// Exemplo de rota protegida
router.get('/profile', authenticateToken, (req, res) => {
  res.json({ userId: req.user.userId, email: req.user.email });
});

module.exports = { router, authenticateToken };

Armazenando JWT no React

Onde voce armazena o JWT no navegador afeta significativamente a postura de seguranca da sua aplicacao. As duas opcoes principais sao localStorage e cookies httpOnly:

Propriedade localStorage Cookie httpOnly
Vulnerabilidade XSS Alta — qualquer script pode le-lo Baixa — inacessivel ao JavaScript
Vulnerabilidade CSRF Nenhuma Sim — mitigar com SameSite + token CSRF
Enviado automaticamente Nao — deve adicionar ao header manualmente Sim — navegador envia com cada requisicao
Acessivel via JS Sim Nao (flag httpOnly)
Recomendado para Apps de baixo risco, dados publicos Apps em producao com dados de usuario

Para aplicacoes em producao, prefira cookies httpOnly. Seu servidor define o cookie no login e o navegador o envia automaticamente, sem o JavaScript nunca tocar no valor do token.

Se voce usar localStorage (por ex., por simplicidade durante o desenvolvimento), armazene o token em memoria quando possivel e implemente uma Content Security Policy robusta para mitigar riscos de XSS:

// React — enviando JWT do localStorage
async function fetchProfile() {
  const token = localStorage.getItem('authToken');

  const response = await fetch('/api/profile', {
    headers: {
      'Authorization': `Bearer ${token}`,
      'Content-Type': 'application/json',
    },
  });

  if (response.status === 401) {
    // Token expirado — redirecionar para login ou renovar
    localStorage.removeItem('authToken');
    window.location.href = '/login';
  }

  return response.json();
}

Estrategia de Refresh Token

Access tokens com vida curta (15 minutos a 1 hora) minimizam a janela de roubo de token, mas forcar usuarios a fazer login a cada hora e uma experiencia terrivel. A solucao e um par de refresh token:

  • Access token: Vida curta (15 min – 1 hora), enviado com cada requisicao API no header Authorization. Armazenado em memoria ou localStorage.
  • Refresh token: Vida longa (7 – 30 dias), usado apenas para obter novos access tokens. Sempre armazenado em um cookie httpOnly, Secure, SameSite=Strict. Deve ser rotacionado a cada uso.
// Servidor: emitir ambos os tokens no login
router.post('/login', async (req, res) => {
  // ... validar credenciais ...

  const accessToken = jwt.sign(
    { userId: user._id, role: user.role },
    process.env.JWT_ACCESS_SECRET,
    { expiresIn: '15m' }
  );

  const refreshToken = jwt.sign(
    { userId: user._id },
    process.env.JWT_REFRESH_SECRET,
    { expiresIn: '30d' }
  );

  // Armazenar hash do refresh token no banco para suporte a revogacao
  await RefreshToken.create({ userId: user._id, token: hashToken(refreshToken) });

  // Enviar refresh token como cookie httpOnly
  res.cookie('refreshToken', refreshToken, {
    httpOnly: true,
    secure: process.env.NODE_ENV === 'production',
    sameSite: 'strict',
    maxAge: 30 * 24 * 60 * 60 * 1000, // 30 dias em ms
  });

  res.json({ accessToken });
});

// Servidor: endpoint de refresh
router.post('/refresh', async (req, res) => {
  const refreshToken = req.cookies.refreshToken;
  if (!refreshToken) return res.status(401).json({ error: 'No refresh token' });

  try {
    const decoded = jwt.verify(refreshToken, process.env.JWT_REFRESH_SECRET);
    // Opcional: verificar se hash do token existe no BD (habilita revogacao)

    const newAccessToken = jwt.sign(
      { userId: decoded.userId },
      process.env.JWT_ACCESS_SECRET,
      { expiresIn: '15m' }
    );

    res.json({ accessToken: newAccessToken });
  } catch {
    res.status(403).json({ error: 'Invalid refresh token' });
  }
});

Melhores Praticas de Seguranca JWT

  • Mantenha access tokens com vida curta: 15 minutos a 1 hora. Tokens roubados ficam inuteis apos a expiracao.
  • Use secrets fortes: Seu JWT_SECRET deve ter pelo menos 256 bits de dados aleatorios. Gere com openssl rand -hex 32. Nunca o coloque hardcoded.
  • Sempre especifique o algoritmo: Passe { algorithms: ['HS256'] } para jwt.verify(). Isso previne o ataque de confusao de algoritmo (none) onde um atacante define "alg": "none" no header.
  • Apenas HTTPS em producao: JWTs em transito sao tao seguros quanto sua camada de transporte. Sempre use TLS em producao.
  • Armazene refresh tokens em cookies httpOnly: Nao em localStorage ou sessionStorage.
  • Implemente revogacao de token: Para logout e eventos de seguranca, mantenha uma blocklist de curta duracao (Redis funciona bem) ou use refresh tokens rotativos com rastreamento no banco de dados.
  • Valide todos os claims: Verifique exp (expiracao), iss (emissor) e aud (audiencia) em cada requisicao. A biblioteca jsonwebtoken faz isso automaticamente quando voce passa as opcoes corretas.

Erros Comuns com JWT

Armazenar JWTs no localStorage em apps de alto risco

Qualquer vulnerabilidade XSS — mesmo um script de terceiros que voce carregou — pode ler localStorage e exfiltrar todos os tokens. Para aplicacoes que lidam com pagamentos, dados pessoais ou acoes administrativas, use cookies httpOnly exclusivamente.

Sem expiracao nos tokens

Tokens sem claim exp sao validos para sempre. Um token vazado se torna uma backdoor permanente. Sempre defina expiresIn ao assinar.

O ataque algorithm none

Bibliotecas JWT antigas aceitavam "alg": "none" no header, efetivamente aceitando tokens sem assinatura como validos. Sempre passe a opcao algorithms para jwt.verify() para restringir quais algoritmos sao aceitaveis:

// Sempre faca isso — nunca permita o algoritmo "none"
jwt.verify(token, secret, { algorithms: ['HS256'] });

Dados sensiveis no payload

O payload JWT e codificado em Base64URL, nao criptografado. Qualquer parte com o token pode decodificar e ler o payload sem conhecer o secret. Nunca inclua senhas, CPFs, numeros de cartao de credito ou detalhes internos do sistema nos claims JWT.

Usar o mesmo secret para access e refresh tokens

Se voce usa o mesmo secret para ambos os tipos de token, um access token comprometido poderia potencialmente ser usado como refresh token. Use secrets separados para cada tipo de token armazenados em variaveis de ambiente separadas.

Decodifique Seus Tokens JWT

Precisa inspecionar um JWT para verificar seus claims, expiracao ou estrutura? Cole-o no Decodificador JWT do devbit.dev para decodificar instantaneamente o header e payload, verificar o tempo de expiracao e validar o formato do token — tudo no navegador sem dados enviados a nenhum servidor.

Decodifique e Inspecione Qualquer JWT Instantaneamente

Cole um token JWT para decodificar seu header, payload e claims. Verifica expiracao, algoritmo e estrutura. 100% client-side — seu token nunca sai do navegador.

Abrir Decodificador JWT →

Ferramentas Relacionadas