# Parte 3 — Identidade, sessão, login, cadastro e segurança de acesso

## Rotas implementadas

### Públicas / autenticação

- `GET /login`
- `POST /login`
- `GET /entrar`
- `POST /entrar`
- `GET /login-tecnico`
- `POST /login-tecnico`
- `GET /entrar-tecnico`
- `POST /entrar-tecnico`
- `POST /logout`
- `POST /sair`
- `GET /cadastro`
- `POST /cadastro`
- `GET /mfa`
- `POST /mfa`
- `POST /mfa/reenviar`
- `POST /mfa/cancelar`
- `GET /verificacao-pendente`
- `POST /verificacao-pendente/reenviar`
- `GET /verificar-email?token=...`
- `GET /esqueci-senha`
- `POST /esqueci-senha`
- `GET /redefinir-senha?token=...`
- `POST /redefinir-senha`
- `GET /convites/aceitar?token=...`
- `POST /convites/aceitar`

### Área autenticada do portal (`/app`)

- `GET /app`
- `GET /app/selecionar-conta`
- `POST /app/selecionar-conta`
- `GET /app/sem-conta`
- `POST /app/contexto`
- `GET /app/catalogo`
- `GET /app/catalogo/produtos/{produtoId}/planos`
- `POST /app/catalogo/produtos/{produtoId}/cupom`
- `POST /app/catalogo/contratar`
- `GET /app/produtos`
- `GET /app/produtos-disponiveis`
- `GET /app/produtos/{produtoId}/acessar`
- `GET /app/produtos/{produtoId}/instancias/{instanciaId}/acessar`
- `GET /app/assinaturas`
- `GET /app/assinaturas/ativas`
- `GET /app/assinaturas/historico`
- `GET /app/trials`
- `POST /app/assinaturas/{subscriptionId}/cancelar`
- `POST /app/assinaturas/{subscriptionId}/reativar`
- `POST /app/assinaturas/itens/{itemId}/trocar-plano`
- `GET /app/faturas`
- `GET /app/faturamento`
- `GET /app/pagamentos`
- `GET /app/conta`
- `POST /app/conta`
- `GET /app/empresa`
- `POST /app/empresa`
- `GET /app/seguranca`
- `POST /app/seguranca/senha`
- `POST /app/seguranca/sessoes/outras/revogar`
- `POST /app/seguranca/sessoes/{sessionId}/revogar`
- `GET /app/configuracoes`
- `POST /app/configuracoes`
- `GET /app/equipe`
- `GET /app/convites`
- `POST /app/equipe/convites`
- `POST /app/equipe/convites/{invitationId}/revogar`
- `POST /app/equipe/convites/{invitationId}/reenviar`
- `POST /app/equipe/membros/{membershipId}/revogar`

### Compatibilidade mantida (`/cliente`)

- `GET /cliente`
- `GET /cliente/selecionar-conta`
- `POST /cliente/selecionar-conta`
- `GET /cliente/sem-conta`
- `POST /cliente/contexto`
- `GET /cliente/catalogo`
- `GET /cliente/catalogo/produtos/{produtoId}/planos`
- `POST /cliente/catalogo/produtos/{produtoId}/cupom`
- `POST /cliente/catalogo/contratar`
- `GET /cliente/produtos`
- `GET /cliente/produtos-disponiveis`
- `GET /cliente/produtos/{produtoId}/acessar`
- `GET /cliente/produtos/{produtoId}/instancias/{instanciaId}/acessar`
- `GET /cliente/assinaturas`
- `GET /cliente/assinaturas/ativas`
- `GET /cliente/assinaturas/historico`
- `GET /cliente/trials`
- `GET /cliente/faturas`
- `GET /cliente/faturamento`
- `GET /cliente/pagamentos`
- `GET /cliente/conta`
- `GET /cliente/empresa`
- `GET /cliente/seguranca`
- `GET /cliente/configuracoes`
- `GET /cliente/equipe`
- `GET /cliente/convites`
- `POST /cliente/equipe/convites`
- `POST /cliente/equipe/convites/{invitationId}/revogar`
- `POST /cliente/equipe/convites/{invitationId}/reenviar`
- `POST /cliente/equipe/membros/{membershipId}/revogar`

> Observação: os aliases `/cliente/*` preservam agora também seleção de conta, formulários e mutações de comercial/equipe quando a navegação começou nesse namespace. O portal continua aceitando `/app/*` como namespace principal.

## Páginas implementadas

- tela de login
- tela de cadastro da conta e usuário principal
- tela de verificação pendente
- tela de resultado de verificação de e-mail
- tela de solicitação de recuperação de senha
- tela de redefinição de senha
- tela de aceite de convite
- tela de seleção de conta
- tela de sem conta ativa
- tela de equipe com emissão e revogação de convites

## Fluxo completo

### Cadastro da conta

