Introdução
Proteger uma API vai muito além de adicionar um campo de senha a um formulário. Você precisa de um esquema em que o servidor possa confiar que a requisição realmente vem de quem ela afirma ser. Os JSON Web Tokens resolvem isso de forma elegante: o servidor assina um token no momento do login, e as requisições subsequentes carregam esse token como prova de identidade. Sem tabela de sessões, sem estado compartilhado.
O que é JWT?
JSON Web Token (JWT) é um padrão aberto (RFC 7519) para transmitir informações entre partes como um objeto JSON assinado. Por ser assinado — seja com uma chave secreta ou com um par de chaves pública/privada — o servidor consegue verificar que o token não foi adulterado.
Por que usar JWT?
JWTs são compactos o suficiente para caber em um cabeçalho HTTP. São autocontidos: o token carrega a identidade do usuário e quaisquer claims necessários, dispensando consulta ao banco de dados a cada requisição. E por não haver sessão no lado do servidor para manter, a autenticação baseada em JWT escala horizontalmente sem coordenação entre servidores.
Como funciona a Autenticação JWT
- O usuário envia as credenciais (usuário e senha).
- O servidor as valida e assina um JWT com uma chave secreta.
- O servidor retorna o token no corpo da resposta.
- O cliente armazena o token (localStorage ou cookie HTTP-only).
- Cada requisição subsequente inclui o token no cabeçalho
Authorization. - O servidor verifica a assinatura antes de processar a requisição.
Implementando Autenticação JWT em TypeScript
A seguir, uma implementação passo a passo em Node.js com TypeScript.
Pré-requisitos
Node.js instalado e familiaridade básica com TypeScript e Express.js.
Configurando o Projeto
-
Inicialize o projeto
mkdir jwt-auth-typescript cd jwt-auth-typescript npm init -y -
Instale as dependências
npm install express jsonwebtoken bcryptjs cors dotenv -
Instale TypeScript e os tipos
npm install --save-dev typescript @types/express @types/jsonwebtoken @types/bcryptjs @types/cors -
Inicialize a configuração do TypeScript
npx tsc --init
Estrutura do Projeto
jwt-auth-typescript/
├── src/
│ ├── index.ts
│ ├── routes/
│ │ └── auth.ts
│ ├── controllers/
│ │ └── authController.ts
│ ├── models/
│ └── user.ts
├── package.json
├── tsconfig.json
└── .env
Configurando o TypeScript (tsconfig.json)
Certifique-se de que o tsconfig.json inclui:
{
"compilerOptions": {
"target": "ES6",
"module": "commonjs",
"outDir": "./dist",
"rootDir": "./src",
"strict": true
}
}
Configurando as Variáveis de Ambiente
Crie um arquivo .env no diretório raiz:
PORT=5000
JWT_SECRET=your_jwt_secret_key
Criando o Servidor (index.ts)
import express from 'express';
import cors from 'cors';
import dotenv from 'dotenv';
import authRoutes from './routes/auth';
dotenv.config();
const app = express();
const PORT = process.env.PORT || 5000;
app.use(cors());
app.use(express.json());
app.use('/api/auth', authRoutes);
app.listen(PORT, () => {
console.log(`Server running on port ${PORT}`);
});
Criando o Modelo de Usuário (models/user.ts)
Em uma aplicação real você usaria um banco de dados. Para este exemplo, um array em memória é suficiente para demonstrar o fluxo de autenticação.
interface User {
id: number;
username: string;
password: string;
}
const users: User[] = [];
export default users;
export type { User };
Criando as Rotas de Autenticação (routes/auth.ts)
import { Router } from 'express';
import { register, login, protectedRoute } from '../controllers/authController';
const router = Router();
router.post('/register', register);
router.post('/login', login);
router.get('/protected', protectedRoute);
export default router;
Implementando o Controller (controllers/authController.ts)
import { Request, Response, NextFunction } from 'express';
import jwt from 'jsonwebtoken';
import bcrypt from 'bcryptjs';
import users, { User } from '../models/user';
const JWT_SECRET = process.env.JWT_SECRET || 'secret_key';
export const register = async (req: Request, res: Response) => {
const { username, password } = req.body;
// Check if user exists
const userExists = users.find((user) => user.username === username);
if (userExists) {
return res.status(400).json({ message: 'User already exists' });
}
// Hash password
const hashedPassword = await bcrypt.hash(password, 10);
// Create user
const newUser: User = {
id: users.length + 1,
username,
password: hashedPassword,
};
users.push(newUser);
res.status(201).json({ message: 'User registered successfully' });
};
export const login = async (req: Request, res: Response) => {
const { username, password } = req.body;
// Find user
const user = users.find((user) => user.username === username);
if (!user) {
return res.status(400).json({ message: 'Invalid credentials' });
}
// Check password
const isValid = await bcrypt.compare(password, user.password);
if (!isValid) {
return res.status(400).json({ message: 'Invalid credentials' });
}
// Sign token
const token = jwt.sign({ id: user.id, username: user.username }, JWT_SECRET, { expiresIn: '1h' });
res.json({ token });
};
export const protectedRoute = (req: Request, res: Response, next: NextFunction) => {
const authHeader = req.headers['authorization'];
const token = authHeader && authHeader.split(' ')[1];
if (!token) {
return res.sendStatus(401);
}
jwt.verify(token, JWT_SECRET, (err, user) => {
if (err) return res.sendStatus(403);
(req as any).user = user;
next();
});
};
Protegendo Rotas
Mova a lógica de verificação para um array de middleware para que possa ser reutilizada em diferentes rotas:
export const protectedRoute = [
(req: Request, res: Response, next: NextFunction) => {
// Middleware to verify JWT
const authHeader = req.headers['authorization'];
const token = authHeader && authHeader.split(' ')[1];
if (!token) {
return res.sendStatus(401);
}
jwt.verify(token, JWT_SECRET, (err, user) => {
if (err) return res.sendStatus(403);
(req as any).user = user;
next();
});
},
(req: Request, res: Response) => {
// Protected route logic
res.json({ message: 'This is a protected route', user: (req as any).user });
}
];
Testando a Aplicação
-
Compile o TypeScript
npx tsc -
Inicie o servidor
node dist/index.js -
Registre um usuário
POST http://localhost:5000/api/auth/register Content-Type: application/json { "username": "testuser", "password": "password123" } -
Faça login
POST http://localhost:5000/api/auth/login Content-Type: application/json { "username": "testuser", "password": "password123" }Você receberá um token JWT na resposta.
-
Acesse a rota protegida
GET http://localhost:5000/api/auth/protected Authorization: Bearer <your_jwt_token>Você deverá receber uma resposta indicando que o acesso foi concedido.
Considerações de Segurança
- Sempre use HTTPS em produção. Um JWT enviado por HTTP simples pode ser interceptado.
- Defina tempos de expiração curtos. Uma hora é razoável para tokens de acesso; use refresh tokens para sessões mais longas.
- Prefira armazenar tokens em cookies HTTP-only, não no localStorage. O localStorage é legível por qualquer JavaScript na página.
- Valide todas as entradas do usuário para prevenir ataques de injeção.
Conclusão
A autenticação JWT é uma solução prática para APIs sem estado. A implementação acima cobre registro, hash de senha com bcrypt, assinatura e verificação de tokens, e proteção de rotas como middleware. A partir dessa base, os próximos passos naturais são a rotação de refresh tokens e a migração do armazenamento em memória para um banco de dados real.
Leitura Complementar
Compartilhe sua Opinião
Se este conteúdo foi útil ou se você tiver dúvidas, entre em contato pelo Twitter.
Por Samuel Fajreldines, especialista em JavaScript, TypeScript, DevOps e Arquitetura Serverless.