API REST focada em autenticação segura, RBAC e boas práticas de backend.
Projeto desenvolvido para demonstrar arquitetura, testes e padrões usados em ambientes corporativos.
Este projeto foi criado para demonstrar:
- Implementação correta de JWT + Refresh Token
- Controle de acesso por roles (RBAC)
- Arquitetura backend escalável
- Testes automatizados reais
- Padrões usados em APIs corporativas
- Características
- Stack Tecnológica
- Arquitetura
- Instalação
- Configuração
- Banco de Dados
- Endpoints
- Autenticação
- Autorização
- Exemplos de Uso
- Estrutura do Projeto
- Decisões Técnicas
- ✅ JWT com Refresh Token (Access 15min + Refresh 7d, rotação e revogação do anterior)
- ✅ Logout com Blacklist (tokens revogados invalidados imediatamente)
- ✅ RBAC por role (ADMIN/USER/MANAGER) e endpoint de gerenciamento de roles
- ✅ Middleware requireRoles para controle de acesso
- ✅ Swagger/OpenAPI em
/api-docs - ✅ Logging estruturado (Winston)
- ✅ Auditoria (tabela de audit logs)
- ✅ Validações com Zod e respostas HTTP padronizadas
- ✅ Arquitetura em camadas (Routes → Controllers → Services → Repositories)
- ✅ Testes automatizados (Jest + Supertest) cobrindo login, RBAC, refresh token e logout
📘 Detalhes Técnicos Avançados
- Tokens usam
jwtidpara garantir unicidade e rotação real de refresh tokens - Blacklist em memória (sugestão: Redis em produção)
- Auditoria armazenada em
audit_logs - Swagger configurado via
swagger-jsdoce servido em/api-docs - Logs estruturados com Winston (console em dev + arquivos)
- Validação de entrada com Zod e helpers padronizados de resposta
- Middleware de autenticação carrega roles e permissões reais do banco
- Estrutura de serviços e repositórios separa regras de negócio de acesso a dados
- Runtime: Node.js
- Framework: Express.js
- Linguagem: TypeScript
- Banco de Dados: MySQL
- Autenticação: JWT (JSON Web Token)
- Hash de Senhas: Bcrypt
- Gerenciamento de Variáveis: dotenv
A API segue uma arquitetura em camadas bem definida:
┌─────────────────────────────────────┐
│ Routes (Rotas) │ ← Definição de endpoints
├─────────────────────────────────────┤
│ Controllers (Controladores) │ ← Lógica de requisições HTTP
├─────────────────────────────────────┤
│ Services (Serviços) │ ← Lógica de negócio
├─────────────────────────────────────┤
│ Repositories (Repositórios) │ ← Acesso a dados
├─────────────────────────────────────┤
│ Database (MySQL) │ ← Persistência
└─────────────────────────────────────┘
- Routes: Define os endpoints e aplica middlewares de autenticação/autorização
- Controllers: Processa requisições HTTP, valida dados e chama services
- Services: Contém a lógica de negócio e validações
- Repositories: Abstrai o acesso ao banco de dados
- Middlewares: Autenticação, autorização e tratamento de erros
- Utils: Funções utilitárias (JWT, Bcrypt)
- Node.js (v18 ou superior)
- MySQL Server (v8.0 ou superior)
- npm ou yarn
- Clone o repositório:
git clone <url-do-repositorio>
cd projeto-apirest- Instale as dependências:
npm install- Configure as variáveis de ambiente:
# Copie o arquivo .env.example para .env
cp .env.example .env
# Edite o arquivo .env com suas configurações- Configure o banco de dados:
# Execute o schema SQL no MySQL Workbench ou via linha de comando
mysql -u root -p < database/schema.sql
# Execute o seed para popular dados iniciais
mysql -u root -p < database/seed.sql- Compile o TypeScript:
npm run build- Inicie o servidor:
# Modo desenvolvimento (com hot-reload)
npm run dev
# Modo produção
npm start# Servidor
PORT=3000
NODE_ENV=development
# Banco de Dados MySQL
DB_HOST=localhost
DB_PORT=3306
DB_USER=root
DB_PASSWORD=sua_senha_aqui
DB_NAME=api_auth_rbac
# JWT
JWT_SECRET=seu_jwt_secret_super_seguro_aqui_mude_em_producao
JWT_EXPIRES_IN=24h
# Segurança
BCRYPT_ROUNDS=10JWT_SECRET para um valor seguro e aleatório!
O banco de dados possui as seguintes tabelas:
- users: Usuários do sistema
- roles: Papéis (ADMIN, MANAGER, USER)
- permissions: Permissões granulares (USER_CREATE, USER_READ, etc.)
- user_roles: Relacionamento N:N entre usuários e roles
- role_permissions: Relacionamento N:N entre roles e permissões
- audit_logs: Logs de auditoria para ações sensíveis
- ADMIN: Acesso total a todas as funcionalidades
- MANAGER: Permissões de gerenciamento (sem delete)
- USER: Permissões básicas de leitura
USER_CREATE,USER_READ,USER_UPDATE,USER_DELETEROLE_CREATE,ROLE_READ,ROLE_UPDATE,ROLE_DELETEPERMISSION_CREATE,PERMISSION_READ,PERMISSION_UPDATE,PERMISSION_DELETEAUDIT_READ
Registra um novo usuário.
Request Body:
{
"username": "johndoe",
"email": "john@example.com",
"password": "senha123",
"fullName": "John Doe"
}Response (201):
{
"success": true,
"message": "Usuário registrado com sucesso",
"data": {
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"user": {
"id": 1,
"username": "johndoe",
"email": "john@example.com",
"fullName": "John Doe",
"roles": []
}
}
}Autentica um usuário e retorna token JWT.
Request Body:
{
"username": "johndoe",
"password": "senha123"
}Response (200):
{
"success": true,
"message": "Login realizado com sucesso",
"data": {
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"user": {
"id": 1,
"username": "johndoe",
"email": "john@example.com",
"fullName": "John Doe",
"roles": ["USER"]
}
}
}Retorna informações do usuário autenticado.
Headers:
Authorization: Bearer <token>
Response (200):
{
"success": true,
"message": "Dados do usuário recuperados com sucesso",
"data": {
"id": 1,
"username": "johndoe",
"email": "john@example.com",
"fullName": "John Doe",
"roles": ["USER"],
"isActive": true,
"createdAt": "2024-01-01T00:00:00.000Z",
"updatedAt": "2024-01-01T00:00:00.000Z"
}
}Renova Access Token usando Refresh Token (com rotação).
Request Body:
{
"refreshToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
}Response (200):
{
"success": true,
"message": "Token renovado com sucesso",
"data": {
"accessToken": "novo_access_token",
"refreshToken": "novo_refresh_token"
}
}Nota: O refresh token antigo é automaticamente revogado após a rotação.
Realiza logout do usuário (revoga tokens).
Headers:
Authorization: Bearer <access_token>
Request Body (opcional):
{
"refreshToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"revokeAll": false
}Response (200):
{
"success": true,
"message": "Logout realizado com sucesso",
"data": {
"message": "Logout realizado com sucesso"
}
}Parâmetros:
refreshToken(opcional): Revoga o refresh token fornecidorevokeAll(opcional, padrão:false): Setrue, revoga todos os tokens do usuário
Lista todos os usuários (requer permissão USER_READ).
Headers:
Authorization: Bearer <token>
Query Parameters:
limit(opcional): Número de resultados (padrão: 50)offset(opcional): Offset para paginação (padrão: 0)
Busca um usuário por ID (requer permissão USER_READ).
Cria um novo usuário (requer permissão USER_CREATE).
Request Body:
{
"username": "janedoe",
"email": "jane@example.com",
"password": "senha123",
"fullName": "Jane Doe",
"roleIds": [1, 2]
}Atualiza um usuário (requer permissão USER_UPDATE).
Request Body:
{
"email": "newemail@example.com",
"fullName": "Jane Smith",
"isActive": true,
"roleIds": [2]
}Deleta um usuário (requer permissão USER_DELETE).
Query Parameters:
hard(opcional): Setrue, faz hard delete (padrão:false- soft delete)
A autenticação é feita via JWT (JSON Web Token) com estratégia de Access Token + Refresh Token.
- Access Token: Válido por 15 minutos (curto, para segurança)
- Refresh Token: Válido por 7 dias (longo, para conveniência)
- Login: Cliente recebe
accessTokenerefreshToken - Uso: Cliente usa
accessTokenem todas as requisições - Renovação: Quando
accessTokenexpira, cliente usarefreshTokenpara obter novo par - Rotação: Refresh token antigo é automaticamente revogado após renovação
- Logout: Tokens são adicionados à blacklist e invalidados
Após o login, o access token deve ser enviado no header Authorization:
Authorization: Bearer <access_token>
O Access Token JWT contém:
userId: ID do usuáriousername: Nome de usuárioemail: Email do usuárioroles: Array de roles do usuárioiat: Data de emissãoexp: Data de expiraçãoiss: Emissor (issuer)aud: Audiência (audience)
Tokens revogados (logout) são adicionados à blacklist em memória. Em produção, recomenda-se migrar para Redis para:
- Persistência entre reinicializações
- Compartilhamento entre múltiplas instâncias
- TTL automático
A autorização funciona em dois níveis:
Verifica se o usuário possui uma das roles necessárias:
// Exemplo: Apenas ADMIN
router.get('/admin', authenticate, requireRoles('ADMIN'), controller);
// Exemplo: ADMIN ou MANAGER
router.get('/admin-or-manager', authenticate, requireRoles('ADMIN', 'MANAGER'), controller);Middleware: requireRoles(...roles: string[])
- Retorna 401 se não autenticado
- Retorna 403 se autenticado sem role necessária
- Logs estruturados de acesso negado
Verifica se o usuário possui uma permissão específica:
// Exemplo: Requer permissão USER_CREATE
router.post('/users', authenticate, authorizePermission('USER_CREATE'), controller);Middleware: authorizePermission(permission: string)
- Retorna 401 se não autenticado
- Retorna 403 se autenticado sem permissão
- 401 Unauthorized: Token ausente, inválido ou expirado
- 403 Forbidden: Usuário autenticado, mas sem permissão suficiente
curl -X POST http://localhost:3000/api/auth/register \
-H "Content-Type: application/json" \
-d '{
"username": "johndoe",
"email": "john@example.com",
"password": "senha123",
"fullName": "John Doe"
}'curl -X POST http://localhost:3000/api/auth/login \
-H "Content-Type: application/json" \
-d '{
"username": "johndoe",
"password": "senha123"
}'curl -X GET http://localhost:3000/api/users \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."curl -X POST http://localhost:3000/api/users \
-H "Authorization: Bearer <token>" \
-H "Content-Type: application/json" \
-d '{
"username": "janedoe",
"email": "jane@example.com",
"password": "senha123",
"fullName": "Jane Doe",
"roleIds": [1]
}'projeto-apirest/
├── src/
│ ├── config/
│ │ └── database.ts # Configuração do banco de dados
│ ├── controllers/
│ │ ├── auth.controller.ts # Controllers de autenticação
│ │ └── user.controller.ts # Controllers de usuários
│ ├── middlewares/
│ │ ├── auth.middleware.ts # Middlewares de auth/autorização
│ │ └── error.middleware.ts # Tratamento de erros
│ ├── repositories/
│ │ ├── user.repository.ts # Acesso a dados de usuários
│ │ ├── role.repository.ts # Acesso a dados de roles
│ │ ├── permission.repository.ts
│ │ └── audit.repository.ts # Logs de auditoria
│ ├── routes/
│ │ ├── auth.routes.ts # Rotas de autenticação
│ │ └── user.routes.ts # Rotas de usuários
│ ├── services/
│ │ ├── auth.service.ts # Lógica de negócio de auth
│ │ └── user.service.ts # Lógica de negócio de usuários
│ ├── types/
│ │ └── index.ts # Definições TypeScript
│ ├── utils/
│ │ ├── jwt.util.ts # Utilitários JWT
│ │ └── bcrypt.util.ts # Utilitários Bcrypt
│ ├── app.ts # Configuração do Express
│ └── server.ts # Inicialização do servidor
├── database/
│ ├── schema.sql # Schema do banco de dados
│ └── seed.sql # Dados iniciais
├── .env.example # Exemplo de variáveis de ambiente
├── .gitignore
├── package.json
├── tsconfig.json
└── README.md
Decisão: Separar responsabilidades em camadas bem definidas.
Benefícios:
- Facilita manutenção e testes
- Permite reutilização de código
- Melhora a organização do projeto
Decisão: Usar TypeScript em vez de JavaScript puro.
Benefícios:
- Tipagem estática reduz erros
- Melhor autocompletar e IntelliSense
- Código mais seguro e manutenível
Decisão: Usar pool de conexões em vez de conexões únicas.
Benefícios:
- Melhor performance
- Gerenciamento eficiente de recursos
- Suporta múltiplas requisições simultâneas
Decisão: Usar JWT em vez de sessões.
Benefícios:
- Stateless (não requer armazenamento no servidor)
- Escalável
- Padrão da indústria
Decisão: Usar bcrypt com 10 rounds.
Benefícios:
- Algoritmo seguro e amplamente usado
- Resistente a ataques de força bruta
- Configurável via variável de ambiente
Decisão: Implementar tabela de audit_logs.
Benefícios:
- Rastreabilidade de ações sensíveis
- Compliance e segurança
- Facilita debugging
Decisão: Middleware único para tratamento de erros.
Benefícios:
- Respostas padronizadas
- Fácil manutenção
- Logs consistentes
Decisão: Implementar soft delete por padrão.
Benefícios:
- Preserva histórico de dados
- Permite recuperação
- Melhor para auditoria
Solução: Verifique se o arquivo .env existe e se DB_PASSWORD está configurado corretamente.
Solução: Verifique se o MySQL está rodando. No Windows, execute:
Get-Service -Name "*MySQL*"
Start-Service -Name "MySQL80"Solução: Execute o arquivo database/schema.sql no MySQL Workbench para criar o banco.
Solução: Execute o script para corrigir:
npm run fix-admin-passwordSolução: Instale as dependências:
npm installOpção 1: Via Script
npm run create-adminOpção 2: Via MySQL Workbench
Execute database/create-admin.sql e ajuste a senha conforme necessário.
Opção 3: Via API (após ter um token com permissão USER_CREATE)
POST /api/users
Authorization: Bearer <token>
Body: {
"username": "admin",
"email": "admin@example.com",
"password": "Admin@123",
"fullName": "Administrador",
"roleIds": [1]
}- Faça login com o usuário admin:
POST /api/auth/login
Body: {
"username": "admin",
"password": "Admin@123"
}- Copie o token da resposta e use no header:
Authorization: Bearer <token>
A API inclui testes automatizados usando Jest e Supertest.
# Executar todos os testes
npm test
# Executar em modo watch
npm run test:watch
# Executar com cobertura
npm run test:coverage- ✅ Login: Credenciais válidas, inválidas, usuário inexistente
- ✅ Autenticação: Token válido, inválido, expirado, sem token
- ✅ RBAC: ADMIN acessa, USER não acessa, sem autenticação
- ✅ Refresh Token: Renovação, rotação, token inválido
- ✅ Logout: Logout bem-sucedido, token invalidado após logout
tests/
├── setup.ts # Configuração global
├── auth.test.ts # Testes de autenticação
├── rbac.test.ts # Testes de RBAC
└── refresh-token.test.ts # Testes de refresh token
- Validação com Zod
- Tokens Access + Refresh com rotação (refresh antigo revogado)
- Logout com blacklist de tokens
- RBAC com middleware
requireRolese endpoint de gerenciamento de roles - Swagger/OpenAPI disponível em
/api-docs - Logging estruturado (Winston)
- Testes automatizados (Jest + Supertest) cobrindo login, RBAC, refresh token e logout
- Respostas HTTP padronizadas (helpers)
Este projeto é open source e está disponível para uso educacional e profissional.
Desenvolvido como projeto de portfólio profissional.
⭐ Se este projeto foi útil, considere dar uma estrela no repositório!