1. `PortalRegistrationService` valida entrada.
2. `AccountRegistrationService` cria `clientes`, `usuarios_plataforma` e `clientes_usuarios`.
3. O usuário principal nasce em `pendente_verificacao`.
4. `EmailVerificationService` gera token real em `email_verification_tokens`.
5. O portal redireciona para `/verificacao-pendente`.

### Login da plataforma

1. `LoginController` aplica rate limit por IP e por credencial.
2. `AuthService` autentica em `usuarios_plataforma`.
3. O portal bloqueia login de conta sem e-mail verificado.
4. Em caso de sucesso, cria sessão real em `sessoes_plataforma`.
5. Se houver uma única conta ativa, já entra em `/app`.
6. Se houver várias contas, redireciona para `/app/selecionar-conta` ou `/cliente/selecionar-conta`, conforme o workspace de origem.
7. Se não houver nenhuma conta ativa, redireciona para `/app/sem-conta` ou `/cliente/sem-conta`, conforme o workspace de origem.

Em ambiente integrado, o fluxo principal deve nascer do portal principal e do launch assinado do produto. O SSO direto do runtime permanece bloqueado por padrão (`AUTH_DIRECT_CENTRAL_SSO_DISABLED=true`) e o login local continua disponível apenas como contingência técnica explícita em `/login-tecnico` e `/entrar-tecnico`. Query params como `?technical=1` ou `mode=tecnico` não habilitam fallback administrativo fora da rota dedicada.

### Verificação de e-mail

1. `EmailVerificationService::inspect` valida token.
2. Trata estados `invalid`, `expired`, `consumed` e `valid`.
3. Em `valid`, `consume` marca o token como consumido e ativa o usuário.
4. O usuário passa a poder autenticar normalmente.

### Recuperação de senha

1. `PasswordResetController` aplica rate limit.
2. `PasswordResetService::request` gera token real em `password_reset_tokens`.
3. `inspect` valida `invalid`, `expired`, `consumed` e `valid`.
4. `reset` troca `senha_hash`, registra `senha_alterada_em` e revoga sessões ativas do usuário.

### Convites

1. Na equipe da conta, um gestor/owner/billing_admin emite convite em `convites_plataforma`.
2. O e-mail recebe link real de aceite.
3. Se já existir usuário com o e-mail convidado, o fluxo exige login e depois aceita o convite.
4. Se não existir usuário, o fluxo cria conta local e aceita o convite no mesmo passo.
5. O vínculo correto é registrado em `clientes_usuarios`.

### Seleção de conta

1. O portal só fixa contexto automático quando existe uma única conta ativa.
2. Com múltiplas contas, a sessão existe, mas sem `cliente_contexto_id` até a escolha.
3. A escolha grava contexto na sessão PHP e em `sessoes_plataforma`.

## Variáveis de ambiente

- `SESSION_NAME`
- `SESSION_COOKIE_PATH`
- `SESSION_COOKIE_DOMAIN`
- `SESSION_COOKIE_SAME_SITE`
- `SESSION_SECURE_COOKIE`
- `SESSION_TTL_HOURS`
- `AUTH_PREFER_CENTRAL_SSO`
- `AUTH_LOCAL_LOGIN_FALLBACK_ENABLED`
- `CENTRAL_APP_URL`
- `CENTRAL_API_URL`
- `CENTRAL_SHARED_SECRET`
- `CENTRAL_SSO_ENABLED`
- `CENTRAL_SSO_*`
- `AUTH_ENFORCE_MFA`
- `AUTH_EMAIL_VERIFICATION_TTL_HOURS`
- `AUTH_PASSWORD_RESET_TTL_MINUTES`
- `AUTH_INVITATION_TTL_HOURS`
- `RATE_LIMIT_LOGIN_MAX_ATTEMPTS`
- `RATE_LIMIT_LOGIN_DECAY_SECONDS`
- `RATE_LIMIT_PASSWORD_RESET_MAX_ATTEMPTS`
- `RATE_LIMIT_PASSWORD_RESET_DECAY_SECONDS`
- `RATE_LIMIT_VERIFICATION_RESEND_MAX_ATTEMPTS`
- `RATE_LIMIT_VERIFICATION_RESEND_DECAY_SECONDS`
- `MAIL_DRIVER`
- `MAIL_FROM_ADDRESS`
- `MAIL_FROM_NAME`
- `MAIL_NATIVE_EXTRA_PARAMETERS`
- `MAIL_SMTP_HOST`
- `MAIL_SMTP_PORT`
- `MAIL_SMTP_ENCRYPTION`
- `MAIL_SMTP_USERNAME`
- `MAIL_SMTP_PASSWORD`
- `MAIL_SMTP_TIMEOUT`
- `MAIL_SMTP_HELO_DOMAIN`
- `DB_*`
- aliases deprecated: `DB_CENTRAL_*` apenas como compatibilidade transitória

## Segurança aplicada

