Autenticação JWT: guia passo a passo para APIs
Implementar autenticação JWT exige entender a estrutura do token e o fluxo de validação. Este guia mostra o caminho completo, do login à proteção de rotas, com exemplos práticos e erros comuns.
Implementar autenticação JWT exige entender a estrutura do token e o fluxo de validação. Este guia mostra o caminho completo, do login à proteção de rotas, com exemplos práticos e erros comuns.
Autenticação JWT (JSON Web Token) é um método stateless para verificar identidade em APIs. O servidor emite um token assinado após o login, e o cliente o envia no header Authorization. O servidor valida a assinatura e a expiração a cada requisição, sem armazenar sessão. Este guia mostra a implementação completa, do login à proteção de rotas, cobrindo a estrutura do token, o fluxo de validação e os erros mais comuns.
Pré-requisitos
Antes de começar, você precisa de um ambiente com Node.js (versão 18 ou superior) e um projeto Express configurado. Se preferir outra linguagem, os conceitos são os mesmos; apenas a biblioteca muda. Para este guia, usaremos a biblioteca jsonwebtoken, a mais adotada no ecossistema JavaScript.
Você também deve ter um modelo de usuário com senha armazenada de forma segura, usando bcrypt ou argon2. A autenticação JWT não substitui a necessidade de hash de senha; ela apenas substitui a sessão do servidor.
Passo 1: Instalar a biblioteca JWT
No terminal, dentro do diretório do projeto, execute:
npm install jsonwebtoken
Isso adiciona a dependência ao package.json. A biblioteca fornece três métodos principais: sign (para gerar token), verify (para validar) e decode (para ler sem validar).
Erro comum: instalar a biblioteca no diretório errado. Verifique se o node_modules está no mesmo nível do arquivo onde você importará a biblioteca.
Passo 2: Configurar a chave secreta
A assinatura do token depende de uma chave secreta. Ela deve ser longa, aleatória e armazenada em variável de ambiente, nunca no código-fonte. Crie um arquivo .env na raiz do projeto:
JWT_SECRET=uma_chave_aleatoria_muito_longa_com_pelo_menos_32_caracteres
No código, acesse via process.env.JWT_SECRET. Se a chave for curta ou previsível, qualquer pessoa pode forjar tokens válidos.
Dica: gere a chave com openssl rand -base64 32 no terminal. Isso produz uma string aleatória de 32 bytes.
Passo 3: Criar o endpoint de login
O endpoint de login recebe e-mail e senha, verifica as credenciais e, se válidas, gera um token. Exemplo com Express:
const jwt = require('jsonwebtoken');
app.post('/login', async (req, res) => { const { email, password } = req.body; const user = await db.findUserByEmail(email);
if (!user || !(await bcrypt.compare(password, user.password))) { return res.status(401).json({ error: 'Credenciais inválidas' }); }
const token = jwt.sign( { userId: user.id, role: user.role }, process.env.JWT_SECRET, { expiresIn: '1h' } );
res.json({ token }); });
O payload contém apenas dados não sensíveis: ID do usuário e papel (role). Nunca inclua senha, CPF ou dados bancários no payload, pois ele é apenas codificado, não criptografado.
Erro comum: retornar 200 em vez de 401 quando as credenciais são inválidas. O status correto é 401, que indica não autenticado.
Passo 4: Enviar o token no header Authorization
O cliente deve enviar o token em toda requisição a rotas protegidas. O formato padrão é:
Authorization: Bearer <token>
No lado do cliente, isso significa adicionar o header em cada chamada. Se você usa axios, pode configurar um interceptador:
axios.interceptors.request.use(config => { const token = localStorage.getItem('token'); if (token) { config.headers.Authorization = Bearer ${token}; } return config; });
Erro comum: enviar o token sem o prefixo Bearer. Muitos servidores rejeitam a requisição se o formato não for exatamente esse.
Passo 5: Criar middleware de autenticação
Um middleware valida o token antes de permitir acesso a rotas protegidas. No Express:
function authMiddleware(req, res, next) { const header = req.headers.authorization;
if (!header || !header.startsWith('Bearer ')) { return res.status(401).json({ error: 'Token não fornecido' }); }
const token = header.split(' ')[1];
try { const decoded = jwt.verify(token, process.env.JWT_SECRET); req.user = decoded; next(); } catch (err) { return res.status(401).json({ error: 'Token inválido ou expirado' }); } }
Esse middleware pode ser aplicado a rotas individuais ou a um grupo de rotas. Ele também pode verificar papéis, se necessário.
Dica: use jwt.verify e não jwt.decode. O decode apenas lê o payload sem validar a assinatura, o que é inseguro para controle de acesso.
Passo 6: Proteger rotas com o middleware
Aplique o middleware em rotas que exigem autenticação:
app.get('/perfil', authMiddleware, (req, res) => { res.json({ userId: req.user.userId }); });
Agora, qualquer requisição sem token válido recebe 401. O servidor não precisa consultar o banco de dados para saber quem é o usuário; as informações estão no payload.
Erro comum: aplicar o middleware em todas as rotas, incluindo login e registro. Essas rotas devem ser públicas, senão o usuário nunca consegue obter o token.
Passo 7: Lidar com expiração e refresh token
O token expira após o tempo definido em expiresIn. Para evitar que o usuário faça login a cada hora, implemente um refresh token. O fluxo típico é:
- O login retorna dois tokens: access token (curto, 15 minutos) e refresh token (longo, 7 dias).
- O access token é enviado no header Authorization.
- Quando o access expira, o cliente envia o refresh token a um endpoint
/refresh, que retorna um novo access token.
O refresh token deve ser armazenado com segurança no servidor, para permitir revogação. Isso adiciona estado, mas é necessário para controle de revogação.
Erro comum: usar o mesmo segredo para access e refresh tokens. Use segredos diferentes e, idealmente, armazene o refresh token em um banco para invalidar em caso de logout.
Passo 8: Validar o token em cada requisição
A validação ocorre em duas camadas: a assinatura e a expiração. A biblioteca jsonwebtoken verifica ambas automaticamente no verify. Se o token foi adulterado, a assinatura não confere. Se expirou, o verify lança erro TokenExpiredError.
Para testar, use uma ferramenta como Insomnia ou Postman. Envie uma requisição sem token, com token inválido e com token expirado. Os três casos devem retornar 401.
Dica: em produção, use HTTPS. O token trafega no header e, sem HTTPS, pode ser interceptado por ataques man-in-the-middle.
Passo 9: Armazenar o token no cliente
No navegador, o token deve ser armazenado de forma segura. As opções são localStorage e cookies httpOnly. O localStorage é vulnerável a XSS, mas simples. Cookies httpOnly protegem contra XSS, mas exigem configuração de CSRF.
Se você usa React ou Vue, a prática comum é armazenar em localStorage e enviar via interceptador. Para aplicações críticas, prefira cookies httpOnly com proteção CSRF.
Erro comum: armazenar o token em sessionStorage e esperar que sobreviva a abas novas. sessionStorage é isolado por aba.
Passo 10: Testar o fluxo completo
Crie um teste manual para verificar o fluxo:
- Faça login com credenciais válidas. Você deve receber um token.
- Acesse uma rota protegida sem token. Deve retornar 401.
- Acesse com o token. Deve retornar 200.
- Modifique o token manualmente (altere um caractere). Deve retornar 401.
- Aguarde a expiração e tente novamente. Deve retornar 401.
Esse teste confirma que a implementação está correta. Se algum passo falhar, revise o middleware e a ordem das rotas.
Checklist final
Antes de considerar concluído, verifique:
- [ ] A chave secreta está em variável de ambiente, com pelo menos 32 caracteres.
- [ ] O payload do token contém apenas dados não sensíveis.
- [ ] O login retorna 401 para credenciais inválidas.
- [ ] O middleware usa
jwt.verify, nãojwt.decode. - [ ] Rotas públicas (login, registro) não usam o middleware.
- [ ] O cliente envia o token no formato
Bearer <token>. - [ ] Tokens expirados retornam 401.
- [ ] HTTPS está habilitado em produção.
Perguntas frequentes
O que é autenticação JWT?
Autenticação JWT é um método stateless para verificar identidade. O servidor emite um token assinado após o login, e o cliente o envia no header Authorization. O servidor valida a assinatura e a expiração a cada requisição, sem armazenar sessão. Isso reduz carga no servidor e facilita escalabilidade horizontal.
JWT é seguro?
JWT é seguro se implementado corretamente. A assinatura impede adulteração, mas o payload é apenas codificado, não criptografado. Nunca coloque dados sensíveis no payload. Use HTTPS para evitar interceptação e chave secreta forte. Tokens de longa duração aumentam o risco de vazamento.
Qual a diferença entre JWT e sessão?
Sessão armazena dados no servidor e envia um ID ao cliente. JWT envia todas as informações no token, sem armazenamento no servidor. Sessão facilita revogação imediata, enquanto JWT é mais escalável. Para logout imediato, é preciso usar refresh token revogável.
O que acontece se o token expirar?
O servidor retorna 401 na próxima requisição. O cliente deve então obter um novo token, usando o refresh token ou pedindo login novamente. A expiração é definida em expiresIn na hora de gerar o token. Valores comuns são 15 minutos para access e 7 dias para refresh.
Posso usar JWT sem HTTPS?
Tecnicamente sim, mas é arriscado. O token trafega no header e pode ser capturado em redes não seguras. Sem HTTPS, qualquer interceptação compromete a sessão. Em produção, HTTPS é obrigatório para qualquer sistema com autenticação.
Como revogar um token JWT antes da expiração?
JWT é stateless, então revogar exige uma lista negra no servidor. Uma alternativa é usar refresh token armazenado em banco; ao fazer logout, remova o refresh token. O access token continua válido até expirar, mas sem refresh, o acesso é limitado.