# 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 claims, long validadeSegundos)` → string do token. - `JwtService.validar(String token)` → `Optional> 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 `, 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.