Como implementar autenticação passwordless em aplicações web
1. Fundamentos da autenticação passwordless
A autenticação passwordless é um método de verificação de identidade que elimina o uso de senhas tradicionais. Em vez de armazenar hashes de senhas no servidor, o sistema utiliza tokens temporários ou credenciais criptográficas para autenticar usuários. Esse modelo reduz significativamente ataques de phishing, roubo de credenciais e a fadiga de senhas entre usuários.
Os principais modelos incluem:
- Magic links: links únicos enviados por email que autenticam o usuário ao serem clicados
- Códigos OTP: códigos numéricos de uso único enviados por SMS ou email
- WebAuthn/FIDO2: autenticação baseada em chave pública/privada usando biometria ou chaves de segurança
- Chaves de segurança físicas: dispositivos como YubiKey que geram assinaturas criptográficas
2. Projetando o fluxo de autenticação sem senha
O fluxo básico segue estas etapas:
- Usuário informa seu identificador (email ou telefone)
- Servidor gera um token efêmero e o envia ao identificador
- Usuário apresenta o token recebido
- Servidor valida o token e cria uma sessão autenticada
Após a validação, utilizamos JWT (JSON Web Tokens) para gerenciar sessões:
// Estrutura do JWT de acesso
{
"sub": "user_12345",
"email": "usuario@exemplo.com",
"iat": 1700000000,
"exp": 1700003600,
"type": "access"
}
// Estrutura do JWT de refresh
{
"sub": "user_12345",
"iat": 1700000000,
"exp": 1700086400,
"type": "refresh"
}
O tratamento de erros inclui timeouts configuráveis (geralmente 5-15 minutos para tokens), limites de tentativas (máximo 3-5 por período) e fallback para reenvio após 30 segundos.
3. Implementação de magic links e códigos OTP
Geração de tokens seguros
// Node.js - Geração de token criptograficamente seguro
const crypto = require('crypto');
function generateSecureToken() {
return crypto.randomBytes(32).toString('hex');
}
// Python - Alternativa com secrets
import secrets
def generate_otp():
return ''.join(str(secrets.randbelow(10)) for _ in range(6))
Envio seguro
// Exemplo de envio por email usando SendGrid
POST https://api.sendgrid.com/v3/mail/send
Headers:
Authorization: Bearer SG_API_KEY
Content-Type: application/json
Body:
{
"personalizations": [{"to": [{"email": "usuario@exemplo.com"}]}],
"from": {"email": "noreply@suaapp.com"},
"subject": "Seu link de acesso",
"content": [{
"type": "text/html",
"value": "<a href='https://suaapp.com/auth/verify?token=TOKEN_AQUI'>Clique para acessar</a>"
}]
}
Armazenamento e verificação
// Armazenamento em Redis com TTL
SETEX token:abc123 300 user_12345
// Token expira em 5 minutos
// Verificação
GET token:abc123
// Retorna user_12345 se válido, nil se expirado
4. Integração com WebAuthn e FIDO2
Registro de credencial (attestation)
// Servidor gera challenge aleatório
{
"challenge": "base64url_encoded_random_bytes",
"rp": {"name": "Minha Aplicação", "id": "suaapp.com"},
"user": {
"id": "base64url_user_id",
"name": "usuario@exemplo.com",
"displayName": "Usuário Exemplo"
},
"pubKeyCredParams": [{"type": "public-key", "alg": -7}],
"timeout": 60000
}
Autenticação (assertion)
// Servidor envia challenge para login
{
"challenge": "base64url_encoded_random_bytes",
"allowCredentials": [{
"id": "base64url_credential_id",
"type": "public-key"
}],
"timeout": 60000
}
Armazenamento de chaves públicas
// Tabela de credenciais no banco de dados
{
"user_id": "user_12345",
"credential_id": "base64url_encoded",
"public_key": "base64url_encoded",
"counter": 0,
"device_name": "iPhone 15"
}
5. Segurança e mitigação de ataques
Proteção contra replay e CSRF
// Geração de nonce para WebAuthn
let challenge = crypto.randomBytes(32);
sessionStorage.setItem('auth_challenge', challenge.toString('base64'));
// Token anti-CSRF em formulários
<input type="hidden" name="_csrf" value="csrf_token_aqui">
Rate limiting
// Limite por IP (5 tentativas por minuto)
RATE_LIMIT: 5
WINDOW: 60 segundos
REDIS_KEY: rate_limit:192.168.1.1:auth
// Limite por identificador (3 tentativas por 15 minutos)
RATE_LIMIT: 3
WINDOW: 900 segundos
REDIS_KEY: rate_limit:email:usuario@exemplo.com
Prevenção de enumeração
// Resposta genérica para sucesso e falha
HTTP 200 OK
{
"message": "Se o email existir, você receberá um link de acesso"
}
// Delay uniforme de 500ms antes de responder
await delay(500);
6. Experiência do usuário e boas práticas
Design de interface simplificado
<!-- Formulário de login passwordless -->
<form id="loginForm">
<label for="email">Email</label>
<input type="email" id="email" required
placeholder="seu@email.com"
autocomplete="email">
<button type="submit" id="submitBtn">
Enviar link de acesso
</button>
<div id="loading" style="display:none">
Enviando link...
<div class="spinner"></div>
</div>
<div id="success" style="display:none">
Link enviado! Verifique seu email.
</div>
</form>
Suporte a múltiplos dispositivos
// Sincronização de credenciais via iCloud Keychain
// A credencial registrada em um dispositivo Apple
// fica disponível em todos os dispositivos do mesmo iCloud
// Gerenciadores de senha (1Password, Bitwarden)
// Detectam automaticamente campos de OTP e magic links
Fallback para senha tradicional
// Opção de fallback quando WebAuthn não está disponível
if (!window.PublicKeyCredential) {
mostrarOpcaoOTP();
// ou redirecionar para login com senha
}
7. Testes e implantação em produção
Testes unitários
// Teste de geração de token
describe('Token Generation', () => {
it('deve gerar token com 64 caracteres hex', () => {
const token = generateSecureToken();
assert.equal(token.length, 64);
assert.match(token, /^[a-f0-9]+$/);
});
it('deve expirar após 300 segundos', async () => {
await storeToken('test_token', 'user_1', 300);
await delay(301000);
const result = await verifyToken('test_token');
assert.isNull(result);
});
});
Monitoramento e logging
// Estrutura de log para tentativas de login
{
"timestamp": "2024-01-01T00:00:00Z",
"event": "login_attempt",
"identifier": "usuario@exemplo.com",
"method": "magic_link",
"success": true,
"ip": "192.168.1.1",
"user_agent": "Mozilla/5.0..."
}
// Alertas para anomalias
if (loginAttempts > 100 em 1 minuto) {
alertarEquipeSeguranca();
bloquearIP(ip);
}
Considerações de desempenho
// Cache Redis para tokens
// Configuração de cluster Redis para alta disponibilidade
// TTL máximo de 15 minutos para tokens de autenticação
// Balanceamento de carga
// Sessões podem ser armazenadas em Redis compartilhado
// CDN para assets estáticos (HTML, CSS, JS)
A implementação de autenticação passwordless requer atenção cuidadosa à segurança, experiência do usuário e escalabilidade. Comece com magic links ou OTP para sua base de usuários e evolua gradualmente para WebAuthn conforme a maturidade do sistema.
Referências
- WebAuthn W3C Recommendation — Especificação oficial do W3C para autenticação WebAuthn, incluindo detalhes sobre attestation, assertion e gerenciamento de credenciais
- FIDO2 Technical Specifications — Documentação completa da FIDO Alliance sobre os padrões FIDO2, incluindo CTAP e WebAuthn
- Auth0 Passwordless Authentication Guide — Guia prático da Auth0 sobre implementação de autenticação passwordless com magic links e OTP
- Redis Token Storage Patterns — Documentação oficial do Redis sobre padrões de armazenamento de tokens com TTL e expiração automática
- OWASP Authentication Cheat Sheet — Guia de segurança da OWASP com melhores práticas para autenticação, incluindo rate limiting e prevenção de enumeração
- SendGrid Email API Documentation — Documentação oficial da API SendGrid para envio seguro de emails transacionais com magic links
- Twilio Verify API for OTP — API da Twilio para envio e verificação de códigos OTP via SMS e email com recursos de segurança integrados