A segurança em aplicações web modernas é fundamental, especialmente na autenticação de APIs REST. Este tutorial demonstra como implementar um sistema de autenticação robusto usando Node.js e JSON Web Tokens (JWT), fornecendo proteção eficaz contra acessos não autorizados.
O que é JWT e Por que Utilizá-lo
JSON Web Tokens (JWT) é um padrão RFC 7519 que define uma forma compacta e autocontida de transmitir informações entre partes como objeto JSON. Os tokens são assinados digitalmente, garantindo integridade e autenticidade dos dados.
Segundo estudos da Stack Overflow, JWT é utilizado por 65% dos desenvolvedores para autenticação em APIs, devido à sua flexibilidade e escalabilidade.
Vantagens do JWT sobre Sessions Tradicionais
- Stateless: Elimina necessidade de armazenar sessões no servidor
- Escalabilidade: Funciona perfeitamente em arquiteturas distribuídas
- CORS-friendly: Ideal para SPAs e aplicações cross-domain
- Performance: Reduz consultas ao banco de dados para validação
- Flexibilidade: Permite inclusão de claims personalizados
Configuração do Ambiente de Desenvolvimento
Inicialize um novo projeto Node.js e instale as dependências essenciais:
npm init -y
npm install express jsonwebtoken bcryptjs dotenv
npm install --save-dev nodemon
Estrutura Recomendada do Projeto
projeto-jwt/
??? src/
? ??? controllers/
? ? ??? authController.js
? ??? middleware/
? ? ??? authMiddleware.js
? ??? models/
? ? ??? User.js
? ??? routes/
? ??? authRoutes.js
??? .env
??? index.js
??? package.json
Implementando a API Base
Crie o arquivo principal da aplicação com Express:
// index.js
const express = require(\'express\');
const authRoutes = require(\'./src/routes/authRoutes\');
require(\'dotenv\').config();
const app = express();
// Middlewares globais
app.use(express.json({ limit: \'10mb\' }));
app.use(express.urlencoded({ extended: true }));
// Headers de segurança
app.use((req, res, next) => {
res.header(\'Access-Control-Allow-Origin\', \'*\');
res.header(\'Access-Control-Allow-Headers\', \'Origin, X-Requested-With, Content-Type, Accept, x-access-token\');
res.header(\'Access-Control-Allow-Methods\', \'GET, POST, PUT, DELETE, OPTIONS\');
next();
});
// Rotas
app.use(\'/api/auth\', authRoutes);
app.get(\'/api/health\', (req, res) => {
res.status(200).json({
status: \'OK\',
timestamp: new Date().toISOString(),
uptime: process.uptime()
});
});
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
console.log(? Servidor executando na porta ${PORT});
});
Configurando Variáveis de Ambiente
Crie um arquivo .env para armazenar configurações sensíveis:
.env
JWT_SECRET=seu_jwt_secret_super_seguro_aqui_min_32_caracteres
JWT_EXPIRES_IN=24h
BCRYPT_ROUNDS=12
PORT=3000
Middleware de Autenticação JWT
Implemente o middleware responsável pela validação dos tokens:
// src/middleware/authMiddleware.js
const jwt = require(\'jsonwebtoken\');
const 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: \'Token de acesso requerido\',
code: \'TOKEN_MISSING\'
});
}
try {
const decoded = jwt.verify(token, process.env.JWT_SECRET);
req.user = decoded;
next();
} catch (error) {
if (error.name === \'TokenExpiredError\') {
return res.status(401).json({
error: \'Token expirado\',
code: \'TOKEN_EXPIRED\'
});
}
if (error.name === \'JsonWebTokenError\') {
return res.status(403).json({
error: \'Token inválido\',
code: \'TOKEN_INVALID\'
});
}
return res.status(500).json({
error: \'Erro interno do servidor\',
code: \'INTERNAL_ERROR\'
});
}
};
module.exports = { authenticateToken };
Sistema de Registro e Login
Desenvolva o controller para gerenciar autenticação de usuários:
// src/controllers/authController.js
const bcrypt = require(\'bcryptjs\');
const jwt = require(\'jsonwebtoken\');
// Simulando banco de dados (use MongoDB/PostgreSQL em produção)
const users = [];
const generateToken = (payload) => {
return jwt.sign(payload, process.env.JWT_SECRET, {
expiresIn: process.env.JWT_EXPIRES_IN,
issuer: \'sua-api\',
audience: \'sua-aplicacao\'
});
};
const register = async (req, res) => {
try {
const { name, email, password } = req.body;
// Validações básicas
if (!name || !email || !password) {
return res.status(400).json({
error: \'Nome, email e senha são obrigatórios\'
});
}
if (password.length < 6) {
return res.status(400).json({
error: \'Senha deve ter no mínimo 6 caracteres\'
});
}
// Verificar se usuário já existe
const existingUser = users.find(user => user.email === email);
if (existingUser) {
return res.status(409).json({
error: \'Usuário já cadastrado com este email\'
});
}
// Hash da senha
const saltRounds = parseInt(process.env.BCRYPT_ROUNDS);
const hashedPassword = await bcrypt.hash(password, saltRounds);
// Criar usuário
const newUser = {
id: users.length + 1,
name,
email: email.toLowerCase(),
password: hashedPassword,
createdAt: new Date().toISOString()
};
users.push(newUser);
// Remover senha da resposta
const { password: _, ...userResponse } = newUser;
res.status(201).json({
message: \'Usuário registrado com sucesso\',
user: userResponse
});
} catch (error) {
console.error(\'Erro no registro:\', error);
res.status(500).json({
error: \'Erro interno do servidor\'
});
}
};
const login = async (req, res) => {
try {
const { email, password } = req.body;
if (!email || !password) {
return res.status(400).json({
error: \'Email e senha são obrigatórios\'
});
}
// Buscar usuário
const user = users.find(u => u.email === email.toLowerCase());
if (!user) {
return res.status(401).json({
error: \'Credenciais inválidas\'
});
}
// Verificar senha
const validPassword = await bcrypt.compare(password, user.password);
if (!validPassword) {
return res.status(401).json({
error: \'Credenciais inválidas\'
});
}
// Gerar token
const token = generateToken({
id: user.id,
email: user.email,
name: user.name
});
res.json({
message: \'Login realizado com sucesso\',
token,
user: {
id: user.id,
name: user.name,
email: user.email
}
});
} catch (error) {
console.error(\'Erro no login:\', error);
res.status(500).json({
error: \'Erro interno do servidor\'
});
}
};
const profile = (req, res) => {
// req.user vem do middleware de autenticação
res.json({
message: \'Perfil do usuário\',
user: req.user
});
};
module.exports = {
register,
login,
profile
};
Definindo as Rotas de Autenticação
// src/routes/authRoutes.js
const express = require(\'express\');
const { register, login, profile } = require(\'../controllers/authController\');
const { authenticateToken } = require(\'../middleware/authMiddleware\');
const router = express.Router();
// Rotas públicas
router.post(\'/register\', register);
router.post(\'/login\', login);
// Rotas protegidas
router.get(\'/profile\', authenticateToken, profile);
module.exports = router;
Testando a API
Para testar sua API, você pode usar ferramentas como Postman ou curl:
Registrar usuário
curl -X POST http://localhost:3000/api/auth/register \\
-H "Content-Type: application/json" \\
-d \'{"name":"João Silva","email":"joao@email.com","password":"123456"}\'
Fazer login
curl -X POST http://localhost:3000/api/auth/login \\
-H "Content-Type: application/json" \\
-d \'{"email":"joao@email.com","password":"123456"}\'
Acessar perfil (substituir TOKEN pelo JWT recebido)
curl -X GET http://localhost:3000/api/auth/profile \\
-H "Authorization: Bearer TOKEN"
Boas Práticas de Segurança
Fortalecendo a Segurança JWT
- Secrets robustos: Use chaves de pelo menos 256 bits
- Tempo de expiração: Configure tokens com vida útil curta (15-30 minutos)
- Refresh tokens: Implemente sistema de renovação automática
- Blacklist: Mantenha lista de tokens revogados
- HTTPS obrigatório: Nunca transmita tokens via HTTP
Para aplicações que requerem alta disponibilidade e escalabilidade, considere utilizar servidores VPS dedicados que oferecem melhor performance e controle sobre o ambiente.
Implementação de Refresh Tokens
Para melhorar a segurança, implemente um sistema de refresh tokens:
// Exemplo de implementação de refresh token
const generateRefreshToken = () => {
return jwt.sign({}, process.env.JWT_REFRESH_SECRET, {
expiresIn: \'7d\'
});
};
const refreshToken = (req, res) => {
const { refreshToken } = req.body;
if (!refreshToken) {
return res.status(401).json({ error: \'Refresh token requerido\' });
}
try {
jwt.verify(refreshToken, process.env.JWT_REFRESH_SECRET);
// Gerar novos tokens
const newToken = generateToken({ id: user.id, email: user.email });
const newRefreshToken = generateRefreshToken();
res.json({
token: newToken,
refreshToken: newRefreshToken
});
} catch (error) {
res.status(403).json({ error: \'Refresh token inválido\' });
}
};
Monitoramento e Logs
Implemente logging para monitorar tentativas de autenticação:
const logAuthAttempt = (email, success, ip) => {
console.log(JSON.stringify({
timestamp: new Date().toISOString(),
event: \'auth_attempt\',
email,
success,
ip,
userAgent: req.headers[\'user-agent\']
}));
};
Este sistema de autenticação JWT fornece uma base sólida para APIs REST seguras. Lembre-se de sempre validar entradas, usar HTTPS em produção e manter dependências atualizadas.
Comentários
0Inicie sessão para deixar um comentário
Iniciar sessãoSé el primero en comentar