Verificação de Email e Autenticação Multifator
Um pipeline de tokens perfeito não vale nada se a pessoa errada consegue obter um token. Por isso o Wittgenstein combina suas bibliotecas de tratamento de tokens com forte garantia de identidade no provedor de identidade: toda conta é vinculada a um endereço de email verificado, e o acesso sensível exige um segundo fator.
Nada disso vive no código da aplicação. É imposto pelo Keycloak antes de qualquer token ser emitido, então toda aplicação da plataforma herda essas garantias — e não consegue enfraquecê-las por acidente.
A escada de garantia
| Nível | O que o Keycloak comprova | Imposto por |
|---|---|---|
| 1 — Identidade verificada | O usuário controla a caixa de email cadastrada | Required action VERIFY_EMAIL |
| 2 — Identidade verificada + posse | …e possui um segundo fator registrado | Autenticador TOTP ou WebAuthn / passkey |
| Recuperação | Um fator ou senha perdidos podem ser redefinidos sem que um administrador manipule um segredo | Links de ação por email assinados e com expiração |
As aplicações leem o nível alcançado no token (claim acr) e decidem o que cada rota exige.
Verificação de email
As contas são criadas não verificadas. Até o usuário provar a posse do endereço, o Keycloak não conclui o login — a required action VERIFY_EMAIL interrompe o fluxo e envia um link assinado.
sequenceDiagram
participant U as Usuário
participant KC as Keycloak
participant SMTP as Relay de email
participant M as Caixa de entrada
U->>KC: Primeiro login (senha)
KC->>KC: Required action: VERIFY_EMAIL
KC->>SMTP: Email de verificação (link assinado e com expiração)
SMTP->>M: Entrega com TLS autenticado
U->>M: Abre a mensagem e clica no link
M->>KC: GET action-token
KC->>KC: emailVerified = true
KC-->>U: Continua o login → token emitido
Por que isso importa:
- Bloqueia contas com erro de digitação e squatting — ninguém consegue registrar um endereço que não possui.
- Torna o email um canal de recuperação confiável — redefinições de senha e de fator vão para uma caixa comprovada, e não apenas digitada.
- Torna os claims de
emailseguros de usar — os serviços podem confiar ememail_verifiedao vincular ou provisionar identidades.
A entrega é feita pelo relay SMTP autenticado da própria plataforma, então o email de verificação não depende de terceiros.
Autenticação multifator
O fluxo de navegador do Keycloak é estendido com uma etapa de segundo fator. Fatores suportados:
| Fator | Tipo | Observações |
|---|---|---|
| TOTP | App autenticador (qualquer app RFC 6238) | Cadastrado pela required action CONFIGURE_TOTP |
| WebAuthn / passkeys | Chave de hardware, autenticador da plataforma | Resistente a phishing: a credencial é vinculada ao origin do site |
| Códigos de recuperação | Códigos de uso único | Alternativa offline quando o dispositivo é perdido |
O MFA pode ser aplicado como política, e não como configuração por usuário:
- Sempre — todos os usuários do realm.
- Por papel ou grupo — por exemplo, quem tem um papel administrativo recebe o desafio; usuários somente leitura, não (fluxos condicionais do Keycloak).
- Step-up — o usuário entra no nível 1 e só recebe o pedido de segundo fator ao acessar algo que exija o nível 2.
Exigindo step-up na sua API
O Keycloak registra o nível de autenticação no claim acr. Como o OidcMiddleware já interpretou o token da requisição, uma rota pode exigir o nível mais forte em poucas linhas:
from fastapi import Depends, HTTPException
from wittgenstein_oidc_backend import get_current_payload
def require_mfa(payload: dict = Depends(get_current_payload)) -> dict:
if int(payload.get("acr", "0")) < 2: # 2 = senha + segundo fator
raise HTTPException(
status_code=401,
detail="step-up required",
headers={"WWW-Authenticate": 'Bearer error="insufficient_user_authentication"'},
)
return payload
@app.delete("/tenants/{tenant_id}", dependencies=[Depends(require_mfa)])
async def delete_tenant(tenant_id: str): ...
A SPA reage a essa resposta redirecionando ao Keycloak com acr_values=2; o usuário conclui o segundo fator e volta com um token que satisfaz a rota.
Links de ação por email
Alterações sensíveis na conta nunca passam pela aplicação. Para trocar a senha, a app pede ao Keycloak que envie ao usuário um link de required action; o usuário escolhe a nova senha na página do próprio Keycloak, onde valem a política de senhas e a proteção contra força bruta. A aplicação nunca vê, armazena nem transmite a senha.
O mesmo mecanismo pode solicitar CONFIGURE_TOTP (recadastrar um fator), VERIFY_EMAIL (reverificar) ou UPDATE_PROFILE. Os links são assinados, de propósito único e de curta duração.
Privilégio mínimo para quem chama
O serviço que dispara esses emails usa um client de service account dedicado, com apenas os papéis de realm-management de que precisa (view-users, manage-users), nunca uma credencial de administrador completa.
Reforço de sessão e bloqueio
A garantia é tão forte quanto a sessão que vem depois dela:
- Access tokens de vida curta (minutos), com timeout de inatividade deslizante e um máximo absoluto de sessão.
- Rotação do refresh token, mantido em um cookie
HttpOnly(veja OIDC e Segurança de Tokens). - Detecção de força bruta bloqueia temporariamente uma conta após falhas repetidas, de modo que o segundo fator não possa ser vencido por tentativa e erro.
- Política de senhas (tamanho, histórico, lista de bloqueio) avaliada pelo Keycloak, em um só lugar.
Verifique a configuração
"0",
"1",
"2"
]curl -s -H "Authorization: Bearer $TOKEN" https://app.example.com/api/tenants/t-1 -X DELETE -o /dev/null -w "%{http_code}\n"401# um token de nível 1 é recusado em uma rota de nível 2 até o usuário concluir o MFA
Implantando o MFA com segurança
Comece pelos papéis administrativos, confirme que os códigos de recuperação e os links de email funcionam de ponta a ponta, e só então amplie a política. Como a imposição vive no IdP, ampliá-la nunca exige um deploy de nenhuma aplicação.