Files
2026-08-14 17:35:24 -03:00

116 lines
5.5 KiB
Markdown

# Etapa 2/8 — Autenticação, Autorização e Row Level Security
## Contexto do projeto
Continuação do **SeConectai Vendas** (CRM/vendas multi-tenant). A Etapa 1
já entregou: projeto NestJS + Docker Compose + Postgres/Prisma com o
schema completo migrado (incluindo as tabelas `master_admins`,
`tenant_users`, `auth_refresh_tokens`, `password_reset_tokens`,
`login_attempts` e as policies de RLS).
Existem dois tipos de usuário:
- **master_admins**: você, dono da plataforma — acesso a todos os tenants.
- **tenant_users**: usuários de cada tenant (cliente seu), com papéis
(`tenant_admin`, `vendedor`, `financeiro`, `visualizador`), restritos
aos próprios dados via RLS.
## Objetivo desta etapa
Implementar login, emissão/renovação de tokens, reset de senha,
mecanismo de definição do `tenant_id` da sessão no Postgres (RLS) e
proteção contra força bruta — para ambos os tipos de usuário.
## Tarefas
1. **Módulo de autenticação (`src/modules/auth/`)**
- `POST /auth/master/login` — email + senha → valida contra
`master_admins.senha_hash` (argon2id) → retorna access token (JWT,
expiração curta, ex: 15 min) + refresh token (opaco, guardado como
hash em `auth_refresh_tokens`, expiração longa, ex: 30 dias).
- `POST /auth/tenant/login` — email + senha + (implícito) tenant
resolvido pelo email único dentro do tenant → mesma lógica, usando
`tenant_users`.
- `POST /auth/refresh` — recebe refresh token, valida hash e
expiração/revogação, **rotaciona** (invalida o antigo, emite um
novo) e retorna novo par de tokens.
- `POST /auth/logout` — revoga o refresh token atual.
- `POST /auth/password/forgot` — gera token em
`password_reset_tokens` (hash, expiração curta, ex: 1h) e dispara
e-mail (por ora, apenas logar o link/token no console — o envio real
de e-mail é de uma etapa futura).
- `POST /auth/password/reset` — valida token, atualiza `senha_hash`,
marca token como usado, revoga todos os refresh tokens ativos do
usuário.
2. **JWT payload**
- Incluir no access token: `sub` (id do usuário), `tipo`
(`master`|`tenant_user`), `tenant_id` (null para master), `role`
(para tenant_user).
- Implementar `JwtStrategy` (Passport) validando assinatura e
expiração.
3. **Guards e decorators**
- `@CurrentUser()` — decorator para extrair o usuário autenticado do
request.
- `MasterGuard` — permite apenas `tipo === 'master'`.
- `TenantGuard` — permite apenas `tipo === 'tenant_user'`, e injeta
`tenant_id` no contexto da request.
- `RolesGuard` + `@Roles('tenant_admin', ...)` — restringe endpoints
por papel dentro do tenant.
4. **Aplicação do RLS via Prisma**
- Implementar um mecanismo (middleware/interceptor do Nest, executado
por request) que, para requests autenticadas como `tenant_user`,
abre uma transação Prisma e executa
`SET LOCAL app.current_tenant_id = '<tenant_id>'` antes de qualquer
query — garantindo que a policy de RLS do Postgres filtre os dados
automaticamente.
- Para requests como `master`, usar uma conexão/role de banco com
`BYPASS ROW LEVEL SECURITY` (criar essa role na migration, se ainda
não existir, com `CREATE ROLE app_master BYPASSRLS LOGIN ...` —
documentar isso no `.env.example` como uma segunda connection string
opcional, `DATABASE_URL_MASTER`).
- Escrever um teste de integração que prove o isolamento: criar dois
tenants, um usuário em cada, confirmar que a query de um nunca
retorna dado do outro mesmo manipulando o `tenant_id` de outras
formas (ex: tentando passar um `tenant_id` diferente no body de uma
requisição — o `tenant_id` da sessão deve **sempre** vir do token,
nunca de input do usuário).
5. **Proteção contra força bruta**
- Toda tentativa de login (sucesso ou falha) grava em
`login_attempts`.
- Após N tentativas falhas (configurável, ex: 5) para o mesmo e-mail
em uma janela de tempo (ex: 15 min), bloquear novas tentativas com
429 e mensagem clara, mesmo que a senha esteja correta.
- Rate limiting geral no endpoint de login (ex: `@nestjs/throttler`).
6. **Segurança de senha**
- Hash com argon2id (biblioteca `argon2`), parâmetros de custo
documentados no código.
- Validação de força mínima da senha no cadastro/reset (tamanho
mínimo, não permitir senhas triviais).
- Refresh tokens e reset tokens **nunca** armazenados em texto puro —
sempre hash (ex: sha256) antes de gravar no banco.
## Critérios de aceite
- [ ] Login master e login tenant funcionam e retornam access+refresh
token válidos
- [ ] Refresh token rotaciona corretamente; token antigo usado após
rotação é rejeitado
- [ ] Logout revoga o refresh token
- [ ] Fluxo de esqueci minha senha gera token, valida expiração/uso único
- [ ] `MasterGuard` e `TenantGuard` bloqueiam corretamente rotas erradas
(testar com token de tenant tentando acessar rota master e
vice-versa)
- [ ] `RolesGuard` bloqueia usuário `vendedor` tentando acessar rota
restrita a `tenant_admin`
- [ ] Teste de integração prova isolamento entre tenants via RLS (dado de
um tenant nunca vaza para outro, mesmo forçando `tenant_id` no
payload da requisição)
- [ ] Após 5 tentativas de login falhas, novas tentativas são bloqueadas
temporariamente
- [ ] Todas as senhas no banco estão como hash argon2id (nenhuma em texto
puro em nenhuma tabela)