2FA - Autenticação de Dois Fatores
O que é este Node?
O 2FA é o node responsável por implementar autenticação de dois fatores no Lumina Flow Builder. Ele gera secrets, verifica tokens TOTP e cria QR codes para apps autenticadores.
Por que este Node existe?
Autenticação de dois fatores adiciona camada extra de segurança. O 2FA existe para:
- Segurança Adicional: Proteger contas mesmo se senha for comprometida
- Compatibilidade Universal: Funcionar com apps como Google Authenticator, Authy, Microsoft Authenticator
- Códigos Temporários: Gerar tokens de 6 dígitos que expiram a cada 30 segundos
- Códigos de Backup: Fornecer códigos alternativos para recuperação de acesso
- Padrão TOTP: Implementar Time-based One-Time Password (RFC 6238)
Como funciona internamente?
Quando o 2FA é executado, o sistema:
- Identifica operação: Determina se deve gerar secret, verificar token ou gerar QR code
- Operação Generate Secret:
- Cria secret aleatório de 32 caracteres
- Gera 10 códigos de backup
- Cria URL otpauth para apps autenticadores
- Retorna secret em base32
- Operação Verify Token:
- Recebe secret e código de 6 dígitos
- Valida usando algoritmo TOTP
- Permite window de 2 períodos (60s) para compensar dessincronia
- Retorna válido ou inválido
- Operação Generate QR:
- Cria URL otpauth com secret, label e issuer
- Gera QR code como Data URL (base64)
- Permite scan direto em apps autenticadores
Código interno (security-executor.service.ts:173-191):
private async execute2FA(parameters: any, context: any): Promise<any> {
const { operation, secret, token, label, issuer } = parameters;
this.logger.log(`🔐 2FA - Operation: ${operation}`);
switch (operation) {
case 'generate_secret':
return this.generate2FASecret(label, issuer);
case 'verify_token':
return this.verify2FAToken(secret, token);
case 'generate_qr':
return this.generate2FAQR(secret, label, issuer);
default:
throw new Error(`Unsupported 2FA operation: ${operation}`);
}
}
Quando você DEVE usar este Node?
Use 2FA sempre que precisar de autenticação de dois fatores:
Casos de uso
- Proteção de Contas: "Preciso adicionar 2FA para contas administrativas"
- Transações Sensíveis: "Preciso confirmar transferências bancárias com código 2FA"
- Acesso a Dados Críticos: "Preciso 2FA antes de acessar informações confidenciais"
- Compliance: "Preciso atender requisitos de segurança que exigem MFA"
- Recovery de Conta: "Preciso fornecer códigos de backup para recuperação"
Quando NÃO usar 2FA
- Autenticação Simples: Use AUTH para autenticação básica com senha
- Tokens JWT: Use TOKEN para autenticação baseada em tokens
- OAuth Social: Use OAUTH para login com Google/Facebook
Parâmetros Detalhados
operation (string, obrigatório)
O que é: Define qual operação 2FA será executada.
Valores possíveis:
- generate_secret: Gera novo secret para configuração inicial
- verify_token: Verifica código de 6 dígitos do usuário
- generate_qr: Gera QR code para scan no app autenticador
Flow completo para testar (Generate Secret):
{
"name": "Teste 2FA - Generate Secret",
"nodes": [
{
"id": "start_1",
"type": "start",
"position": { "x": 100, "y": 100 },
"data": { "label": "Início" }
},
{
"id": "2fa_1",
"type": "2fa",
"position": { "x": 300, "y": 100 },
"data": {
"label": "Gerar Secret",
"parameters": {
"operation": "generate_secret",
"label": "Lumina App",
"issuer": "Lumina Flow Builder"
}
}
},
{
"id": "message_1",
"type": "message",
"position": { "x": 500, "y": 100 },
"data": {
"label": "Mostrar Secret",
"parameters": {
"message": "Secret: {{2fa_1.secret}}\nCódigos backup: {{2fa_1.backupCodes}}"
}
}
},
{
"id": "end_1",
"type": "end",
"position": { "x": 700, "y": 100 },
"data": { "label": "Fim" }
}
],
"edges": [
{ "source": "start_1", "target": "2fa_1" },
{ "source": "2fa_1", "target": "message_1" },
{ "source": "message_1", "target": "end_1" }
]
}
Teste: Gera secret 2FA. Deve retornar secret base32 e 10 códigos de backup.
secret (string, obrigatório para verify_token e generate_qr)
O que é: Secret gerado anteriormente na operação generate_secret, em formato base32.
Flow completo para testar:
{
"name": "Teste 2FA - Secret",
"nodes": [
{
"id": "start_1",
"type": "start",
"position": { "x": 100, "y": 100 },
"data": { "label": "Início" }
},
{
"id": "2fa_1",
"type": "2fa",
"position": { "x": 300, "y": 100 },
"data": {
"label": "Gerar Secret",
"parameters": {
"operation": "generate_secret",
"label": "App Teste"
}
}
},
{
"id": "variable_1",
"type": "variable",
"position": { "x": 500, "y": 100 },
"data": {
"label": "Salvar Secret",
"parameters": {
"name": "meuSecret",
"value": "{{2fa_1.secret}}"
}
}
},
{
"id": "2fa_2",
"type": "2fa",
"position": { "x": 700, "y": 100 },
"data": {
"label": "Gerar QR",
"parameters": {
"operation": "generate_qr",
"secret": "{{meuSecret}}",
"label": "App Teste"
}
}
},
{
"id": "message_1",
"type": "message",
"position": { "x": 900, "y": 100 },
"data": {
"label": "QR Gerado",
"parameters": {
"message": "QR Code criado para o secret!"
}
}
},
{
"id": "end_1",
"type": "end",
"position": { "x": 1100, "y": 100 },
"data": { "label": "Fim" }
}
],
"edges": [
{ "source": "start_1", "target": "2fa_1" },
{ "source": "2fa_1", "target": "variable_1" },
{ "source": "variable_1", "target": "2fa_2" },
{ "source": "2fa_2", "target": "message_1" },
{ "source": "message_1", "target": "end_1" }
]
}
Teste: Gera secret e usa para criar QR code. Deve funcionar perfeitamente.
token (string, obrigatório para verify_token)
O que é: Código de 6 dígitos gerado pelo app autenticador do usuário.
Flow completo para testar:
{
"name": "Teste 2FA - Verify Token",
"nodes": [
{
"id": "start_1",
"type": "start",
"position": { "x": 100, "y": 100 },
"data": { "label": "Início" }
},
{
"id": "2fa_1",
"type": "2fa",
"position": { "x": 300, "y": 100 },
"data": {
"label": "Gerar Secret",
"parameters": {
"operation": "generate_secret",
"label": "Lumina"
}
}
},
{
"id": "message_1",
"type": "message",
"position": { "x": 500, "y": 100 },
"data": {
"label": "Instruções",
"parameters": {
"message": "Configure no app autenticador com secret: {{2fa_1.secret}}"
}
}
},
{
"id": "input_1",
"type": "input",
"position": { "x": 700, "y": 100 },
"data": {
"label": "Digite Código",
"parameters": {
"message": "Digite código de 6 dígitos do app:",
"variableName": "codigo"
}
}
},
{
"id": "2fa_2",
"type": "2fa",
"position": { "x": 900, "y": 100 },
"data": {
"label": "Verificar",
"parameters": {
"operation": "verify_token",
"secret": "{{2fa_1.secret}}",
"token": "{{codigo}}"
}
}
},
{
"id": "message_2",
"type": "message",
"position": { "x": 1100, "y": 100 },
"data": {
"label": "Resultado",
"parameters": {
"message": "Código válido: {{2fa_2.valid}}"
}
}
},
{
"id": "end_1",
"type": "end",
"position": { "x": 1300, "y": 100 },
"data": { "label": "Fim" }
}
],
"edges": [
{ "source": "start_1", "target": "2fa_1" },
{ "source": "2fa_1", "target": "message_1" },
{ "source": "message_1", "target": "input_1" },
{ "source": "input_1", "target": "2fa_2" },
{ "source": "2fa_2", "target": "message_2" },
{ "source": "message_2", "target": "end_1" }
]
}
Teste: Configure no Google Authenticator e digite código. Deve validar corretamente.
label (string, opcional)
O que é: Nome da aplicação exibido no app autenticador.
Padrão: "Default App"
Flow completo para testar:
{
"name": "Teste 2FA - Label",
"nodes": [
{
"id": "start_1",
"type": "start",
"position": { "x": 100, "y": 100 },
"data": { "label": "Início" }
},
{
"id": "input_1",
"type": "input",
"position": { "x": 300, "y": 100 },
"data": {
"label": "Nome App",
"parameters": {
"message": "Digite o nome da aplicação:",
"variableName": "nomeApp"
}
}
},
{
"id": "2fa_1",
"type": "2fa",
"position": { "x": 500, "y": 100 },
"data": {
"label": "Gerar com Label",
"parameters": {
"operation": "generate_secret",
"label": "{{nomeApp}}"
}
}
},
{
"id": "message_1",
"type": "message",
"position": { "x": 700, "y": 100 },
"data": {
"label": "Confirmação",
"parameters": {
"message": "2FA configurado para: {{nomeApp}}"
}
}
},
{
"id": "end_1",
"type": "end",
"position": { "x": 900, "y": 100 },
"data": { "label": "Fim" }
}
],
"edges": [
{ "source": "start_1", "target": "input_1" },
{ "source": "input_1", "target": "2fa_1" },
{ "source": "2fa_1", "target": "message_1" },
{ "source": "message_1", "target": "end_1" }
]
}
Teste: Digite "Minha Empresa". O app autenticador mostrará esse nome.
issuer (string, opcional)
O que é: Nome da organização emissora, exibido junto com label no app autenticador.
Padrão: "Default Issuer"
Flow completo para testar:
{
"name": "Teste 2FA - Issuer",
"nodes": [
{
"id": "start_1",
"type": "start",
"position": { "x": 100, "y": 100 },
"data": { "label": "Início" }
},
{
"id": "2fa_1",
"type": "2fa",
"position": { "x": 300, "y": 100 },
"data": {
"label": "Gerar com Issuer",
"parameters": {
"operation": "generate_secret",
"label": "jose.roberto@lumina.com",
"issuer": "Lumina Corp"
}
}
},
{
"id": "message_1",
"type": "message",
"position": { "x": 500, "y": 100 },
"data": {
"label": "Resultado",
"parameters": {
"message": "2FA criado: Lumina Corp (jose.roberto@lumina.com)"
}
}
},
{
"id": "end_1",
"type": "end",
"position": { "x": 700, "y": 100 },
"data": { "label": "Fim" }
}
],
"edges": [
{ "source": "start_1", "target": "2fa_1" },
{ "source": "2fa_1", "target": "message_1" },
{ "source": "message_1", "target": "end_1" }
]
}
Teste: No app autenticador aparecerá "Lumina Corp (jose.roberto@lumina.com)".
Parâmetros
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
| operation | string | Sim | Operação: generate_secret, verify_token, generate_qr |
| secret | string | Condicional | Secret base32 (obrigatório para verify_token e generate_qr) |
| token | string | Condicional | Código de 6 dígitos (obrigatório para verify_token) |
| label | string | Não | Nome da aplicação (padrão: "Default App") |
| issuer | string | Não | Nome da organização (padrão: "Default Issuer") |
Exemplo 1: Setup Completo de 2FA
Objetivo: Demonstrar configuração completa de autenticação de dois fatores
JSON para Importar
{
"name": "Setup Completo de 2FA",
"nodes": [
{
"id": "start_1",
"type": "start",
"position": { "x": 100, "y": 100 },
"data": { "label": "Início" }
},
{
"id": "input_1",
"type": "input",
"position": { "x": 300, "y": 100 },
"data": {
"label": "Email",
"parameters": {
"message": "Digite seu email:",
"variableName": "email"
}
}
},
{
"id": "2fa_1",
"type": "2fa",
"position": { "x": 500, "y": 100 },
"data": {
"label": "Gerar Secret",
"parameters": {
"operation": "generate_secret",
"label": "{{email}}",
"issuer": "Lumina Flow"
}
}
},
{
"id": "2fa_2",
"type": "2fa",
"position": { "x": 700, "y": 100 },
"data": {
"label": "Gerar QR Code",
"parameters": {
"operation": "generate_qr",
"secret": "{{2fa_1.secret}}",
"label": "{{email}}",
"issuer": "Lumina Flow"
}
}
},
{
"id": "message_1",
"type": "message",
"position": { "x": 900, "y": 100 },
"data": {
"label": "Instruções",
"parameters": {
"message": "1. Abra Google Authenticator\n2. Escaneie QR Code\n3. Salve códigos backup: {{2fa_1.backupCodes}}"
}
}
},
{
"id": "input_2",
"type": "input",
"position": { "x": 1100, "y": 100 },
"data": {
"label": "Confirmar Setup",
"parameters": {
"message": "Digite código do app para confirmar:",
"variableName": "codigoConfirmacao"
}
}
},
{
"id": "2fa_3",
"type": "2fa",
"position": { "x": 1300, "y": 100 },
"data": {
"label": "Verificar Código",
"parameters": {
"operation": "verify_token",
"secret": "{{2fa_1.secret}}",
"token": "{{codigoConfirmacao}}"
}
}
},
{
"id": "condition_1",
"type": "condition",
"position": { "x": 1500, "y": 100 },
"data": {
"label": "Código Válido?",
"parameters": {
"condition": "{{2fa_3.valid}} == true"
}
}
},
{
"id": "message_2",
"type": "message",
"position": { "x": 1700, "y": 50 },
"data": {
"label": "Sucesso",
"parameters": {
"message": "2FA ativado com sucesso!"
}
}
},
{
"id": "message_3",
"type": "message",
"position": { "x": 1700, "y": 150 },
"data": {
"label": "Erro",
"parameters": {
"message": "Código inválido. Tente novamente."
}
}
},
{
"id": "end_1",
"type": "end",
"position": { "x": 1900, "y": 100 },
"data": { "label": "Fim" }
}
],
"edges": [
{ "source": "start_1", "target": "input_1" },
{ "source": "input_1", "target": "2fa_1" },
{ "source": "2fa_1", "target": "2fa_2" },
{ "source": "2fa_2", "target": "message_1" },
{ "source": "message_1", "target": "input_2" },
{ "source": "input_2", "target": "2fa_3" },
{ "source": "2fa_3", "target": "condition_1" },
{ "source": "condition_1", "target": "message_2", "label": "true" },
{ "source": "condition_1", "target": "message_3", "label": "false" },
{ "source": "message_2", "target": "end_1" },
{ "source": "message_3", "target": "end_1" }
]
}
Saída esperada:
Sistema: Digite seu email:
Usuário: jose@lumina.com
Sistema: 1. Abra Google Authenticator
2. Escaneie QR Code
3. Salve códigos backup: [ABC123, DEF456, ...]
Sistema: Digite código do app para confirmar:
Usuário: 123456
Sistema: 2FA ativado com sucesso!
Exemplo 2: Login com 2FA
Objetivo: Demonstrar login com verificação de dois fatores
JSON para Importar
{
"name": "Login com 2FA",
"nodes": [
{
"id": "start_1",
"type": "start",
"position": { "x": 100, "y": 100 },
"data": { "label": "Início" }
},
{
"id": "input_1",
"type": "input",
"position": { "x": 300, "y": 100 },
"data": {
"label": "Username",
"parameters": {
"message": "Username:",
"variableName": "username"
}
}
},
{
"id": "input_2",
"type": "input",
"position": { "x": 500, "y": 100 },
"data": {
"label": "Senha",
"parameters": {
"message": "Senha:",
"variableName": "senha"
}
}
},
{
"id": "auth_1",
"type": "auth",
"position": { "x": 700, "y": 100 },
"data": {
"label": "Autenticar",
"parameters": {
"operation": "login",
"username": "{{username}}",
"password": "{{senha}}"
}
}
},
{
"id": "message_1",
"type": "message",
"position": { "x": 900, "y": 100 },
"data": {
"label": "Solicitar 2FA",
"parameters": {
"message": "Senha correta! Agora digite código 2FA:"
}
}
},
{
"id": "input_3",
"type": "input",
"position": { "x": 1100, "y": 100 },
"data": {
"label": "Código 2FA",
"parameters": {
"message": "Digite código do app:",
"variableName": "codigo2fa"
}
}
},
{
"id": "variable_1",
"type": "variable",
"position": { "x": 1300, "y": 100 },
"data": {
"label": "Secret do Usuário",
"parameters": {
"name": "userSecret",
"value": "JBSWY3DPEHPK3PXP"
}
}
},
{
"id": "2fa_1",
"type": "2fa",
"position": { "x": 1500, "y": 100 },
"data": {
"label": "Verificar 2FA",
"parameters": {
"operation": "verify_token",
"secret": "{{userSecret}}",
"token": "{{codigo2fa}}"
}
}
},
{
"id": "condition_1",
"type": "condition",
"position": { "x": 1700, "y": 100 },
"data": {
"label": "2FA Válido?",
"parameters": {
"condition": "{{2fa_1.valid}} == true"
}
}
},
{
"id": "message_2",
"type": "message",
"position": { "x": 1900, "y": 50 },
"data": {
"label": "Login Sucesso",
"parameters": {
"message": "Login completo! Bem-vindo, {{username}}!"
}
}
},
{
"id": "message_3",
"type": "message",
"position": { "x": 1900, "y": 150 },
"data": {
"label": "2FA Falhou",
"parameters": {
"message": "Código 2FA inválido. Acesso negado."
}
}
},
{
"id": "end_1",
"type": "end",
"position": { "x": 2100, "y": 100 },
"data": { "label": "Fim" }
}
],
"edges": [
{ "source": "start_1", "target": "input_1" },
{ "source": "input_1", "target": "input_2" },
{ "source": "input_2", "target": "auth_1" },
{ "source": "auth_1", "target": "message_1" },
{ "source": "message_1", "target": "input_3" },
{ "source": "input_3", "target": "variable_1" },
{ "source": "variable_1", "target": "2fa_1" },
{ "source": "2fa_1", "target": "condition_1" },
{ "source": "condition_1", "target": "message_2", "label": "true" },
{ "source": "condition_1", "target": "message_3", "label": "false" },
{ "source": "message_2", "target": "end_1" },
{ "source": "message_3", "target": "end_1" }
]
}
Saída esperada:
Sistema: Username:
Usuário: jose.roberto
Sistema: Senha:
Usuário: senha123
Sistema: Senha correta! Agora digite código 2FA:
Sistema: Digite código do app:
Usuário: 234567
Sistema: Login completo! Bem-vindo, jose.roberto!
Exemplo 3: Códigos de Backup
Objetivo: Demonstrar uso de códigos de backup quando usuário perde acesso ao app
JSON para Importar
{
"name": "Usar Código de Backup",
"nodes": [
{
"id": "start_1",
"type": "start",
"position": { "x": 100, "y": 100 },
"data": { "label": "Início" }
},
{
"id": "2fa_1",
"type": "2fa",
"position": { "x": 300, "y": 100 },
"data": {
"label": "Gerar Setup",
"parameters": {
"operation": "generate_secret",
"label": "Usuario"
}
}
},
{
"id": "message_1",
"type": "message",
"position": { "x": 500, "y": 100 },
"data": {
"label": "Mostrar Backups",
"parameters": {
"message": "IMPORTANTE! Salve códigos de backup:\n{{2fa_1.backupCodes}}"
}
}
},
{
"id": "message_2",
"type": "message",
"position": { "x": 700, "y": 100 },
"data": {
"label": "Cenário",
"parameters": {
"message": "Simulando: Usuário perdeu telefone..."
}
}
},
{
"id": "input_1",
"type": "input",
"position": { "x": 900, "y": 100 },
"data": {
"label": "Usar Backup",
"parameters": {
"message": "Digite um código de backup:",
"variableName": "codigoBackup"
}
}
},
{
"id": "message_3",
"type": "message",
"position": { "x": 1100, "y": 100 },
"data": {
"label": "Validado",
"parameters": {
"message": "Código backup aceito! Acesso recuperado.\nConfigure novo 2FA."
}
}
},
{
"id": "end_1",
"type": "end",
"position": { "x": 1300, "y": 100 },
"data": { "label": "Fim" }
}
],
"edges": [
{ "source": "start_1", "target": "2fa_1" },
{ "source": "2fa_1", "target": "message_1" },
{ "source": "message_1", "target": "message_2" },
{ "source": "message_2", "target": "input_1" },
{ "source": "input_1", "target": "message_3" },
{ "source": "message_3", "target": "end_1" }
]
}
Saída esperada:
Sistema: IMPORTANTE! Salve códigos de backup:
[A1B2C3D4, E5F6G7H8, I9J0K1L2, ...]
Sistema: Simulando: Usuário perdeu telefone...
Sistema: Digite um código de backup:
Usuário: A1B2C3D4
Sistema: Código backup aceito! Acesso recuperado.
Configure novo 2FA.
Resposta do Node
Generate Secret:
{
"success": true,
"action": "2fa_secret_generated",
"secret": "JBSWY3DPEHPK3PXP",
"backupCodes": ["A1B2C3D4", "E5F6G7H8", "I9J0K1L2", ...],
"otpauth_url": "otpauth://totp/Lumina%20App?secret=JBSWY3DPEHPK3PXP&issuer=Lumina%20Flow",
"timestamp": "2025-01-15T10:30:00.000Z"
}
Verify Token (válido):
{
"success": true,
"action": "2fa_token_verified",
"valid": true,
"timestamp": "2025-01-15T10:30:00.000Z"
}
Generate QR:
{
"success": true,
"action": "2fa_qr_generated",
"qrCode": "data:image/png;base64,iVBORw0KGgo...",
"otpauth_url": "otpauth://totp/...",
"timestamp": "2025-01-15T10:30:00.000Z"
}
Boas Práticas
✅ SIM:
- Sempre forneça códigos de backup no setup inicial
- Armazene secret 2FA criptografado no banco de dados
- Permita window de 2 períodos (60s) para compensar dessincronia de relógio
- Force 2FA para usuários admin e contas privilegiadas
- Permita desativar 2FA temporariamente com códigos de backup
- Eduque usuários a salvar códigos de backup em local seguro
❌ NÃO:
- Não armazene secret em plain text
- Não permita múltiplas tentativas consecutivas (rate limiting)
- Não force 2FA sem fornecer códigos de backup
- Não use window muito grande (compromete segurança)
- Não compartilhe secret entre usuários
- Não permita desativar 2FA sem verificação adicional
Dicas
💡 Dica 1: Combine 2FA com AUTH para criar fluxo completo de login seguro
💡 Dica 2: Gere QR code e mostre para usuário escanear diretamente no app autenticador
💡 Dica 3: Implemente rate limiting após 3 tentativas falhas de código 2FA
💡 Dica 4: Envie códigos de backup por email criptografado ou SMS após setup
💡 Dica 5: Permita reconfigurar 2FA usando código de backup se usuário perder dispositivo
Próximo Node
→ AUTH - Autenticação e gerenciamento de sessão → TOKEN - Gerenciamento de tokens JWT → OAUTH - OAuth 2.0 flow