Guia Completo de Autenticação em Node.js: JWT, Bcrypt e Sessões com Express Já leu

Autenticação em Node.js: JWT, Bcrypt e Sessões com Express Autenticação é o pilar da segurança em aplicações web. Nesta aula, exploraremos três abordagens fundamentais: JWT (JSON Web Tokens), Bcrypt para hash de senhas e gerenciamento de sessões com Express. Compreender quando e como usar cada uma transformará sua capacidade de construir sistemas seguros e escaláveis. Proteção de Senhas com Bcrypt Por que não armazenar senhas em texto plano? Senhas jamais devem ser armazenadas em texto plano no banco de dados. Se comprometido, todos os usuários estarão em risco. O Bcrypt é uma função de hash adaptativa que torna computacionalmente cara a reversão, implementando "salt" automaticamente (um valor aleatório que previne ataques de rainbow table). O custo 10 (padrão) significa que o algoritmo itera 2^10 vezes. Nunca use custo abaixo de 10 em produção. A função é crucial: ela hash a entrada e compara com o hash armazenado, nunca descriptografando. JWT: Autenticação Stateless Estrutura e fluxo JWT JWT (JSON Web Token)

Autenticação em Node.js: JWT, Bcrypt e Sessões com Express

Autenticação é o pilar da segurança em aplicações web. Nesta aula, exploraremos três abordagens fundamentais: JWT (JSON Web Tokens), Bcrypt para hash de senhas e gerenciamento de sessões com Express. Compreender quando e como usar cada uma transformará sua capacidade de construir sistemas seguros e escaláveis.

Proteção de Senhas com Bcrypt

Por que não armazenar senhas em texto plano?

Senhas jamais devem ser armazenadas em texto plano no banco de dados. Se comprometido, todos os usuários estarão em risco. O Bcrypt é uma função de hash adaptativa que torna computacionalmente cara a reversão, implementando "salt" automaticamente (um valor aleatório que previne ataques de rainbow table).

const bcrypt = require('bcrypt');

// Durante o registro do usuário
async function registrarUsuario(email, senhaPlana) {
  const salt = await bcrypt.genSalt(10); // Gera salt com custo 10
  const senhaHash = await bcrypt.hash(senhaPlana, salt);

  // Salva no banco: { email, senhaHash }
  console.log('Senha armazenada:', senhaHash);
}

// Durante login, valida a senha
async function validarSenha(senhaPlana, senhaHashArmazenada) {
  const valida = await bcrypt.compare(senhaPlana, senhaHashArmazenada);
  return valida; // true ou false
}

registrarUsuario('user@example.com', 'minhaSenha123');

O custo 10 (padrão) significa que o algoritmo itera 2^10 vezes. Nunca use custo abaixo de 10 em produção. A função compare() é crucial: ela hash a entrada e compara com o hash armazenado, nunca descriptografando.

JWT: Autenticação Stateless

Estrutura e fluxo JWT

JWT (JSON Web Token) é um padrão aberto que permite transmitir informações seguras entre partes. Composto por três seções separadas por pontos: header.payload.signature. Diferente de sessões, não requer armazenamento no servidor, tornando-o ideal para APIs escaláveis.

const jwt = require('jsonwebtoken');
const express = require('express');
const app = express();

const SECRET = process.env.JWT_SECRET || 'sua_chave_secreta_super_segura';

// Gerar token após login bem-sucedido
function gerarToken(usuario) {
  const payload = {
    id: usuario.id,
    email: usuario.email
  };

  const token = jwt.sign(payload, SECRET, { expiresIn: '24h' });
  return token;
}

// Middleware para verificar token
function verificarToken(req, res, next) {
  const token = req.headers.authorization?.split(' ')[1]; // Bearer <token>

  if (!token) {
    return res.status(401).json({ erro: 'Token não fornecido' });
  }

  try {
    const decoded = jwt.verify(token, SECRET);
    req.usuario = decoded; // Adiciona ao request
    next();
  } catch (error) {
    res.status(401).json({ erro: 'Token inválido ou expirado' });
  }
}

// Rota protegida
app.get('/perfil', verificarToken, (req, res) => {
  res.json({ mensagem: `Bem-vindo, ${req.usuario.email}` });
});

Quando usar JWT: APIs RESTful, aplicações mobile, arquiteturas de microsserviços. O cliente armazena o token (localStorage, memory) e o envia a cada requisição. O servidor apenas valida, sem manter estado.

Gerenciamento de Sessões com Express

Sessões no lado do servidor

Sessões mantêm estado no servidor. O Express, combinado com express-session e um store de sessões, cria um cookie no cliente contendo apenas um identificador, enquanto os dados reais ficam no servidor.

const session = require('express-session');
const RedisStore = require('connect-redis').default;
const { createClient } = require('redis');
const express = require('express');

const app = express();

// Cliente Redis para armazenar sessões
const redisClient = createClient();
redisClient.connect();

// Configurar sessões
app.use(session({
  store: new RedisStore({ client: redisClient }),
  secret: process.env.SESSION_SECRET || 'chave_secreta',
  resave: false,
  saveUninitialized: false,
  cookie: {
    secure: true, // HTTPS apenas
    httpOnly: true, // Não acessível por JavaScript
    sameSite: 'strict',
    maxAge: 24 * 60 * 60 * 1000 // 24 horas
  }
}));

// Login com sessão
app.post('/login', (req, res) => {
  // Após validar credenciais...
  req.session.usuarioId = 123;
  req.session.email = 'user@example.com';
  res.json({ mensagem: 'Login realizado' });
});

// Rota protegida
app.get('/dashboard', (req, res) => {
  if (!req.session.usuarioId) {
    return res.status(401).json({ erro: 'Não autenticado' });
  }
  res.json({ mensagem: `Bem-vindo, ${req.session.email}` });
});

// Logout
app.post('/logout', (req, res) => {
  req.session.destroy((err) => {
    if (err) return res.status(500).json({ erro: err });
    res.json({ mensagem: 'Logout realizado' });
  });
});

Quando usar sessões: Aplicações monolíticas, single-page applications (SPAs) server-side, quando o servidor é confiável e centralizado. Redis ou banco de dados armazenam dados da sessão; o cliente recebe apenas um cookie seguro.

Comparação e Escolha

JWT: Stateless, escalável, ideal para APIs e microsserviços. Desvantagem: revogar token é complexo (mantenha blacklist em cache).

Sessões: Stateful, controle total, fácil revogar. Desvantagem: requer infraestrutura de armazenamento.

A escolha depende da arquitetura. Em um monólito com um único servidor, sessões são simples. Em uma arquitetura distribuída com múltiplos servidores/microsserviços, JWT reduz overhead. Uma estratégia híbrida é comum: use JWT para autenticação inicial e sessão para dados sensíveis locais.

Conclusão

Três pontos-chave dominados nesta aula:

  1. Bcrypt protege senhas irreversivelmente — sempre hash com salt, nunca armazene em texto plano. Use bcrypt.compare() para validar, nunca armazene a senha plana.

  2. JWT oferece autenticação stateless — ideal para APIs, mas exige estratégia para revogação. Token é autocontido e escalável entre múltiplos servidores.

  3. Sessões mantêm controle centralizado — simples revogar, mas requerem estado no servidor. Escolha baseando-se na arquitetura e requisitos de escalabilidade.

Combine essas técnicas conforme necessário. Em aplicações reais, JWT para APIs públicas, sessões para SPAs e sempre Bcrypt para senhas.

Referências


Artigos relacionados