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

4.4 KiB
Raw Blame History

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.