A melhor forma de desenvolver um sistema de autenticação
Perdi o acesso a uma assinatura que eu tinha acabado de pagar. Nada quebrou criptograficamente — a autenticação funcionou perfeitamente. O que falhou foi arquitetura. Este artigo parte desse caso real para separar Auth System, Auth Broker e IAM, e propor um princípio: identidade precisa de redundância.
1. O dia em que perdi o acesso a uma assinatura que eu tinha acabado de pagar
Eu criei um endereço de e-mail no Google para compartilhar o acesso de uma ferramenta com um colaborador. Dias depois, tinha acabado de assinar um plano de alto valor — cerca de US$ 200 — usando exatamente aquele endereço.
O Google desabilitou a conta de e-mail.
Não houve aviso útil, não houve interlocutor, não houve processo de contestação que produzisse resposta em tempo hábil. E o serviço que eu tinha acabado de pagar autenticava exclusivamente por e-mail: login por link mágico enviado àquele endereço, sem opção de definir uma senha própria, sem segundo endereço vinculado, sem telefone, sem passkey, sem código de recuperação.
O resultado foi absurdo na sua simplicidade: eu continuava sendo o titular. A assinatura continuava paga. Os dados continuavam lá. O contrato continuava existindo. Só o mecanismo pelo qual eu conseguia provar tudo isso havia desaparecido — e ele estava sob controle de uma terceira empresa que não tinha nenhuma obrigação comigo.
Sem canal de suporte que resolvesse e sem caminho de autenticação alternativo, a única alavanca que me restou foi contestar a cobrança no cartão para forçar a recuperação do acesso.
Vale insistir num ponto, porque ele é o eixo deste artigo inteiro:
Nada falhou criptograficamente. A autenticação funcionou perfeitamente. Ela simplesmente autenticou um canal que eu não controlava mais.
Isso não é um bug. É uma decisão de arquitetura.
---
2. Desenhando a falha
O que aconteceu comigo tem uma forma, e essa forma se repete em muitos produtos:
`` Serviço pago │ ▼ Autenticação │ ▼ E-mail (canal único) │ ▼ Provedor externo (Google) │ ▼ Sistema antiabuso automatizado │ ▼ Decisão automatizada ``
Cada seta é uma dependência. E a cadeia inteira tem exatamente um caminho.
Quando o penúltimo nó decide desligar, tudo abaixo dele cai:
`` Conta de e-mail desabilitada ✗ │ ▼ Link mágico não chega ✗ │ ▼ Login impossível ✗ │ ▼ Serviço inacessível ``
O Google documenta que contas podem ser desabilitadas por sistemas automatizados de detecção de abuso, inclusive por criação de contas em massa ou por uso considerado irregular, e que o caminho para reverter é um processo de apelação. Não interessa aqui se a decisão foi correta ou equivocada no meu caso — e é importante dizer que nem toda conta bloqueada é falso positivo.
O que interessa para quem projeta sistemas é a consequência arquitetural:
Um serviço externo pode tomar uma decisão — legítima ou não — que destrói a disponibilidade da identidade do seu usuário dentro do seu produto.
Se o seu sistema não sobrevive a isso, o problema é do seu sistema.
---
3. Autenticação não é autorização
Antes de falar de arquitetura, é preciso separar dois conceitos que a prática mistura.
Autenticação (AuthN) responde: você é quem afirma ser? Senha, passkey, FIDO2/WebAuthn, TOTP, código por SMS, link mágico, login federado, certificado.
Autorização (AuthZ) responde: o que essa identidade pode fazer?
`` Autenticação → Quem é você? ↓ Identidade ↓ Autorização → O que você pode fazer? ``
No meu caso, repare, a autorização nunca esteve em questão. Ninguém disputou que a assinatura era minha. O sistema simplesmente perdeu o canal pelo qual eu provava continuar controlando aquela identidade.
É por isso que tratar autenticação como "a telinha de login" é um erro de categoria. Autenticação é a porta de entrada de uma infraestrutura de identidade — e infraestrutura se projeta pensando em falha.
---
4. Três camadas: Auth System, Auth Broker e IAM
Os termos são usados de forma imprecisa no mercado. Uma divisão útil:
| Camada | Função principal | Complexidade | |---|---|---| | Auth System | Autenticar usuários | Baixa/Média | | Auth Broker | Unificar múltiplos provedores de identidade | Média/Alta | | IAM | Identidade + autenticação + autorização + políticas + ciclo de vida | Alta |
A diferença não é quantidade de funcionalidade. É responsabilidade arquitetural.
4.1. Auth System
Autentica usuários de uma aplicação ou de um conjunto limitado delas.
`` Usuário │ ▼ Auth System │ ┌───────┼───────┐ ▼ ▼ ▼ Senha OTP Passkey │ │ │ └───────┼───────┘ ▼ Sessão │ ▼ Aplicação ``
Administra usuários, credenciais, sessões, MFA, recuperação, e-mails, telefones, tokens e dispositivos. É suficiente para muita coisa — e fica curto quando a empresa tem dezenas de sistemas.
4.2. Auth Broker
Em vez de cada aplicação saber conversar com Google, Microsoft, Apple, GitHub, SAML e LDAP, um componente centraliza isso e devolve uma identidade interna.
`` Google ──────┐ Microsoft ───┤ Apple ───────┤ GitHub ──────┤ SAML ────────┤ LDAP ────────┤ ▼ Auth Broker │ ▼ Aplicações ``
A aplicação deixa de precisar saber por onde o usuário entrou. Ela recebe user_id e segue a vida.
Só que o broker resolve um problema e cria outro: ele vira um ponto central de falha. Um broker sério precisa de alta disponibilidade, replicação, rotação de chaves, recuperação de desastre, observabilidade e fallback. E, acima de tudo:
O broker não pode transformar um provedor externo em dependência obrigatória para recuperar a identidade.
Foi exatamente essa regra que o serviço da minha história violou.
4.3. IAM
O IAM administra o ciclo completo da identidade.
`` IAM │ ┌─────────────┼─────────────┐ │ │ │ Identidade AuthN AuthZ │ │ │ │ ┌────┼────┐ │ │ MFA SSO Passkey RBAC │ ├── Ciclo de vida do usuário ├── Sessões ├── Dispositivos ├── Recuperação ├── Políticas ├── Auditoria └── Delegação ``
IAM não é "login centralizado". É identidade tratada como infraestrutura.
---
5. O princípio central: identidade não pode depender de um canal só
Aqui está a regra que eu extraí do meu prejuízo, e que eu proponho como piso de projeto:
Um autenticador não deve ser, ao mesmo tempo, o único meio de login, o único meio de recuperação e uma dependência externa que o sistema não controla.
Repare que são três papéis distintos acumulados numa coisa só. Qualquer um deles isolado é aceitável. Os três juntos é o que produz perda total.
No meu caso, o e-mail era:
- o único identificador de login;
- o único canal de recuperação;
- um serviço de terceiro, gratuito, sujeito a decisão automatizada.
Três papéis, um ponto de falha, zero saídas.
---
6. Login e recuperação são processos diferentes
Esse é o erro conceitual mais caro que vi na prática.
Login é o caminho de quem tem um autenticador válido. Recuperação é o caminho de quem perdeu o autenticador.
São fluxos com premissas opostas — e, ainda assim, muitos sistemas os implementam sobre o mesmo canal. Quando isso acontece, o sistema não tem recuperação: tem uma segunda cópia do mesmo login.
As diretrizes do NIST (SP 800-63B) tratam recuperação de conta como um processo distinto da autenticação, com mecanismos próprios — códigos de recuperação, autenticadores ainda disponíveis, verificações adicionais.
A consequência prática para quem projeta:
Autenticação e recuperação precisam ser desenhadas juntas, no mesmo momento. Construir login primeiro e "pensar em recuperação depois" é como construir um prédio e pensar na escada de incêndio depois.
---
7. Múltiplos autenticadores independentes
A conta deixa de ser um segredo e passa a ser um conjunto de provas possíveis:
`` Usuário ├── E-mail principal ├── E-mail secundário (outro provedor) ├── Telefone principal ├── Telefone secundário ├── Senha ├── Passkey 1 ├── Passkey 2 ├── TOTP ├── Códigos de recuperação └── Dispositivos confiáveis ``
Se o serviço da minha história tivesse essa estrutura, o desfecho teria sido banal: o Google desabilita o e-mail A, eu entro pelo e-mail B (ou pela passkey, ou por senha + TOTP), abro o painel de segurança, removo o e-mail morto, cadastro outro. Cinco minutos. Sem contestação de cartão, sem prejuízo, sem atrito.
---
8. Redundância de identidade: por que dois Gmails não são redundância
Esse é o ponto que quase todo mundo erra, inclusive gente que já entendeu a necessidade de ter "mais de um e-mail".
Considere:
`` user@gmail.com backup@gmail.com ``
Parece redundante. Não é. Ambos dependem do mesmo domínio de falha:
`` Google │ ┌─────┴─────┐ conta A conta B ``
Se a falha for sistêmica no provedor — bloqueio de perfil, decisão antiabuso que atinge a pessoa e não só o endereço, indisponibilidade regional, encerramento de serviço — os dois caem juntos.
Redundância de verdade exige independência de domínio de falha:
`` Gmail + Outlook Gmail + Proton Mail Outlook + Yahoo Gmail + domínio próprio domínio próprio + provedor externo ``
Formalizando o raciocínio: se E1 = Gmail e E2 = Outlook, então Falha(E1) ≠ Falha(E2). Mas se E1 = Gmail e E2 = Gmail, então Falha(Google) → E1 ∧ E2.
Dois métodos podem existir no banco de dados e não serem independentes. Chamo isso de diversidade de provedor de identidade — e sustento que ela deveria ser uma métrica de projeto, não um conselho de usuário avançado.
A métrica certa não é "quantos métodos de autenticação temos?", e sim:
Quantos domínios de falha independentes temos?
`` Ruim Gmail A, Gmail B, Gmail C → 3 identificadores, 1 domínio de falha Melhor Gmail, Outlook, Proton → 3 domínios de provedor Ideal Gmail, Outlook, Passkey, TOTP, códigos offline → classes de dependência diferentes ``
Uma conta com cinco endereços Gmail é menos resiliente que uma conta com um Gmail, um Outlook, uma passkey, um TOTP e dez códigos de recuperação impressos.
---
9. O sistema deveria incentivar a redundância, não apenas permitir
Permitir não basta. O usuário não vai configurar redundância por conta própria — eu, que trabalho com isso, não tinha configurado.
Um IAM maduro mede e cobra:
``` Resiliência da conta: 62%
✓ E-mail principal ✓ Telefone principal ✓ Passkey ✗ E-mail secundário (outro provedor) ✗ Telefone secundário ✗ Códigos de recuperação ```
E age sobre o que falta, com mensagem específica em vez de genérica:
"Seus dois e-mails são do mesmo provedor. Se esse provedor bloquear seu perfil, você perde os dois. Adicione um endereço de outro provedor."
Isso transforma segurança em resiliência operacional — que é o que o usuário realmente precisa e nunca pede.
---
10. E-mail secundário não deveria ser só "e-mail de recuperação"
Há uma diferença relevante entre recovery email e identidade de login.
`` emails ├── endereço ├── verificado_em ├── é_primário └── login_habilitado ← esta coluna muda tudo ``
Com login_habilitado, três endereços apontam para a mesma identidade e qualquer um deles inicia autenticação:
`` lucas@gmail.com ─┐ lucas@outlook.com ─┼──→ user_id = 123 lucas@empresa.com ─┘ ``
Não são três usuários. É uma identidade com três portas.
O mesmo vale para telefones — com uma ressalva importante: SMS é vulnerável a SIM swap, interceptação, engenharia social e comprometimento da operadora. Telefone é ótimo como redundância e recuperação, e ruim como fator forte isolado.
---
11. Passkeys, e o problema que elas não resolvem
Passkeys (WebAuthn/FIDO2) usam criptografia de chave pública. O servidor guarda a chave pública; a privada nunca sai do autenticador.
`` Servidor ──challenge──▶ Dispositivo Servidor ◀──assinatura── Dispositivo │ ▼ Verificação ``
O NIST classifica WebAuthn/FIDO2 como autenticação resistente a phishing, porque a credencial é vinculada ao domínio legítimo — o usuário não consegue entregá-la a um site clonado.
É a melhor tecnologia de autenticação disponível hoje. E ela não resolve recuperação. A resposta a "perdi o dispositivo" não pode ser "perdeu a conta".
`` Passkey A perdida ↓ Passkey B (outro dispositivo) ↓ Login ↓ Registrar nova Passkey A ``
Autenticação forte só vira autenticação resiliente quando há mais de um autenticador forte.
E sim: senha ainda é útil. Considerar senha obsoleta é simplista. Uma senha forte, guardada em gerenciador, combinada com MFA, é um caminho de autenticação independente do seu telefone, do seu dispositivo e do seu provedor de e-mail — exatamente o tipo de independência que faltou no meu caso. As revisões recentes do NIST elevaram consideravelmente o comprimento mínimo recomendado e passaram a exigir bloqueio de senhas comuns ou já vazadas, em vez das antigas regras de composição e troca periódica.
---
12. 2FA não significa "dois métodos quaisquer"
Confusão extremamente comum. Dois e-mails não são dois fatores. Dois telefones também não.
Os fatores clássicos:
`` Algo que você sabe → senha Algo que você tem → passkey, TOTP, chave física Algo que você é → biometria ``
O AAL2 do NIST exige dois fatores distintos, e recomenda oferecer autenticação resistente a phishing.
E MFA, sozinho, não resolve recuperação: senha + TOTP é excelente até o usuário perder o celular com o TOTP. Sem códigos de recuperação, segunda passkey ou segundo canal verificado, o MFA vira uma barreira contra o próprio dono.
Os códigos de recuperação precisam ser de alta entropia, armazenáveis offline, de uso único, revogáveis e regeneráveis — e o sistema deve avisar quando um for consumido.
---
13. Sessões: autenticação não termina no login
`` Login → Autenticação → Access Token + Refresh Token → Sessão ``
O sistema deve registrar session_id, user_id, device_id, IP, user agent, created_at, last_seen, expires_at, revoked_at — e mostrar isso ao usuário, com botão de encerrar cada sessão e de encerrar todas as outras.
Um conceito que economiza muito sofrimento:
Sessão não é identidade.
`` Identidade ├── Sessão 1 ├── Sessão 2 └── Token de API ``
A identidade permanece; as sessões são descartáveis. Isso permite logout sem delete account, e revogar um dispositivo sem desabilitar o usuário.
Isso se conecta ao conceito de blast radius. Se um e-mail for comprometido, o correto não é "conta inteira comprometida", e sim "aquele autenticador revogado, conta preservada":
`` Telefone perdido → revoga telefone → passkey + e-mail continuam Sessão roubada → revoga sessão → resto da conta intacto ``
---
14. SSO não significa "um único método de login"
Outro erro conceitual, e o que mais se parece com o que me aconteceu.
SSO significa: uma identidade centralizada autentica o usuário para múltiplos serviços. SSO não significa: existe apenas uma maneira de autenticar essa identidade.
`` Passkey TOTP Senha OIDC └─────────┼───────┴────────┘ ▼ IAM │ ┌─────────┼─────────┐ ▼ ▼ ▼ App A App B App C ``
Muitos ecossistemas de produto: entram por SSO com um provedor e fecham todas as outras portas. O usuário ganha conveniência e perde soberania sobre a própria conta.
E uma distinção técnica que costuma ser atropelada: OAuth não é login. OAuth 2.0 é um framework de delegação/autorização. Para autenticação federada usa-se OpenID Connect, que roda sobre OAuth 2.0. O IAM precisa saber exatamente o que está recebendo — OAuth → autorização, OIDC → identidade.
---
15. Recuperação não pode ser a vulnerabilidade
Aqui a coisa inverte. Depois de defender tantos caminhos alternativos, é preciso dizer o oposto com a mesma força:
``` Conta muito segura: senha + passkey + TOTP
mas:
"Esqueci minha senha" → digite o e-mail → código por SMS → conta recuperada ```
Nesse desenho, o atacante simplesmente ignora toda a segurança principal. Portanto:
segurança(conta) = mínimo(segurança de todos os caminhos de login e recuperação)
A conta é tão segura quanto seu caminho mais fraco de recuperação.
O equilíbrio entre as duas regras é o coração do projeto:
- Nenhum caminho individual pode ser indispensável.
- Nenhum caminho alternativo pode ser fraco a ponto de destruir a segurança do principal.
Os mecanismos que sustentam esse equilíbrio:
- Recuperação composta: código de recuperação + autenticador existente; ou segunda passkey + e-mail verificado; ou, em contas de alto valor, verificação de identidade + contato de recuperação + período de espera.
- Step-up authentication: ver o painel exige sessão normal; trocar o e-mail principal exige reautenticação + MFA; excluir a conta exige senha/passkey + MFA + confirmação.
- Regra de adição de autenticador: para adicionar um novo autenticador, exija autenticação equivalente ou superior à que já existe.
- Atraso de segurança: recuperação de alto risco entra em janela de 24h, notificando todos os canais e permitindo cancelamento pelo dono legítimo.
- Notificação multicanal: todo evento crítico — novo login, nova passkey, novo e-mail, MFA removido, recuperação iniciada, chave de API criada — vai para mais de um canal.
Trocar o e-mail principal, aliás, merece destaque como operação de altíssimo risco:
`` Sessão roubada → troca e-mail → recebe recuperação → remove e-mail antigo → conta perdida ``
---
16. Modelo de dados mínimo
A estrutura reflete a tese: identidade separada de credenciais, e credenciais em 1:N.
`` users → id, status, created_at user_emails → user_id, email, verified_at, is_primary, login_enabled user_phones → user_id, phone, verified_at, login_enabled authenticators → user_id, type, status, created_at, last_used_at passkeys → authenticator_id, credential_id, public_key, sign_count totp_credentials → authenticator_id, secret_encrypted recovery_codes → user_id, code_hash, used_at sessions → user_id, device_id, created_at, expires_at, revoked_at devices → user_id, fingerprint identity_providers → provider user_identities → user_id, provider_id, external_subject ``
Duas regras de armazenamento que não se negociam: senha vira Argon2id(senha, salt, parâmetros) — nunca a senha; passkey guarda chave pública — nunca a privada; e o segredo TOTP é material secreto, com criptografia em repouso e acesso restrito.
E um detalhe que parece pequeno e não é: o user_id interno deve ser imutável e independente do e-mail. No momento em que o identificador da identidade é o endereço de e-mail, você acabou de amarrar sua identidade ao provedor de outra empresa.
---
17. Como implementar isso num sistema desenvolvido com IA
Vale um parágrafo prático, porque hoje boa parte dos sistemas nasce com assistência de IA — e é justamente aí que essa camada costuma sair errada. O modelo entrega, por padrão, o caminho mais curto: tabela users com email e password_hash, login, "esqueci minha senha" por e-mail, pronto. Funciona na demo e reproduz exatamente a arquitetura que me custou dinheiro.
O problema não é a IA escrever autenticação. É ela escrever o mínimo viável de autenticação e isso passar despercebido na revisão, porque o login funciona.
Uma forma eficiente de resolver isso é padronizar o gerador em vez de revisar caso a caso — carregar uma skill que já traga os pisos deste artigo como requisito obrigatório: identidade separada do e-mail, múltiplos autenticadores desde a primeira migration, recuperação desenhada junto com o login, sessões revogáveis, step-up nas operações sensíveis. É o que mantemos em skills.schematize.org: um gerador de IAM que produz a estrutura completa em vez do atalho, e que trata "só e-mail + link mágico" como o anti-padrão que ele é.
A vantagem de fixar isso no gerador é que a redundância entra na primeira versão do schema. Adicionar user_emails com login_enabled depois que existem cem mil contas amarradas a users.email é uma migração cara — e é exatamente o motivo pelo qual tantos produtos maduros continuam com autenticação de canal único.
---
18. Checklist
Identidade user_id interno e imutável · múltiplos e-mails · múltiplos telefones · identidades externas vinculáveis · identidade separada de credenciais
Autenticação senha (Argon2id) · passkeys/WebAuthn · TOTP · MFA · OIDC · SAML · login social · step-up
Redundância mais de um e-mail · provedores diferentes · mais de um telefone quando viável · mais de uma passkey · códigos de recuperação · métodos genuinamente independentes
Sessões session_id · gestão de dispositivos · expiração · idle timeout · revogação · logout global · lista visível ao usuário
Segurança rate limiting · proteção contra credential stuffing e força bruta · detecção de senha vazada · CSRF · cookies Secure/HttpOnly/SameSite · TLS · rotação de chaves · trilha de auditoria
Recuperação códigos de recuperação · segundo e-mail · segundo autenticador · fluxo de recuperação próprio · notificação · cooldown em alterações críticas · proteção contra takeover
Governança RBAC · ABAC · organizações · grupos · políticas · contas de serviço · chaves de API · delegação · provisioning e deprovisioning
---
19. Conclusão
A arquitetura tradicional pensa assim:
`` Usuário → E-mail → Login ``
Um sistema moderno precisa pensar assim:
`` IDENTIDADE │ ┌──────────────────┼──────────────────┐ Autenticadores Recuperação Federação │ │ │ Passkey/Senha/TOTP Código/E-mail/Tel OIDC/SAML/SSO │ Sessões → Políticas → Autorização ``
O Auth System resolve autenticação. O Auth Broker resolve integração entre provedores. O IAM resolve identidade como infraestrutura — e infraestrutura precisa ser segura, recuperável, auditável, revogável, federável e resiliente.
O princípio que orienta todo o desenho cabe numa frase:
Nunca transforme um único e-mail, telefone, dispositivo, provedor ou método de autenticação no ponto de falha definitivo da identidade de um usuário.
O usuário vai perder o celular. Vai perder uma passkey. Vai esquecer uma senha. Vai trocar de número. Vai ter um provedor de e-mail bloqueado — eu tive. Pode até perder várias dessas coisas ao mesmo tempo.
Um IAM mal projetado lê isso como: "você perdeu seu e-mail, portanto perdeu sua conta."
Um IAM bem projetado lê como: "você perdeu um dos seus autenticadores; ainda existem outros mecanismos independentes pelos quais você prova controle sobre essa identidade."
Em sistemas de alto valor, essa diferença deixa de ser questão de UX. Vira questão de segurança, de continuidade operacional e de propriedade efetiva da conta — como eu descobri da pior forma, contestando no cartão uma compra que era legitimamente minha, porque não me restou nenhum outro jeito de provar que eu era eu.
Autenticação prova identidade. IAM preserva identidade.