Voltar para o Blog avançado 25 minutos

Guia Avançado de Autenticação JWT (JSON Web Token) em Node.js

Tutorial passo a passo sobre autenticação JWT (JSON Web Token) avançada em aplicações Node.js, cobrindo conceitos, implementação prática, fluxo completo de login, refresh token e logout, middleware de proteção de rotas e melhores práticas de segurança.

Em aplicações modernas, a autenticação de usuários é fundamental para garantir acesso seguro aos recursos. JSON Web Tokens (JWT) tornaram-se uma abordagem popular para implementar autenticação sem estado (stateless) em APIs web e aplicações single-page (SPA). Neste tutorial avançado, exploraremos em profundidade o uso de JWT para autenticação em um ambiente Node.js. Vamos cobrir conceitos avançados sobre JWT (como payload, assinatura e diferentes tipos de token), e então passar para uma implementação prática em Node.js com Express, ilustrando um fluxo completo de autenticação, incluindo login de usuário, renovação de token (refresh token) e logout. Também implementaremos um middleware para proteger rotas e discutiremos as melhores práticas de segurança (expiração curta de tokens, uso de refresh token, armazenamento seguro, blacklist de tokens, etc.). Por fim, mostraremos como manter a estrutura do projeto modular e integraremos bibliotecas essenciais como jsonwebtoken, Express e dotenv. Ao final deste guia, você terá uma compreensão abrangente de como implementar autenticação JWT de forma robusta e segura em Node.js.

Conceitos Avançados sobre JWT

Um JWT (JSON Web Token) é um token em formato JSON utilizado para transmitir informações de forma segura entre duas partes. Ele é composto por três partes: Header, Payload e Signature, separadas por pontos. Quando codificado, um JWT pode se parecer com isto: xxxxx.yyyyy.zzzzz.

  • Header: contém metadados sobre o token, como o algoritmo de assinatura (alg) e o tipo de token (typ, geralmente JWT). Por exemplo, o header pode especificar HS256 (HMAC-SHA256) ou RS256 (RSA-SHA256) como algoritmo.
  • Payload: é o corpo do token, onde ficam as claims (declarações) do JWT. Estas claims podem ser informações sobre o usuário (por exemplo, ID, nome, roles) ou dados adicionais necessários. Existem claims padronizadas, como iss (issuer, emissor), sub (subject, assunto), iat (issued at, data de emissão) e exp (expiration time, tempo de expiração). O payload é codificado em Base64 e não é criptografado, o que significa que seu conteúdo pode ser lido por qualquer um que tenha o token (embora não possa ser alterado sem invalidar a assinatura).
  • Signature (Assinatura): é o resultado da assinatura digital do header e payload, gerada usando uma chave secreta (no caso de algoritmos simétricos como HS256) ou uma chave privada (no caso de algoritmos assimétricos como RS256). A assinatura serve para verificar se o token é autêntico e não foi alterado. Somente quem possui a chave secreta (ou a chave pública correspondente, no caso de RSA) pode verificar a assinatura.

Quando um JWT é criado e assinado pelo servidor, ele pode ser enviado ao cliente (por exemplo, após um login bem-sucedido). O cliente armazena esse token e o envia em requisições subsequentes, tipicamente em um cabeçalho Authorization: Bearer <token>, para acessar rotas protegidas. O servidor, por sua vez, verifica a assinatura e validade do token em cada requisição.

Tipos de tokens JWT: Em cenários de autenticação, é comum usar dois tipos de JWT com propósitos diferentes:

  • Token de acesso (access token): JWT de curta duração, usado para acessar recursos protegidos (APIs). Devido à sua curta validade (por exemplo, 15 minutos), reduz o impacto caso seja comprometido, pois expira rapidamente.
  • Refresh token: JWT de longa duração, usado exclusivamente para obter novos tokens de acesso quando estes expirarem, sem exigir que o usuário faça login novamente. O refresh token geralmente tem validade maior (por exemplo, dias ou semanas) e deve ser armazenado de forma ainda mais segura, pois seu comprometimento permite a obtenção de novos tokens de acesso.

