Pular para conteúdo

0005 — Auth in-app em 3 camadas + telas de auditoria

Status: Aprovado · Responsável: Gustavo Madruga · Atualizado em: 2026-07-28 · Decidido em: 2026-07-28

Atualização (08/2026, decisão 0006): o motor de auth descrito aqui deixou de ser cópia local e passou a vir da lib br.com.xadm:xadm-seguranca (pacote br.com.xadm.comum.seguranca). O modelo de 3 camadas segue igual; só mudou a origem do código. A policy de rota app-específica (whitelist, /webhook/, views públicas por UUID) vive agora em security/ViewWhitelist (local). O issuer do cookie virou config (AUTH_ISSUER).

Contexto

O tradutor rodava com micronaut-security desligado: as telas de auditoria ficavam atrás do middleware do Coolify e os endpoints de máquina usavam filtros Bearer dedicados (ProdutoAuthFilter, WebhookAuthFilter). A operação precisava de três níveis de acesso: API de máquina, área admin (reprocesso/processamentos) e uma tela pública para a equipe Thoms/WebStorm ver a auditoria sem login. Com o Coolify na frente de tudo não dá para abrir um path anônimo — a tela pública exige auth in-app.

Decisão

Portar o padrão de segurança in-app do onpetro (bi-comercial-xls) e ligar o micronaut-security com tri-estado (ViewSecurityRule): enabled (envs AUTH_* presentes) exige login; bypass em dev/test; fail-closed (503) em prod sem envs. Três camadas:

Camada Rotas Mecanismo
API (máquina) /api/csv/processar, /api/integrador/produto (+ alias /webhook/estoque-mudou) @Secured("ROLE_API") + StaticBearerTokenValidator (Bearer API_BEARER_TOKEN)
Pública (anônima) / e /{id} (payloads/saída, valores no detalhe) ViewSecurityRule retorna ALLOWED direto (ViewWhitelist.isPublicView)
Admin (humano) /admin, /admin/{id}, /admin/produto/{cod}/reprocessar login Google @xadm.com.br (Firebase xadm-6ab81, cookie xadm_session); reprocesso com CSRF
  • Um API_BEARER_TOKEN compartilhado consolida os dois Bearers de entrada antigos (PRODUTO_TOKEN do X-Adm + WEBHOOK_TOKEN do integrador).
  • Convenção nova: todo callback integrador→app fica sob /api/integrador/<recurso> (este poke = /api/integrador/produto). O /webhook/estoque-mudou vira alias temporário até o integrador migrar (o poke não cai no deploy; o sweep é a rede).

Consequências

  • Telas saem de trás do Coolify (auth é do app); em prod, sem AUTH_* → 503 (fail-closed), por isso o deploy tem que setar os AUTH_*.
  • O integrador tem que mandar o API_BEARER_TOKEN (não mais WEBHOOK_TOKEN) e migrar o path do poke — coordenação cross-repo, coberta pelo alias.
  • A tela pública expõe preço/estoque completos (decisão do usuário; dado da Thoms).
  • Auditoria em duas trilhas sem join por lote_id: / (payloads/saída) × /admin (processamentos/entrada, webstorm_ecom_ingestao) — a saída é async por produto/versão.

Alternativas descartadas

  • Manter só o Coolify na frente — zero código, mas impede a tela pública anônima (tudo ficaria atrás do guard).
  • Híbrido Coolify + rota anônima — dois modelos de auth convivendo + config de proxy fina; frágil.
  • Dois tokens de API (emissores separados) — mais higiênico, mas o usuário optou por um token só (simples, igual onpetro).
  • Pattern greedy no intercept-url-map (/* isAnonymous) para o detalhe público — exporia o /admin (o intercept-url-map, ordem -100, ganha da ViewSecurityRule). Por isso a ViewSecurityRule retorna ALLOWED direto para / e /{uuid}.

Linhagem de trabalho: .ia/006-auth-camadas-telas-auditoria-*.