Pular para o conteúdo principal
ℹ️ Conteúdo em atualização

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.

Quando usar

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

CampoTipoObrigatórioDescrição
appstringNome do aplicativo configurado
fluxostringFixo: "RECUPERAR_SENHA"
cliente.cpfstringCPF do cliente (apenas números)
cliente.dataNascimentostringData de nascimento (YYYY-MM-DD)
dispositivo.dispositivoIdstringID único do dispositivo
dispositivo.idSistemaOperacionalstring1=iOS, 2=Android, 3=Outros
dispositivo.versaoSistemaOperacionalstringVersão do SO
dispositivo.modelostringModelo do dispositivo
dispositivo.versaoAppstringVersão do aplicativo
🔄 Diferença do Primeiro Acesso

Note que o campo fluxo agora é "RECUPERAR_SENHA" em vez de "PRIMEIRO_ACESSO".

Resposta de Sucesso

200 OK
{
"access_token": "eyJhbGci...",
"token_type": "Bearer",
"expires_in": 900
}
⏱️ Atenção

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;
}

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;
}
📸 Biometria

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();
}
📱 Informações do SMS
  • 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();
}
🛡️ Análise de Fraude

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();
}
⚖️ Legal

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;
}
🎉 Senha Recuperada!

O usuário já pode fazer login com a nova senha definida.

🔐 Política de Senhas

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ódigoDescriçãoAção Recomendada
400Dados inválidosVerificar CPF e data de nascimento
401Token expiradoReiniciar fluxo de recuperação
404Cliente não encontradoVerificar CPF ou sugerir primeiro acesso
410Código SMS expiradoSolicitar reenvio de código
422Código SMS inválidoPermitir nova tentativa (máx 3)
429Muitas tentativasAguardar 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


Última atualização: 03 de Dezembro de 2025