Ao adotar JWT para autenticação, a aplicação pode permanecer sem estado no lado do servidor, pois os tokens em si carregam as informações de sessão do usuário. No entanto, é importante considerar estratégias para revogar tokens (invalidá-los antes da expiração), como veremos adiante, já que por padrão um JWT permanece válido até expirar.

Implementação prática com Node.js

Agora que entendemos os conceitos do JWT, vamos implementar uma solução de autenticação JWT em Node.js na prática. Usaremos o framework Express para criar uma API REST simples com rotas de autenticação, e a biblioteca jsonwebtoken para gerar e verificar os tokens JWT. Também utilizaremos dotenv para gerenciar configurações sensíveis (como as chaves secretas) através de variáveis de ambiente. O fluxo de autenticação que construiremos incluirá login, emissão de token JWT, refresh de token e logout, além de middleware para proteção de rotas. Nos passos a seguir, descrevemos cada etapa dessa implementação.

Fluxo completo de autenticação JWT: passo a passo

Passo 1: Configuração do projeto e dependências

Primeiro, vamos configurar o projeto Node.js. Certifique-se de ter o Node.js instalado em seu sistema. Crie um diretório para o projeto e inicie um projeto Node:

mkdir projeto-jwt-node
cd projeto-jwt-node
npm init -y

Em seguida, instale as dependências necessárias, incluindo Express, jsonwebtoken e dotenv:

npm install express jsonwebtoken dotenv

Opcionalmente, você pode instalar o nodemon para reiniciar o servidor automaticamente durante o desenvolvimento:

npm install --save-dev nodemon

Com as dependências instaladas, vamos criar a estrutura básica de arquivos. Para este tutorial, usaremos uma estrutura simplificada:

projeto-jwt-node/
├── server.js          // Arquivo principal do servidor Node/Express
├── routes/
│   └── auth.js        // Rotas de autenticação (login, refresh, logout, etc.)
├── middleware/
│   └── auth.js        // Middleware de autenticação JWT
├── .env               // Variáveis de ambiente (chaves secretas, configurações)
└── package.json

Essa estrutura separa as responsabilidades: teremos um arquivo principal (server.js) para configurar o servidor, um módulo de rotas de autenticação e um módulo para o middleware de JWT. A seguir, configuraremos as variáveis de ambiente.

Passo 2: Definição de variáveis de ambiente

Crie um arquivo .env na raiz do projeto para armazenar as variáveis de ambiente, como as chaves secretas dos JWT e tempos de expiração:

JWT_SECRET=SUA_CHAVE_SECRETA_AQUI
JWT_REFRESH_SECRET=SUA_CHAVE_SECRETA_REFRESH_AQUI
JWT_EXPIRES=15m
JWT_REFRESH_EXPIRES=7d
PORT=3000

No exemplo acima, definimos duas chaves secretas distintas: uma para assinar tokens de acesso (JWT_SECRET) e outra para tokens de refresh (JWT_REFRESH_SECRET). Também definimos que o token de acesso expira em 15 minutos (15m) e o refresh token em 7 dias (7d). Você pode ajustar esses valores conforme a necessidade de segurança da sua aplicação. A porta do servidor está definida como 3000, mas pode ser alterada.

Passo 3: Configuração do servidor Express

Agora, vamos configurar o servidor Express no arquivo server.js. Esse arquivo irá inicializar o Express, carregar as variáveis de ambiente usando dotenv, configurar o parsing de JSON e importar as rotas de autenticação:

const express = require('express');
const dotenv = require('dotenv');

dotenv.config(); // Carrega as variáveis do arquivo .env

const app = express();

// Middleware para interpretar JSON no corpo das requisições
app.use(express.json());

// Importa as rotas de autenticação
const authRoutes = require('./routes/auth');
app.use('/api/auth', authRoutes);

// Porta do servidor
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
  console.log(`Servidor rodando na porta ${PORT}`);
});

