# MFA obrigatorio nas cabines PIX e SPB (2026-07-24)

> Mandato do dono: revalidacao full do MFA nas cabines PIX e SPB. Habilitar para
> TODOS os usuarios exceto `admin@monetarie.com`. Enforcement no login (criar MFA
> no 1o ou proximo login). Step-up MFA em TODA operacao manual de valor via front.
> Decisoes fechadas: (1) step-up = TOTP a CADA operacao; (2) rollout HML antes de PRD.

## Estado atual (provado, 24/07)

**PIX** (`pix/services/settlement_service` gateway -> `pix/apps/dict_service` auth authority):
- Auth por usuario (Bcrypt) em `monetarie_auth.users`; colunas `mfa_secret/mfa_enabled/mfa_backup_codes` ja existem.
- MFA TOTP + backup codes FUNCIONA (front `views/security/MfaSetupView.vue`, back `mfa_controller.ex`), mas e opt-in (`mfa_enabled` default false), so no login. Sem forcar, sem step-up.
- Precedente de forcar acao no login: `must_change_password` (router guard `frontend/admin/src/router/index.ts`).
- Money endpoints (JWT+CSRF, sem MFA): POST `/transactions`, `/transactions/:id/return`, `/messages/send`, `/balance/requests`, balance approve/reject/cancel, `/alcada/.../visto`, `/funds-recoveries`, MED refunds.

**SPB** (`spb/services/bacen_gateway`; front `spb/frontend-vue`):
- Auth por usuario (Bcrypt) em `users` (role/RBAC).
- MFA esqueleto QUEBRADO: `mfa_engine.ex` (TOTP) ok, mas `mfa_controller.ex` le `conn.assigns[:current_user]` que o `auth_plug.ex` NUNCA seta (so `:current_user_id`) -> setup no-op. Sem colunas `mfa_*` no `users` (segredo em `system_settings` KV). Admin `MFAConfigView.vue` e stub client-side. Nao roda no login. Sem enrollment view TOTP.
- Money endpoints (JWT only): POST `/messages/send`, `/messages/:id/approve`, `/operations/*/send`, `/devolutions`, `/substitutions`, `/payments`, `/transactions`, `/accounting/reverse/:id`, `/balances/positions/:id/force`.

## Design comum

- **Isencao**: `admin@monetarie.com(.br)` isento de forcar-criar E de step-up. Predicado por email (uma funcao `mfa_exempt?/1`).
- **Enforcement login**: apos senha ok, se usuario NAO isento e `mfa_enabled=false` -> resposta sinaliza `mfa_setup_required` (analogo a `must_change_password`); front leva para tela de enrollment TOTP e bloqueia o resto ate ativar. Se ja tem MFA -> desafio TOTP normal.
- **Step-up por operacao**: front adiciona campo "codigo do app (6 digitos)" no dialogo de confirmacao das acoes de valor; envia junto ao request (header `x-mfa-totp` ou campo no body). Backend: plug `RequireStepUpMfa` nos endpoints de dinheiro valida o TOTP do usuario naquele request (reusa `verify_totp/2`); isento passa direto. Falha -> 403 `MFA_STEP_UP_REQUIRED`/`MFA_INVALID`.
- TDD em cada peca. Commits locais por peca. Sem push/deploy sem OK.

## Fases

### Fase 1 — PIX (MFA ja funciona)
1. Backend enforcement login: `mfa_setup_required` no auth (dict_service) para nao-isento sem MFA. TDD.
2. Front PIX: guard leva `mfa_setup_required` para `security/mfa` (enrollment) e bloqueia navegacao ate ativar. TDD.
3. Backend plug `RequireStepUpMfa` + aplicar nos money endpoints (gateway repassa TOTP ao dict/valida). TDD.
4. Front PIX: dialogos de confirmacao das acoes de valor pedem TOTP e enviam no request. TDD.

### Fase 2 — SPB (consertar base primeiro)
0. Consertar plumbing: `mfa_controller` usar `:current_user_id`; migrar colunas `mfa_secret/mfa_enabled/mfa_backup_codes` para `users`; persistir de verdade. TDD.
1. Enrollment view TOTP (QR + codigo) no `spb/frontend-vue` (nao existe). 
2. Enforcement login (`mfa_setup_required`) analogo ao PIX. TDD.
3. Plug step-up + aplicar nos money endpoints. TDD.
4. Front: dialogos de valor pedem TOTP. TDD.

### Deploy (apos validar HML)
- PIX: `dict_service` + `settlement_service` (pix-api) + `pix-admin-ui`.
- SPB: `bacen_gateway` (spb-api) + `spb-admin-ui`.
- HML primeiro, validar vivo (criar MFA + step-up ponta a ponta), depois PRD.

## Guardas
- Nao quebrar login de quem ja tem MFA. `admin@monetarie.com` nunca travado.
- Step-up nao pode bloquear leitura (so acoes de valor).
- Dinheiro = teste de integracao real onde aplicavel.
