<h2>Autenticação em Node.js: JWT, Bcrypt e Sessões com Express</h2>
<p>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.</p>
<h2>Proteção de Senhas com Bcrypt</h2>
<h3>Por que não armazenar senhas em texto plano?</h3>
<p>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).</p>
<pre><code class="language-javascript">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');</code></pre>
<p>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 <code>compare()</code> é crucial: ela hash a entrada e compara com o hash armazenado, nunca descriptografando.</p>
<h2>JWT: Autenticação Stateless</h2>
<h3>Estrutura e fluxo JWT</h3>
<p>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.</p>
<pre><code class="language-javascript">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} });
});</code></pre>
<p><strong>Quando usar JWT</strong>: 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.</p>
<h2>Gerenciamento de Sessões com Express</h2>
<h3>Sessões no lado do servidor</h3>
<p>Sessões mantêm estado no servidor. O Express, combinado com <code>express-session</code> e um store de sessões, cria um cookie no cliente contendo apenas um identificador, enquanto os dados reais ficam no servidor.</p>
<pre><code class="language-javascript">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' });
});
});</code></pre>
<p><strong>Quando usar sessões</strong>: 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.</p>
<h2>Comparação e Escolha</h2>
<blockquote><p><strong>JWT</strong>: Stateless, escalável, ideal para APIs e microsserviços. Desvantagem: revogar token é complexo (mantenha blacklist em cache).</p></blockquote>
<blockquote><p><strong>Sessões</strong>: Stateful, controle total, fácil revogar. Desvantagem: requer infraestrutura de armazenamento.</p></blockquote>
<p>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.</p>
<h2>Conclusão</h2>
<p>Três pontos-chave dominados nesta aula:</p>
<ol>
<li><strong>Bcrypt protege senhas irreversivelmente</strong> — sempre hash com salt, nunca armazene em texto plano. Use <code>bcrypt.compare()</code> para validar, nunca armazene a senha plana.</li>
</ol>
<ol>
<li><strong>JWT oferece autenticação stateless</strong> — ideal para APIs, mas exige estratégia para revogação. Token é autocontido e escalável entre múltiplos servidores.</li>
</ol>
<ol>
<li><strong>Sessões mantêm controle centralizado</strong> — simples revogar, mas requerem estado no servidor. Escolha baseando-se na arquitetura e requisitos de escalabilidade.</li>
</ol>
<p>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.</p>
<h2>Referências</h2>
<ul>
<li><a href="https://www.npmjs.com/package/jsonwebtoken" target="_blank" rel="noopener noreferrer">Documentação oficial jsonwebtoken (npm)</a></li>
<li><a href="https://www.npmjs.com/package/bcrypt" target="_blank" rel="noopener noreferrer">Documentação oficial bcrypt (npm)</a></li>
<li><a href="https://www.npmjs.com/package/express-session" target="_blank" rel="noopener noreferrer">Express-session documentação</a></li>
<li><a href="https://cheatsheetseries.owasp.org/cheatsheets/Authentication_Cheat_Sheet.html" target="_blank" rel="noopener noreferrer">OWASP Authentication Cheat Sheet</a></li>
<li><a href="https://github.com/goldbergyoni/nodebestpractices#6-security-best-practices" target="_blank" rel="noopener noreferrer">Node.js Best Practices - Security</a></li>
</ul>