Nessa configuração, montamos as rotas de autenticação sob o caminho /api/auth (por exemplo, /api/auth/login será a URL para login). O uso de dotenv.config() carrega as configurações que definimos no .env para process.env. A seguir, criaremos as rotas de autenticação no arquivo routes/auth.js.

Passo 4: Implementando a rota de Login e geração do JWT

No arquivo routes/auth.js, vamos criar a rota de login. Essa rota verifica as credenciais do usuário e, se estiverem corretas, gera um token JWT de acesso e um refresh token. Para simplificar, usaremos um usuário de exemplo definido em memória. Em aplicações reais, você verificaria as credenciais consultando um banco de dados e também faria hash de senhas ao armazená-las.

const express = require('express');
const jwt = require('jsonwebtoken');
const router = express.Router();

// Usuário de exemplo (em uma aplicação real, use um banco de dados)
const usuarios = [
  { id: 1, username: 'admin', password: '123456', role: 'admin' }
];

// Armazenamento de refresh tokens válidos (em produção, prefira um banco ou cache)
let refreshTokens = [];

// Rota de login
router.post('/login', (req, res) => {
  const { username, password } = req.body;
  const usuario = usuarios.find(u => u.username === username);
  if (!usuario || usuario.password !== password) {
    return res.status(401).json({ erro: 'Credenciais inválidas' });
  }
  // Credenciais válidas - gerar tokens
  const accessToken = jwt.sign(
    { id: usuario.id, username: usuario.username, role: usuario.role },
    process.env.JWT_SECRET,
    { expiresIn: process.env.JWT_EXPIRES }
  );
  const refreshToken = jwt.sign(
    { id: usuario.id },
    process.env.JWT_REFRESH_SECRET,
    { expiresIn: process.env.JWT_REFRESH_EXPIRES }
  );
  // Armazena o refresh token gerado
  refreshTokens.push(refreshToken);
  // Retorna os tokens para o cliente
  res.json({ accessToken, refreshToken });
});

module.exports = router;

Acima, definimos um array usuarios com um usuário de exemplo e um array refreshTokens para armazenar os refresh tokens gerados. Na rota /login, validamos o username e password enviados no corpo da requisição. Se forem inválidos, retornamos status 401 (Não autorizado). Se forem válidos, utilizamos jwt.sign para criar um access token com alguns dados do usuário (id, username, role) e a chave secreta JWT_SECRET, configurando a expiração definida em JWT_EXPIRES. Também geramos um refresh token usando JWT_REFRESH_SECRET e tempo de expiração mais longo JWT_REFRESH_EXPIRES. Armazenamos o refresh token no array refreshTokens e enviamos ambos os tokens de volta ao cliente em formato JSON.

Com os tokens em mãos, o cliente deverá incluir o accessToken nas requisições subsequentes às rotas protegidas, usualmente adicionando um cabeçalho Authorization: Bearer <accessToken> em cada requisição.

Passo 5: Implementando a rota de Refresh Token

A rota de refresh permite que o cliente obtenha um novo token de acesso usando um refresh token válido, sem necessitar fazer login novamente. Vamos implementá-la no mesmo arquivo routes/auth.js:

// Rota de refresh token
router.post('/refresh', (req, res) => {
  const { refreshToken } = req.body;
  if (!refreshToken) {
    return res.status(400).json({ erro: 'Refresh token é obrigatório' });
  }
  if (!refreshTokens.includes(refreshToken)) {
    return res.status(403).json({ erro: 'Refresh token inválido' });
  }
  try {
    // Verifica se o refresh token é válido e não expirou
    const dados = jwt.verify(refreshToken, process.env.JWT_REFRESH_SECRET);
    const userId = dados.id;
    // (Opcional) Poderia-se invalidar o refresh token antigo aqui, implementando rotação de token
    // Gera um novo token de acesso para o mesmo usuário
    const usuario = usuarios.find(u => u.id === userId);
    if (!usuario) {
      return res.status(403).json({ erro: 'Usuário não encontrado' });
    }
    const novoAccessToken = jwt.sign(
      { id: usuario.id, username: usuario.username, role: usuario.role },
      process.env.JWT_SECRET,
      { expiresIn: process.env.JWT_EXPIRES }
    );
    // Opcional: Gerar também um novo refresh token e substituir o antigo na lista
    res.json({ accessToken: novoAccessToken });
  } catch (e) {
    return res.status(403).json({ erro: 'Refresh token inválido ou expirado' });
  }
});