- sessão real com persistência em `sessoes_plataforma`
- cookie isolado por nome e path compatíveis com a subpasta publicada do NVR
- `session_regenerate_id(true)` no login e logout seguro
- CSRF nas rotas `POST`
- guards reais para autenticação, contexto de conta e papel
- rate limit em login, recuperação e reenvio de verificação
- revogação de sessões após reset de senha
- tratamento de erro amigável sem vazar credenciais de banco
- auditoria mínima em login, verificação, reset, troca de conta e convites

## MFA

O fluxo MFA já está implementado de forma **condicional por feature flag**.

Comportamento atual:

- o desafio é habilitado quando `auth_mfa_enabled=true` ou `auth_enforce_mfa=true`;
- o usuário também precisa estar com `mfa_habilitado=true`;
- o portal procura um fator suportado ativo em `mfa_fatores`;
- nesta fase, o fator suportado operacionalmente é `email_otp`;
- o desafio gera código temporário, permite reenvio com cooldown, controla tentativas e só conclui o login após verificação.

Em ambientes onde as flags estão desligadas ou o usuário não possui fator suportado ativo, o login segue sem desafio MFA adicional.

Flags mínimas desta fase:

- `AUTH_MFA_ENABLED=true` ativa o desafio apenas para contas já elegíveis (`mfa_habilitado=true` + fator suportado);
- `AUTH_ENFORCE_MFA=true` mantém o modo estrito e bloqueia conclusão do login quando a conta exige MFA, mas o fator compatível não está disponível;
- `AUTH_MFA_EMAIL_OTP_ENABLED=true` mantém o canal operacional suportado nesta fase (`email_otp`);
- `AUTH_MFA_CHALLENGE_TTL_MINUTES`, `AUTH_MFA_MAX_ATTEMPTS` e `AUTH_MFA_RESEND_COOLDOWN_SECONDS` controlam janela do código, tentativas e cooldown de reenvio.

Limitação declarada desta fase: o fator operacional fechado neste patch é `email_otp`. Em ambiente local com `MAIL_DRIVER=log`, o portal não entrega o e-mail ao cliente: a mensagem é registrada em `storage/logs/mail.log` apenas para validação operacional. Para envio real ao cliente, configure `MAIL_DRIVER=smtp` com `MAIL_SMTP_HOST`, `MAIL_SMTP_PORT`, `MAIL_SMTP_ENCRYPTION`, `MAIL_SMTP_USERNAME` e `MAIL_SMTP_PASSWORD`.

## Escopo explícito das tabelas centrais correlatas de identidade

### `mfa_recovery_codes` — fora de escopo do portal por enquanto
- O portal web **não** gera, lista, consome ou revoga recovery codes nesta fase.
- O fluxo MFA implementado continua restrito ao fator operacional `email_otp`, lido de `mfa_fatores`.
- A presença de `mfa_recovery_codes` no schema mínimo e no readiness evita falso verde de ambiente, mas **não** significa suporte funcional já implementado no portal.
- Esse gap é consciente: quando a recuperação MFA virar requisito do produto, a entrada deve acontecer por repositório e UX dedicados, sem acoplar o fluxo atual de login.

### `segredos_plataforma` — suporte incremental mínimo
- O portal trata essa estrutura como dependência operacional do schema central, **não** como fonte direta de leitura de segredo.
- Nesta fase, o portal preserva referências seguras relacionadas a MFA e gateways, mantém o healthcheck atento à presença da tabela e evita exibir material sensível.
- Gestão, rotação, descriptografia e cadastro de segredos continuam fora do escopo do portal web.

### `segredos_plataforma_versoes` — suporte incremental mínimo
- O portal aceita a existência de versões de segredo como parte do contrato do banco central e pode expor apenas identificadores seguros, como `segredo_versao_id`, em contexto administrativo.
- Não existe leitura do conteúdo sigiloso, nem rotação de versão feita pelo portal nesta fase.
- O consumo atual é deliberadamente mínimo para manter compatibilidade com MFA e gateways sem transformar o portal em console de segredos.

## Checklist manual

1. Criar uma nova conta em `/cadastro`.
2. Confirmar que o usuário nasce em `pendente_verificacao`.
3. Abrir o link de verificação enviado por e-mail ou pelo mail log local.
4. Confirmar que o usuário muda para `ativo`.
5. Fazer login em `/entrar`.
6. Validar redirecionamento correto para `/app` ou `/cliente`, incluindo `/app/selecionar-conta`/`/cliente/selecionar-conta` e `/app/sem-conta`/`/cliente/sem-conta`.
7. Solicitar recuperação em `/esqueci-senha`.
8. Abrir o link de redefinição e trocar a senha.
9. Confirmar que sessões antigas do usuário foram revogadas.
10. Com uma conta ativa e papel gestor/owner/billing_admin, emitir convite em `/app/equipe`.
11. Aceitar convite com usuário existente.
12. Aceitar convite com criação de novo usuário.
13. Revogar um convite pendente em `/app/equipe`.
14. Testar rate limit de login com tentativas inválidas repetidas.
15. Testar token inválido, expirado e já consumido para verificação e redefinição.
