Estamos revisando estes procedimentos. Pessoas técnicas podem encontrar ajustes em códigos de erro ou mapeamentos de payload; valide com os logs e o OpenAPI mais recente. Pessoas não técnicas podem continuar usando o passo a passo e acionar o time de suporte caso algo pareça diferente do descrito.
🔑 Fluxo de Recuperação de Senha
Este guia apresenta o fluxo completo para usuários que esqueceram a senha e precisam redefinir o acesso.
📋 Visão Geral
O fluxo de recuperação de senha permite que usuários redefinam suas credenciais de forma segura, passando por validações de identidade.
Este fluxo deve ser iniciado quando:
- Usuário clica em "Esqueci minha senha"
- Após múltiplas tentativas de login incorretas
- Por solicitação explícita do usuário
Passo 1️⃣: Validar Dados do Cliente
Inicie o fluxo de recuperação validando CPF e data de nascimento do usuário.
Endpoint
POST {baseUrl}/identificacao/dados
Content-Type: application/json
Request Body
{
"app": "APP_NAME",
"fluxo": "RECUPERAR_SENHA",
"cliente": {
"cpf": "12345678909",
"dataNascimento": "1990-01-01"
},
"dispositivo": {
"dispositivoId": "device123",
"idSistemaOperacional": "2",
"versaoSistemaOperacional": "12",
"modelo": "Samsung Galaxy",
"versaoApp": "1.0.0"
}
}
Parâmetros
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
app | string | ✅ | Nome do aplicativo configurado |
fluxo | string | ✅ | Fixo: "RECUPERAR_SENHA" |
cliente.cpf | string | ✅ | CPF do cliente (apenas números) |
cliente.dataNascimento | string | ✅ | Data de nascimento (YYYY-MM-DD) |
dispositivo.dispositivoId | string | ✅ | ID único do dispositivo |
dispositivo.idSistemaOperacional | string | ✅ | 1=iOS, 2=Android, 3=Outros |
dispositivo.versaoSistemaOperacional | string | ✅ | Versão do SO |
dispositivo.modelo | string | ✅ | Modelo do dispositivo |
dispositivo.versaoApp | string | ✅ | Versão do aplicativo |
Note que o campo fluxo agora é "RECUPERAR_SENHA" em vez de "PRIMEIRO_ACESSO".
Resposta de Sucesso
{
"access_token": "eyJhbGci...",
"token_type": "Bearer",
"expires_in": 900
}
Este token expira em 15 minutos. Complete o fluxo dentro deste prazo.
Exemplo de Código
async function iniciarRecuperacaoSenha(cpf, dataNascimento, dispositivo) {
const response = await fetch(`${baseUrl}/identificacao/dados`, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
app: 'meu-app-private-label',
fluxo: 'RECUPERAR_SENHA',
cliente: {
cpf: cpf,
dataNascimento: dataNascimento
},
dispositivo: dispositivo
})
});
if (!response.ok) {
const error = await response.json();
if (response.status === 404) {
throw new Error('CPF não encontrado ou data de nascimento incorreta');
}
throw new Error(error.message || 'Erro ao iniciar recuperação de senha');
}
const { access_token } = await response.json();
// Armazene o token de sessão para os próximos passos
sessionStorage.setItem('recovery_token', access_token);
return access_token;
}
Passo 2️⃣: Solicitar Link de Biometria (Opcional)
Se configurado, solicite validação biométrica como método adicional de segurança.
Endpoint
POST {baseUrl}/identificacao/biometria
Authorization: Bearer {token-sessao-cliente}
Content-Type: application/json
Request Body
{
"consumidor": "CONSUMIDOR_ID",
"loja": 123456
}
Exemplo de Código
async function solicitarBiometriaRecuperacao(sessionToken, consumidorId, lojaId) {
const response = await fetch(`${baseUrl}/identificacao/biometria`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${sessionToken}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
consumidor: consumidorId,
loja: lojaId
})
});
if (!response.ok) {
throw new Error('Erro ao solicitar biometria');
}
const { link } = await response.json();
return link;
}
A biometria é opcional e pode ser usada como validação adicional ou alternativa ao SMS.
Passo 3️⃣: Enviar Token SMS
Solicite o envio de um código de verificação por SMS para o telefone cadastrado.
Endpoint
POST {baseUrl}/identificacao/token-sms/envio
Authorization: Bearer {token-sessao-cliente}
Exemplo de Código
async function enviarSMSRecuperacao(sessionToken) {
const response = await fetch(`${baseUrl}/identificacao/token-sms/envio`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${sessionToken}`
}
});
if (!response.ok) {
const error = await response.json();
throw new Error(error.message || 'Erro ao enviar SMS');
}
console.log('📱 SMS enviado com sucesso');
return await response.json();
}
- Código de 6 dígitos
- Válido por 5 minutos
- Enviado para o telefone cadastrado no sistema
- Uso único
Passo 4️⃣: Validar Token SMS
Valide o código de 6 dígitos recebido por SMS.
Endpoint
POST {baseUrl}/identificacao/token-sms
Authorization: Bearer {token-sessao-cliente}
Content-Type: application/json
Request Body
{
"pinCode": "123456",
"analise": {
"userID": "12345678909",
"origem": "APP_NAME",
"deviceID": "device123",
"integrationName": "app-login-pl",
"dadosMaquina": {
"ip": "192.168.0.1",
"fingerPrint": "{fingerprint}"
}
}
}
Exemplo de Código
async function validarSMSRecuperacao(sessionToken, pinCode, dadosDispositivo) {
const response = await fetch(`${baseUrl}/identificacao/token-sms`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${sessionToken}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
pinCode: pinCode,
analise: {
userID: dadosDispositivo.cpf,
origem: 'meu-app-private-label',
deviceID: dadosDispositivo.dispositivoId,
integrationName: 'app-login-pl',
dadosMaquina: {
ip: dadosDispositivo.ip,
fingerPrint: dadosDispositivo.fingerprint
}
}
})
});
if (!response.ok) {
const error = await response.json();
if (response.status === 422) {
throw new Error('Código SMS inválido');
}
if (response.status === 410) {
throw new Error('Código SMS expirado. Solicite um novo código.');
}
throw new Error(error.message || 'Erro ao validar SMS');
}
return await response.json();
}
O objeto analise é usado para detecção de fraudes. Envie informações precisas do dispositivo.
Passo 5️⃣: Buscar Termo de Aceite
Obtenha o texto do termo de uso atualizado que o usuário deve aceitar.
Endpoint
GET {baseUrl}/contratos/termo-aceite
Authorization: Bearer {token-sessao-cliente}
Exemplo de Código
async function buscarTermoRecuperacao(sessionToken) {
const response = await fetch(`${baseUrl}/contratos/termo-aceite`, {
headers: {
'Authorization': `Bearer ${sessionToken}`
}
});
if (!response.ok) {
throw new Error('Erro ao buscar termo de aceite');
}
const { termo } = await response.json();
return termo;
}
Exemplo de Resposta
{
"termo": {
"id": 123,
"versao": "2.1",
"titulo": "Termos de Uso e Política de Privacidade",
"conteudo": "Texto completo do termo...",
"dataPublicacao": "2025-01-01T00:00:00Z"
}
}
Passo 6️⃣: Aceitar Termo
Registre a aceitação do termo pelo usuário.
Endpoint
PUT {baseUrl}/contratos/termo-aceite
Authorization: Bearer {token-sessao-cliente}
Content-Type: application/json
Request Body
{
"aceite": true
}
Exemplo de Código
async function aceitarTermoRecuperacao(sessionToken) {
const response = await fetch(`${baseUrl}/contratos/termo-aceite`, {
method: 'PUT',
headers: {
'Authorization': `Bearer ${sessionToken}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
aceite: true
})
});
if (!response.ok) {
throw new Error('Erro ao aceitar termo');
}
return await response.json();
}
Sempre apresente o termo completo ao usuário e obtenha consentimento explícito antes de enviar o aceite.
Passo 7️⃣: Definir Nova Senha
Finalize o processo criando uma nova senha para o usuário.
Endpoint
POST {baseUrl}/usuario/acesso
Authorization: Bearer {token-sessao-cliente}
Content-Type: application/json
Request Body
{
"senha": "NovaSenhaSegura123"
}
Exemplo de Código
async function definirNovaSenha(sessionToken, novaSenha, confirmacaoSenha) {
// Validar senhas
if (novaSenha !== confirmacaoSenha) {
throw new Error('As senhas não coincidem');
}
if (!validarForcaSenha(novaSenha)) {
throw new Error('A senha não atende aos requisitos de segurança');
}
const response = await fetch(`${baseUrl}/usuario/acesso`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${sessionToken}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
senha: novaSenha
})
});
if (!response.ok) {
const error = await response.json();
throw new Error(error.message || 'Erro ao definir nova senha');
}
return await response.json();
}
function validarForcaSenha(senha) {
// Mínimo 8 caracteres
if (senha.length < 8) return false;
// Pelo menos 1 letra maiúscula
if (!/[A-Z]/.test(senha)) return false;
// Pelo menos 1 letra minúscula
if (!/[a-z]/.test(senha)) return false;
// Pelo menos 1 número
if (!/[0-9]/.test(senha)) return false;
// Pelo menos 1 caractere especial
if (!/[!@#$%^&*(),.?":{}|<>]/.test(senha)) return false;
return true;
}
O usuário já pode fazer login com a nova senha definida.
Requisitos obrigatórios para senha segura:
- Mínimo de 8 caracteres
- Pelo menos 1 letra maiúscula
- Pelo menos 1 letra minúscula
- Pelo menos 1 número
- Pelo menos 1 caractere especial (!@#$%^&*)
🔄 Fluxo Completo - Exemplo
async function fluxoRecuperacaoSenhaCompleto(dadosCliente, dadosDispositivo) {
try {
// Passo 1: Validar dados
console.log('🔍 Validando dados do cliente...');
const sessionToken = await iniciarRecuperacaoSenha(
dadosCliente.cpf,
dadosCliente.dataNascimento,
dadosDispositivo
);
exibirMensagem('Dados validados com sucesso');
// Passo 2: Biometria (opcional)
// const linkBiometria = await solicitarBiometriaRecuperacao(sessionToken, consumidorId, lojaId);
// Passo 3: Enviar SMS
console.log('📱 Enviando código SMS...');
await enviarSMSRecuperacao(sessionToken);
exibirMensagem('Código SMS enviado para seu telefone cadastrado');
// Aguardar usuário digitar código
const pinCode = await solicitarCodigoSMS();
// Passo 4: Validar SMS
console.log('✔️ Validando código SMS...');
await validarSMSRecuperacao(sessionToken, pinCode, dadosDispositivo);
exibirMensagem('Código validado com sucesso');
// Passo 5: Buscar termo
console.log('📄 Buscando termo de aceite...');
const termo = await buscarTermoRecuperacao(sessionToken);
// Exibir termo ao usuário
await exibirTermo(termo);
// Aguardar aceite
const aceitou = await aguardarAceiteUsuario();
if (!aceitou) {
throw new Error('Usuário recusou o termo de aceite');
}
// Passo 6: Aceitar termo
console.log('✅ Registrando aceite do termo...');
await aceitarTermoRecuperacao(sessionToken);
// Passo 7: Criar nova senha
console.log('🔑 Aguardando nova senha...');
const { novaSenha, confirmacaoSenha } = await solicitarNovaSenha();
await definirNovaSenha(sessionToken, novaSenha, confirmacaoSenha);
console.log('🎉 Senha recuperada com sucesso!');
exibirSucesso('Senha alterada com sucesso! Você já pode fazer login.');
// Limpar sessão temporária
sessionStorage.removeItem('recovery_token');
// Redirecionar para tela de login
setTimeout(() => {
navegarParaLogin();
}, 2000);
} catch (error) {
console.error('❌ Erro na recuperação de senha:', error);
if (error.message.includes('CPF não encontrado')) {
exibirErro('CPF não encontrado ou data de nascimento incorreta.');
} else if (error.message.includes('Código SMS inválido')) {
exibirErro('Código incorreto. Tente novamente.');
} else if (error.message.includes('Token expirado')) {
exibirErro('Tempo expirado. Por favor, inicie o processo novamente.');
navegarParaRecuperarSenha();
} else {
exibirErro('Erro ao recuperar senha. Tente novamente mais tarde.');
}
throw error;
}
}
🎨 Interface do Usuário - Exemplos
1. Tela Inicial de Recuperação
function TelaRecuperarSenha() {
const [cpf, setCpf] = useState('');
const [dataNascimento, setDataNascimento] = useState('');
const [carregando, setCarregando] = useState(false);
const handleRecuperar = async () => {
setCarregando(true);
try {
await iniciarRecuperacaoSenha(cpf, dataNascimento, obterDadosDispositivo());
navegarParaValidacaoSMS();
} catch (error) {
Alert.alert('Erro', error.message);
} finally {
setCarregando(false);
}
};
return (
<View style={styles.container}>
<Text style={styles.title}>Recuperar Senha</Text>
<Text style={styles.subtitle}>
Digite seus dados para recuperar o acesso à sua conta
</Text>
<Input
label="CPF"
value={cpf}
onChangeText={formatarCPF}
keyboardType="numeric"
maxLength={14}
placeholder="000.000.000-00"
/>
<Input
label="Data de Nascimento"
value={dataNascimento}
onChangeText={formatarData}
keyboardType="numeric"
maxLength={10}
placeholder="DD/MM/AAAA"
/>
<Button
title="Continuar"
onPress={handleRecuperar}
loading={carregando}
disabled={!cpf || !dataNascimento}
/>
<Link onPress={() => navegarParaLogin()}>
Voltar para login
</Link>
</View>
);
}
2. Validação de SMS com Timer
function TelaValidacaoSMS() {
const [codigo, setCodigo] = useState('');
const [tempoRestante, setTempoRestante] = useState(300); // 5 minutos
const [podeReenviar, setPodeReenviar] = useState(false);
useEffect(() => {
const timer = setInterval(() => {
setTempoRestante(prev => {
if (prev <= 1) {
setPodeReenviar(true);
return 0;
}
return prev - 1;
});
}, 1000);
return () => clearInterval(timer);
}, []);
const formatarTempo = (segundos) => {
const min = Math.floor(segundos / 60);
const seg = segundos % 60;
return `${min}:${seg.toString().padStart(2, '0')}`;
};
const handleReenviar = async () => {
try {
await enviarSMSRecuperacao(sessionToken);
setTempoRestante(300);
setPodeReenviar(false);
Alert.alert('Sucesso', 'Novo código enviado!');
} catch (error) {
Alert.alert('Erro', error.message);
}
};
return (
<View style={styles.container}>
<Icon name="message" size={64} color="#4CAF50" />
<Text style={styles.title}>Código SMS</Text>
<Text style={styles.subtitle}>
Digite o código de 6 dígitos enviado para seu telefone
</Text>
<CodeInput
value={codigo}
onChangeText={setCodigo}
cellCount={6}
autoFocus
/>
<View style={styles.timerContainer}>
{tempoRestante > 0 ? (
<Text>Código expira em {formatarTempo(tempoRestante)}</Text>
) : (
<Text style={styles.expired}>Código expirado</Text>
)}
</View>
{podeReenviar ? (
<Button
title="Reenviar código"
onPress={handleReenviar}
variant="outline"
/>
) : (
<Text style={styles.hint}>
Não recebeu? Aguarde para reenviar
</Text>
)}
<Button
title="Validar"
onPress={() => validarCodigo(codigo)}
disabled={codigo.length !== 6}
/>
</View>
);
}
3. Definição de Nova Senha com Validação Visual
function TelaNovaSenha() {
const [senha, setSenha] = useState('');
const [confirmacao, setConfirmacao] = useState('');
const [mostrarSenha, setMostrarSenha] = useState(false);
const validacoes = {
tamanho: senha.length >= 8,
maiuscula: /[A-Z]/.test(senha),
minuscula: /[a-z]/.test(senha),
numero: /[0-9]/.test(senha),
especial: /[!@#$%^&*(),.?":{}|<>]/.test(senha),
coincide: senha === confirmacao && senha.length > 0
};
const senhaValida = Object.values(validacoes).every(v => v);
return (
<View style={styles.container}>
<Text style={styles.title}>Criar Nova Senha</Text>
<Text style={styles.subtitle}>
Escolha uma senha forte para proteger sua conta
</Text>
<Input
label="Nova senha"
value={senha}
onChangeText={setSenha}
secureTextEntry={!mostrarSenha}
rightIcon={
<IconButton
icon={mostrarSenha ? 'eye-off' : 'eye'}
onPress={() => setMostrarSenha(!mostrarSenha)}
/>
}
/>
<Input
label="Confirmar senha"
value={confirmacao}
onChangeText={setConfirmacao}
secureTextEntry={!mostrarSenha}
/>
<View style={styles.requisitos}>
<Text style={styles.requisitosTitle}>Requisitos da senha:</Text>
<RequisitoItem
atendido={validacoes.tamanho}
texto="Mínimo 8 caracteres"
/>
<RequisitoItem
atendido={validacoes.maiuscula}
texto="Pelo menos 1 letra maiúscula"
/>
<RequisitoItem
atendido={validacoes.minuscula}
texto="Pelo menos 1 letra minúscula"
/>
<RequisitoItem
atendido={validacoes.numero}
texto="Pelo menos 1 número"
/>
<RequisitoItem
atendido={validacoes.especial}
texto="Pelo menos 1 caractere especial"
/>
<RequisitoItem
atendido={validacoes.coincide}
texto="Senhas coincidem"
/>
</View>
<Button
title="Salvar Nova Senha"
onPress={() => salvarNovaSenha(senha)}
disabled={!senhaValida}
/>
</View>
);
}
function RequisitoItem({ atendido, texto }) {
return (
<View style={styles.requisito}>
<Icon
name={atendido ? 'check-circle' : 'circle'}
size={20}
color={atendido ? '#4CAF50' : '#999'}
/>
<Text style={[
styles.requisitoTexto,
atendido && styles.requisitoAtendido
]}>
{texto}
</Text>
</View>
);
}
⚠️ Tratamento de Erros
| Código | Descrição | Ação Recomendada |
|---|---|---|
| 400 | Dados inválidos | Verificar CPF e data de nascimento |
| 401 | Token expirado | Reiniciar fluxo de recuperação |
| 404 | Cliente não encontrado | Verificar CPF ou sugerir primeiro acesso |
| 410 | Código SMS expirado | Solicitar reenvio de código |
| 422 | Código SMS inválido | Permitir nova tentativa (máx 3) |
| 429 | Muitas tentativas | Aguardar 15 minutos |
Exemplo de Tratamento
function tratarErroRecuperacao(error) {
if (error.status === 404) {
return {
titulo: 'CPF não encontrado',
mensagem: 'Verifique se o CPF está correto ou faça seu primeiro acesso.',
acao: 'Ir para Primeiro Acesso'
};
}
if (error.status === 422) {
return {
titulo: 'Código incorreto',
mensagem: 'O código digitado está incorreto. Tente novamente.',
acao: 'Tentar Novamente'
};
}
if (error.status === 429) {
return {
titulo: 'Muitas tentativas',
mensagem: 'Por segurança, aguarde 15 minutos antes de tentar novamente.',
acao: 'Voltar'
};
}
return {
titulo: 'Erro',
mensagem: 'Não foi possível recuperar a senha. Tente novamente mais tarde.',
acao: 'Fechar'
};
}
🔒 Segurança
1. Limite de Tentativas
// Implementar limite de tentativas de recuperação
class GerenciadorRecuperacao {
constructor() {
this.tentativasPorCPF = new Map();
this.maxTentativas = 5;
this.periodoLimite = 24 * 60 * 60 * 1000; // 24 horas
}
async verificarLimite(cpf) {
const agora = Date.now();
const tentativas = this.tentativasPorCPF.get(cpf) || [];
// Remover tentativas antigas
const tentativasRecentes = tentativas.filter(
timestamp => (agora - timestamp) < this.periodoLimite
);
if (tentativasRecentes.length >= this.maxTentativas) {
throw new Error(
'Limite de tentativas excedido. Tente novamente em 24 horas.'
);
}
// Registrar nova tentativa
tentativasRecentes.push(agora);
this.tentativasPorCPF.set(cpf, tentativasRecentes);
}
}
2. Log de Auditoria
// Registrar todas as tentativas de recuperação
async function registrarTentativaRecuperacao(cpf, sucesso, motivo) {
await fetch(`${baseUrl}/auditoria/recuperacao-senha`, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
cpf: cpf,
timestamp: new Date().toISOString(),
sucesso: sucesso,
motivo: motivo,
dispositivo: await obterDadosDispositivo()
})
});
}
📚 Recursos Relacionados
- Login - Autenticação de usuários
- Primeiro Acesso - Cadastro de novos usuários
- Troca de Dispositivo - Autorizar novo dispositivo
Última atualização: 03 de Dezembro de 2025