Nesta rota /refresh, primeiro verificamos se o refreshToken foi fornecido no corpo da requisição. Se não, retornamos erro 400. Em seguida, verificamos se esse token está armazenado na lista refreshTokens; se não estiver, retornamos 403 (forbidden), pois pode ser um token inválido ou já logout. Então usamos jwt.verify com a chave JWT_REFRESH_SECRET para validar o refresh token. Caso a verificação seja bem sucedida, extraímos o id do usuário dos dados do token e emitimos um novo accessToken para esse usuário (usando novamente jwt.sign com JWT_SECRET). Devolvemos o novo token de acesso em resposta. Em caso de qualquer falha (token inválido ou expirado), retornamos 403.

Dica de segurança: Uma melhoria aqui seria implementar rotação de refresh token: ao usar o refresh token para obter um novo, gerar também um novo refresh token e invalidar o antigo (removendo-o da lista ou marcando-o como usado). Assim, um refresh token vazado não poderia ser reutilizado múltiplas vezes. Isso aumenta a segurança, embora adicione complexidade.

Passo 6: Criando o middleware de verificação JWT

Para proteger rotas, vamos criar um middleware que valida o token de acesso enviado pelo cliente nas requisições. Esse middleware verificará o cabeçalho Authorization, validará o JWT e disponibilizará os dados do usuário para as próximas etapas da request caso o token seja válido. Crie o arquivo middleware/auth.js com o seguinte conteúdo:

const jwt = require('jsonwebtoken');

function verificarToken(req, res, next) {
  const authHeader = req.headers['authorization'];
  const token = authHeader && authHeader.split(' ')[1];
  if (!token) {
    return res.status(401).json({ erro: 'Acesso negado: token não fornecido' });
  }
  try {
    const decodificado = jwt.verify(token, process.env.JWT_SECRET);
    req.user = decodificado;
    next();
  } catch (err) {
    return res.status(403).json({ erro: 'Token inválido ou expirado' });
  }
}

module.exports = verificarToken;

O middleware verificarToken acima procura pelo token JWT no cabeçalho Authorization da requisição (esperando o formato Bearer <token>). Se não encontrar o token, responde com 401 (não autorizado). Se o token existir, utiliza jwt.verify com a chave secreta JWT_SECRET para validá-lo. Caso a verificação seja bem sucedida, os dados decodificados do usuário (as claims do token, por exemplo id, username, role) são anexados ao objeto req (por exemplo, req.user = decodificado) e a execução continua (next()). Se a validação falhar ou o token estiver expirado, retornamos 403 (proibido).

Passo 7: Protegendo rotas com o middleware de autenticação

Agora podemos aplicar o middleware verificarToken em rotas que desejamos proteger. Vamos adicionar uma rota de exemplo que retorna dados de perfil do usuário apenas se ele estiver autenticado. Adicione no routes/auth.js (ou em outra rota protegida conforme sua estrutura) o seguinte:

const verificarToken = require('../middleware/auth');

// ... (código anterior de auth.js) ...

// Rota protegida de exemplo (dados do usuário)
router.get('/perfil', verificarToken, (req, res) => {
  // Nesta rota, req.user contém os dados do token JWT decodificado
  res.json({ mensagem: 'Dados de perfil acessados com sucesso', usuario: req.user });
});

Nessa rota /perfil, aplicamos o middleware verificarToken. Se o token JWT enviado for válido, req.user terá os dados do usuário e a rota responderá com uma mensagem de sucesso e os dados do usuário (neste caso, apenas os dados contidos no token). Caso o token seja inválido ou ausente, o middleware já terá respondido com o status apropriado, e a função da rota não será executada.

