Files
Condominio_SeConectai/Schemas/04-auth-jwt-apikey.md
T
2026-09-29 12:12:31 -03:00

61 lines
4.4 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Prompt 04 — Autenticação: senha master, login de funcionário (JWT) e API Key
## Contexto
Prompts 01–03 concluídos: app no ar, `db_master` e `db_empresa` com schema completo, DAOs básicos, provisionamento de tenant funcionando. **Leia `10-tracker-progresso.md` antes de começar.**
Este prompt implementa as 3 camadas de autenticação da arquitetura v3.0: master (senha única), funcionário do escritório (JWT), app externo (API Key).
## Objetivo
Implementar geração/validação de JWT (sem lib pesada — implementar HMAC-SHA256 manualmente ou usar uma lib mínima tipo `java-jwt`/`jjwt`, only se realmente necessário; preferir implementação própria já que o projeto é "Java puro"), hashing de senha (BCrypt — aqui vale trazer uma lib pequena e específica, tipo `at.favre.lib:bcrypt`, já que reimplementar bcrypt do zero não compensa), geração/validação de API Key, e os middlewares de autenticação usados pelos handlers HTTP dos próximos prompts.
## Especificação
### 1. Hash de senha
- Usar BCrypt (biblioteca `at.favre.lib:bcrypt`, única dependência externa além do driver JDBC e (se precisar) da lib de JWT).
- `PasswordHasher.hash(senha)` e `PasswordHasher.verificar(senha, hash)`.
### 2. JWT
- `JwtService.gerar(Map<String,Object> claims, long validadeSegundos)` → string do token.
- `JwtService.validar(String token)` → `Optional<Map<String,Object>> claims` (vazio se inválido/expirado).
- Claims para o token **master**: `{"escopo": "master"}`.
- Claims para o token de **funcionário**: `{"escopo": "empresa", "empresa_id": X, "funcionario_id": Y, "cargo": "..."}`.
- Assinatura HMAC-SHA256 usando `JWT_SECRET` (variável de ambiente já lida no Prompt 01).
- Validade sugerida: 8 horas para funcionário, 2 horas para master (reautenticação frequente por segurança, já que é a chave mestra do sistema).
### 3. API Key
- Geração: `ApiKeyService.gerar()` → retorna a chave em texto puro (mostrada **uma única vez** ao usuário) + o hash (SHA-256 é suficiente aqui, não precisa de bcrypt) que é salvo em `api_keys.key_hash`.
- Validação: `ApiKeyService.validar(String chaveRecebida)` → busca por hash no banco master, confere `ativa=true`, retorna o `empresa_id` vinculado (ou vazio se inválida/revogada).
### 4. Endpoints
```
POST /api/master/login body: {"senha": "..."}
→ confere hash contra MASTER_PASSWORD_HASH (env var); se ok, retorna {"token": "..."}
POST /api/auth/login body: {"email": "...", "senha": "..."}
→ busca funcionario por email (em qual empresa? funcionarios.email é único globalmente,
então a busca não precisa de empresa_id no body), confere senha, gera JWT de empresa
→ se funcionarios.status != 'ativo' OU empresas.status != 'ativa': erro 403 com mensagem clara
("cadastro desativado" ou "escritório com pendência financeira", respectivamente)
```
### 5. Middlewares (para uso nos próximos prompts)
Criar filtros/handlers wrapper reutilizáveis:
- `exigirMaster(HttpHandler)` — lê header `Authorization: Bearer <token>`, valida escopo=master, senão retorna 401.
- `exigirFuncionario(HttpHandler)` — idem, escopo=empresa; injeta `empresa_id`/`funcionario_id`/`cargo` no contexto da requisição (pode ser via `HttpExchange.setAttribute` ou um `ThreadLocal` simples).
- `exigirApiKey(HttpHandler)` — lê header `X-API-Key`, valida, injeta `empresa_id` no contexto.
### 6. Segurança básica
- Nunca logar senha, token completo ou API key em texto puro nos logs (logar no máximo os primeiros 6 caracteres + "...").
- Rate limiting simples no `/api/master/login` e `/api/auth/login` (ex: máximo 5 tentativas por IP a cada 5 minutos, em memória — não precisa de Redis nesta fase).
## Critérios de aceite
- [ ] `POST /api/master/login` com senha errada retorna 401; com senha certa retorna um JWT válido.
- [ ] Token master usado em `exigirFuncionario` é rejeitado (escopo errado) — e vice-versa.
- [ ] `POST /api/auth/login` com funcionário de empresa bloqueada (`empresas.status='bloqueada'`) retorna 403 com mensagem específica de pendência financeira.
- [ ] API Key gerada, salva com hash, validada corretamente; uma chave revogada (`ativa=false`) é rejeitada.
- [ ] 6ª tentativa de login em 5 minutos do mesmo IP é bloqueada temporariamente.
- [ ] Nenhum log do sistema imprime senha, token completo ou API key completa.
## Ao concluir
Atualizar `10-tracker-progresso.md`: Prompt 04 concluído, data, resumo. Confirmar Prompts 01–03 seguem marcados.