Você pode adicionar quantas rotas protegidas forem necessárias da mesma forma, garantindo que verificarToken seja passado como middleware antes do handler da rota. Além disso, para autorização baseada em permissões ou roles, é possível verificar req.user.role dentro do handler ou em outro middleware para permitir ou negar acesso a determinadas rotas (veremos isso a seguir).

Passo 8: Implementando a rota de Logout

Por fim, vamos criar a rota de logout para invalidar o refresh token do usuário. Embora o JWT de acesso vá expirar naturalmente, remover o refresh token garante que não serão emitidos novos tokens de acesso após o logout. Adicione em routes/auth.js:

// Rota de logout
router.post('/logout', verificarToken, (req, res) => {
  const { refreshToken } = req.body;
  if (!refreshToken) {
    return res.status(400).json({ erro: 'Refresh token é obrigatório' });
  }
  // Remove o refresh token da lista para invalidá-lo
  refreshTokens = refreshTokens.filter(token => token !== refreshToken);
  res.json({ mensagem: 'Logout realizado com sucesso' });
});

Nesta implementação de /logout, esperamos que o cliente envie também o refreshToken no corpo da requisição de logout. Utilizamos novamente o middleware verificarToken aqui para garantir que a requisição de logout esteja autenticada (opcional, pois se o usuário está se deslogando, presumivelmente ele tem um token válido). Removemos o refreshToken fornecido da lista refreshTokens, efetivamente invalidando-o para uso futuro. Assim, mesmo que alguém possua aquele token, não conseguirá usá-lo para obter um novo accessToken. Finalmente, retornamos uma mensagem de sucesso.

Vale notar que, com essa abordagem simples, o token de acesso em si não é imediatamente invalidado; ele expirará no tempo determinado (JWT_EXPIRES). Se for necessário invalidar também o token de acesso antes da expiração (por exemplo, em um logout ativo), seria preciso manter um registro de tokens de acesso inválidos (uma blacklist) e consultá-la no middleware de verificação. Essa adição traz mais complexidade e uso de armazenamento, por isso muitos sistemas optam por confiar na curta duração do access token e invalidar apenas o refresh token no logout.

Middleware de verificação e autorização de rotas

Já implementamos o middleware de autenticação JWT (verificarToken) para garantir que somente usuários com um token válido acessem rotas protegidas. Além da autenticação, aplicações avançadas geralmente implementam controle de autorização mais granular. Isso significa verificar se o usuário autenticado tem permissão para realizar certa ação ou acessar determinada rota.

Uma abordagem comum é utilizar o campo role (perfil ou papel) do usuário, que foi incluído no token JWT no nosso exemplo de login. Podemos criar um middleware adicional para verificar a role do usuário antes de permitir acesso. Por exemplo, para restringir acesso apenas a administradores em uma rota, poderíamos ter:

function autorizarRoles(...rolesPermitidas) {
  return (req, res, next) => {
    if (!rolesPermitidas.includes(req.user.role)) {
      return res.status(403).json({ erro: 'Acesso negado: permissão insuficiente' });
    }
    next();
  };
}

// Uso em uma rota de administrador, por exemplo:
router.get('/admin', verificarToken, autorizarRoles('admin'), (req, res) => {
  res.json({ mensagem: 'Bem-vindo, administrador!' });
});

No exemplo acima, a função autorizarRoles retorna um middleware que verifica se req.user.role está incluído nas roles permitidas para a rota. Se não estiver, respondemos com 403 (forbidden); se estiver, chamamos next() para prosseguir para o handler da rota. Dessa forma, combinando a verificação de autenticação (verificarToken) e autorização por perfil, conseguimos proteger rotas de forma adequada.

A implementação exata da autorização dependerá das necessidades da sua aplicação. Você pode usar claims personalizadas no JWT (por exemplo, uma lista de permissões ou escopos) para verificação, ou consultar um banco de dados de permissões. O importante é manter essas verificações no servidor, já que o cliente pode alterar interfaces, mas não pode forjar um token válido sem a chave secreta do servidor.

Práticas recomendadas de segurança

Ao trabalhar com JWT e autenticação, é crucial adotar práticas de segurança para mitigar riscos comuns:

  • Use tempos de expiração curtos para tokens de acesso: Tokens JWT não devem ter vida longa. Defina o expiresIn do token de acesso para um período curto (p. ex. 15 minutos ou menos)​:contentReference[oaicite:0]{index=0}. Assim, caso um token seja comprometido, a janela de uso indevido é limitada.
  • Mantenha os refresh tokens seguros e com expiração adequada: Refresh tokens permitem prolongar a sessão sem novo login, mas devem expirar em um prazo razoável (p. ex. alguns dias)​:contentReference[oaicite:1]{index=1}. Guarde-os com cuidado extra, de preferência em cookies HTTP-Only seguros (quando utilizado em navegadores) para evitar acesso via JavaScript (mitigando XSS).
  • Armazene as chaves secretas de forma segura: Nunca exponha as secret keys do JWT no código-fonte público ou repos. Use variáveis de ambiente (como fizemos com dotenv)​:contentReference[oaicite:2]{index=2}​:contentReference[oaicite:3]{index=3} e mantenha-as fora do controle de versão. Utilize chaves longas e aleatórias para dificultar ataques de força bruta​:contentReference[oaicite:4]{index=4}.
  • Verifique e valide o token em todas as requisições protegidas: Sempre utilize o middleware de verificação para garantir que cada requisição tenha um token válido​:contentReference[oaicite:5]{index=5}​:contentReference[oaicite:6]{index=6}. Não confie apenas no token apresentado pelo cliente sem validá-lo no servidor.
  • Utilize HTTPS: Sempre sirva sua API sobre HTTPS​:contentReference[oaicite:7]{index=7}. Isso impede que tokens sejam interceptados em trânsito por atacantes man-in-the-middle. Nunca envie JWTs (especialmente refresh tokens) por conexões não seguras.
  • Implementar invalidação de tokens (quando necessário): Como mencionado, JWTs por padrão só expiram quando o tempo se esgota. Se a aplicação requer revogação imediata (por exemplo, logout ativo, remoção de acessos), implemente uma lista de tokens revogados (blacklist) ou altere alguma referência no servidor para considerar tokens antigos inválidos (por exemplo, um campo de versão no usuário que é checado em cada token). Embora isso traga estado ao servidor, pode ser necessário em cenários de segurança mais rígidos.
  • Não armazene dados sensíveis no payload: Lembre-se de que o conteúdo do JWT pode ser lido por qualquer um que possua o token. Não coloque informações como senhas ou dados pessoais sensíveis no payload. Mantenha apenas o mínimo necessário (identificação do usuário, permissões, etc.).
  • Proteja credenciais de usuário adequadamente: Ainda que não seja específico dos JWT, garanta que as senhas de usuários estejam armazenadas de forma segura (usar hash forte, como bcrypt, ao invés de texto puro) e aplique políticas de senha seguras. Isso protege a etapa de login, evitando que atacantes consigam facilmente comprometer contas.

Seguindo essas práticas, sua implementação de autenticação JWT será muito mais segura e menos propensa a vulnerabilidades. Lembre-se de que segurança é um processo contínuo: mantenha as dependências atualizadas (bibliotecas como jsonwebtoken) para receber correções de segurança, e fique atento a novas recomendações da comunidade.

Estrutura modular e organizada do projeto

Manter uma estrutura de projeto organizada é importante à medida que sua aplicação cresce. No nosso exemplo, separamos as funcionalidades em módulos diferentes:

  • Arquivo principal do servidor (server.js): responsável por configurar o servidor Express, conectar middleware globais e montar as rotas principais.
  • Módulo de rotas de autenticação (routes/auth.js): contém as definições das rotas de login, refresh, logout e quaisquer outras relacionadas à autenticação. Poderia ser ainda mais organizado separando a lógica de cada rota em um controlador (controllers/authController.js, por exemplo), especialmente se houver muita lógica.
  • Middleware de autenticação (middleware/auth.js): encapsula a lógica de verificação do JWT, podendo ser reutilizado em qualquer rota protegida da aplicação.
  • Variáveis de ambiente (.env): arquivo separado para configurações sensíveis e específicas do ambiente (chaves secretas, configurações de expiracão, etc.), mantendo esses dados fora do código-fonte principal.
  • Outros módulos: Em aplicações reais, você terá outros módulos, como rotas e controladores para recursos (usuários, produtos, etc.), utilitários (por exemplo, funções para gerar tokens, enviar emails de confirmação, etc.), e talvez serviços de acesso a banco de dados. Estruturar cada parte em pastas e arquivos separados ajuda na manutenção e facilita testes.

Por exemplo, poderíamos ter um arquivo controllers/authController.js contendo funções como loginUsuario, refreshToken e logoutUsuario, e no arquivo de rotas auth.js apenas importar essas funções e registrá-las nas rotas (e.g. router.post('/login', loginUsuario)). Essa separação limpa a lógica das rotas e permite reaproveitar o código (por exemplo, chamar loginUsuario em um teste unitário facilmente). Ao planejar a estrutura, procure seguir convenções do framework e deixar claro onde cada responsabilidade está implementada.

Bibliotecas utilizadas

Este projeto utiliza algumas bibliotecas npm chave para viabilizar a autenticação JWT:

  • express: Framework web para Node.js, facilitando a criação de servidores HTTP e rotas. Usamos o Express para definir nossas rotas de API (login, refresh, etc.) e middleware.
  • jsonwebtoken: Biblioteca para criação e verificação de JWTs. Fornece métodos como jwt.sign() para gerar um token e jwt.verify() para validar. Essa biblioteca implementa os padrões JWT de forma segura, contanto que usada corretamente (por exemplo, sempre fornecendo a chave secreta correta e tratando os erros de verificação).
  • dotenv: Utilitário para carregar variáveis de ambiente de um arquivo .env para process.env. Isso permite manter chaves secretas e configurações fora do código. No nosso projeto, usamos dotenv para carregar JWT_SECRET, JWT_REFRESH_SECRET, tempos de expiração e a porta do servidor.
  • nodemon (devDependency): Ferramenta de desenvolvimento que reinicia automaticamente o servidor Node ao detectar mudanças nos arquivos. Útil durante o desenvolvimento para agilizar testes das alterações sem precisar reiniciar manualmente.
  • body-parser (opcional): Em versões mais antigas do Express, era necessário usar o body-parser para tratar JSON no corpo das requisições. No nosso código usamos express.json() (disponível no Express moderno) para esse fim. Se estivesse usando uma versão muito antiga do Express, você instalaria e utilizaria body-parser da mesma forma.

Além dessas, em implementações reais você provavelmente usará outras bibliotecas conforme a necessidade: por exemplo, bcrypt para hash de senhas, bibliotecas de banco de dados (como mongoose ou pg), entre outras. Escolha bibliotecas mantidas ativamente e com boa reputação na comunidade para garantir segurança e suporte.

Conclusão

Neste tutorial avançado, desenvolvemos um sistema completo de autenticação JWT em Node.js, cobrindo desde os conceitos fundamentais de JSON Web Tokens até a implementação prática de rotas de login, refresh token e logout, além do uso de um middleware para proteger rotas e técnicas de autorização. Também discutimos as melhores práticas de segurança envolvidas na gestão de tokens e estruturação do projeto.

Com essa base, você pode ampliar e adaptar a solução para as necessidades da sua aplicação. Por exemplo, integrar com um banco de dados real para validar usuários, implementar refresh token rotativo, enviar tokens via cookies HTTP-only para apps web, e assim por diante. Lembre-se de que a segurança deve ser priorizada em cada etapa – desde a proteção das credenciais de usuário até o uso adequado dos tokens JWT.

Ao aplicar JWT corretamente, você consegue um sistema de autenticação escalável e interoperável, pois os tokens podem ser usados em diferentes serviços e até mesmo em aplicações móveis ou de terceiros com segurança (desde que verificados apropriadamente). Continue explorando as documentações das bibliotecas e fique atento a atualizações de segurança para manter sua implementação atualizada. Bom desenvolvimento!