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 Troca de Dispositivo
Este guia apresenta o fluxo para autorizar o acesso de um usuário em um novo dispositivo.
📋 Visão Geral
Quando um usuário precisa acessar sua conta em um novo dispositivo, é necessário validar a segurança da operação através de verificações adicionais.
Este fluxo é ativado automaticamente quando o sistema detecta:
- Login de um dispositivo não reconhecido
- Primeiro acesso após troca/reset de dispositivo
- Dispositivo diferente do último login registrado
Passo 1️⃣: Fazer Login no Novo Dispositivo
Realize o login normalmente com as credenciais do usuário.
Endpoint
POST https://ssohml.credsystem.com.br/auth/realms/{realm}/protocol/openid-connect/token
Content-Type: application/x-www-form-urlencoded
Parâmetros da Requisição
Importante: Envie as informações do novo dispositivo no parâmetro infosAdicionais.
curl -X POST 'https://ssohml.credsystem.com.br/auth/realms/{realm}/protocol/openid-connect/token' \
-H 'Content-Type: application/x-www-form-urlencoded' \
-d 'client_id=YOUR_CLIENT_ID' \
-d 'client_secret=YOUR_CLIENT_SECRET' \
-d 'username=12345678909' \
-d 'password=SenhaDoUsuario' \
-d 'grant_type=password' \
-d 'infosAdicionais={"dispositivoId":"novo-device-456","modelo":"iPhone 14 Pro","idSistemaOperacional":"1","versaoSistemaOperacional":"17.2","versaoApp":"2.1.0"}'
Exemplo em JavaScript
async function loginNovoDispositivo(cpf, senha, novoDispositivo) {
const response = await fetch(
'https://ssohml.credsystem.com.br/auth/realms/{realm}/protocol/openid-connect/token',
{
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
},
body: new URLSearchParams({
client_id: 'YOUR_CLIENT_ID',
client_secret: 'YOUR_CLIENT_SECRET',
username: cpf,
password: senha,
grant_type: 'password',
infosAdicionais: JSON.stringify({
dispositivoId: novoDispositivo.id,
modelo: novoDispositivo.modelo,
idSistemaOperacional: novoDispositivo.idSO,
versaoSistemaOperacional: novoDispositivo.versaoSO,
versaoApp: novoDispositivo.versaoApp
})
})
}
);
const data = await response.json();
// Se o dispositivo não é reconhecido, será retornado um token de sessão
// temporário que exige validação adicional
return data;
}
Resposta - Dispositivo Não Reconhecido
Quando o sistema detecta um novo dispositivo, retorna um token de sessão temporário:
{
"access_token": "eyJhbGci...",
"token_type": "Bearer",
"expires_in": 900,
"requires_device_validation": true,
"validation_methods": ["SMS", "BIOMETRIA"]
}
O flag requires_device_validation: true indica que validações adicionais são necessárias antes do acesso completo.
Passo 2️⃣: Solicitar Link de Biometria (Opcional)
Se configurado, você pode solicitar validação biométrica como método alternativo ou adicional.
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 solicitarBiometriaNovoDispositivo(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();
// Abra o link para captura biométrica
return link;
}
A validação biométrica é opcional e depende da configuração do aplicativo. Pode ser usada como método único ou em conjunto com 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 enviarSMSTrocaDispositivo(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');
}
const data = await response.json();
console.log('SMS enviado com sucesso');
return data;
}
- Código de 6 dígitos
- Válido por 5 minutos
- Enviado para o telefone cadastrado
- Uso único
Passo 4️⃣: Validar Token SMS
Valide o código SMS recebido pelo usuário.
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": "novo-device-456",
"integrationName": "app-login-pl",
"dadosMaquina": {
"ip": "192.168.0.1",
"fingerPrint": "{fingerprint}"
}
}
}
Parâmetros
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
pinCode | string | ✅ | Código de 6 dígitos recebido por SMS |
analise.userID | string | ✅ | CPF do usuário |
analise.origem | string | ✅ | Nome do aplicativo |
analise.deviceID | string | ✅ | ID do novo dispositivo |
analise.integrationName | string | ✅ | Nome da integração |
analise.dadosMaquina.ip | string | ✅ | Endereço IP do novo dispositivo |
analise.dadosMaquina.fingerPrint | string | ✅ | Fingerprint do novo dispositivo |
Exemplo de Código
async function validarSMSNovoDispositivo(sessionToken, pinCode, novoDispositivo) {
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: novoDispositivo.cpf,
origem: 'meu-app-private-label',
deviceID: novoDispositivo.id,
integrationName: 'app-login-pl',
dadosMaquina: {
ip: novoDispositivo.ip,
fingerPrint: novoDispositivo.fingerprint
}
}
})
});
if (!response.ok) {
const error = await response.json();
throw new Error(error.message || 'Código SMS inválido');
}
const data = await response.json();
// Após validação bem-sucedida, retorna tokens de acesso completos
return data;
}
Resposta de Sucesso
{
"access_token": "eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldU...",
"refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCIgOiAiSldU...",
"expires_in": 300,
"refresh_expires_in": 1800,
"token_type": "Bearer",
"device_validated": true
}
Após a validação bem-sucedida, o novo dispositivo estará autorizado e o usuário poderá acessar o app normalmente com os tokens retornados.
🔄 Fluxo Completo - Exemplo
async function fluxoTrocaDispositivoCompleto(credenciais, novoDispositivo) {
try {
// Passo 1: Login no novo dispositivo
console.log('🔑 Fazendo login no novo dispositivo...');
const loginResponse = await loginNovoDispositivo(
credenciais.cpf,
credenciais.senha,
novoDispositivo
);
// Verificar se precisa validação
if (!loginResponse.requires_device_validation) {
console.log('✅ Dispositivo já autorizado');
return loginResponse;
}
console.log('🔐 Dispositivo novo detectado. Validação necessária.');
const sessionToken = loginResponse.access_token;
// Passo 2: Biometria (opcional)
if (loginResponse.validation_methods.includes('BIOMETRIA')) {
const usarBiometria = await perguntarUsuario('Deseja usar biometria?');
if (usarBiometria) {
const linkBiometria = await solicitarBiometriaNovoDispositivo(
sessionToken,
credenciais.consumidorId,
credenciais.lojaId
);
await abrirBiometria(linkBiometria);
// Se biometria for aprovada, pode pular SMS
return;
}
}
// Passo 3: Enviar SMS
console.log('📱 Enviando código SMS...');
await enviarSMSTrocaDispositivo(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...');
const tokensFinais = await validarSMSNovoDispositivo(
sessionToken,
pinCode,
novoDispositivo
);
// Salvar tokens e informações do novo dispositivo
await salvarTokens(tokensFinais);
await salvarDispositivoAutorizado(novoDispositivo);
console.log('✅ Novo dispositivo autorizado com sucesso!');
// Navegar para tela principal
navegarParaHome();
return tokensFinais;
} catch (error) {
console.error('❌ Erro na troca de dispositivo:', error);
if (error.message.includes('Código SMS inválido')) {
exibirErro('Código incorreto. Tente novamente ou solicite um novo código.');
} else if (error.message.includes('Token expirado')) {
exibirErro('Tempo expirado. Por favor, faça login novamente.');
navegarParaLogin();
} else {
exibirErro('Erro ao validar dispositivo. Tente novamente mais tarde.');
}
throw error;
}
}
🔒 Segurança e Boas Práticas
1. Identificação Única do Dispositivo
// Gerar ID único e persistente do dispositivo
async function obterDispositivoId() {
// Android
if (Platform.OS === 'android') {
return await DeviceInfo.getUniqueId();
}
// iOS - Use UUID armazenado no Keychain
if (Platform.OS === 'ios') {
let uuid = await Keychain.getGenericPassword({ service: 'deviceId' });
if (!uuid) {
uuid = UUID.v4();
await Keychain.setGenericPassword('deviceId', uuid, { service: 'deviceId' });
}
return uuid;
}
}
2. Fingerprint do Dispositivo
// Coletar informações para fingerprint
async function gerarFingerprint() {
const info = {
brand: await DeviceInfo.getBrand(),
model: await DeviceInfo.getModel(),
systemName: await DeviceInfo.getSystemName(),
systemVersion: await DeviceInfo.getSystemVersion(),
deviceId: await DeviceInfo.getUniqueId(),
appVersion: await DeviceInfo.getVersion(),
buildNumber: await DeviceInfo.getBuildNumber(),
carrier: await DeviceInfo.getCarrier(),
timezone: Intl.DateTimeFormat().resolvedOptions().timeZone
};
// Criar hash do fingerprint
return btoa(JSON.stringify(info));
}
3. Gerenciamento de Dispositivos Múltiplos
// Armazenar lista de dispositivos autorizados
async function salvarDispositivoAutorizado(dispositivo) {
const dispositivos = await obterDispositivosAutorizados();
// Adicionar novo dispositivo
dispositivos.push({
id: dispositivo.id,
modelo: dispositivo.modelo,
dataAutorizacao: new Date().toISOString(),
ultimoAcesso: new Date().toISOString()
});
// Limitar a 3 dispositivos mais recentes
if (dispositivos.length > 3) {
dispositivos.sort((a, b) =>
new Date(b.ultimoAcesso) - new Date(a.ultimoAcesso)
);
dispositivos.splice(3);
}
await AsyncStorage.setItem('dispositivos_autorizados', JSON.stringify(dispositivos));
}
// Verificar se dispositivo já está autorizado
async function dispositivoJaAutorizado(dispositivoId) {
const dispositivos = await obterDispositivosAutorizados();
return dispositivos.some(d => d.id === dispositivoId);
}
4. Limite de Tentativas
// Controlar tentativas de validação SMS
class ValidadorSMS {
constructor() {
this.tentativas = 0;
this.maxTentativas = 3;
this.bloqueadoAte = null;
}
async validar(codigo) {
// Verificar se está bloqueado
if (this.bloqueadoAte && new Date() < this.bloqueadoAte) {
const minutosRestantes = Math.ceil(
(this.bloqueadoAte - new Date()) / 60000
);
throw new Error(
`Muitas tentativas. Aguarde ${minutosRestantes} minutos.`
);
}
try {
const resultado = await validarSMSNovoDispositivo(codigo);
this.tentativas = 0;
return resultado;
} catch (error) {
this.tentativas++;
if (this.tentativas >= this.maxTentativas) {
// Bloquear por 15 minutos
this.bloqueadoAte = new Date(Date.now() + 15 * 60 * 1000);
throw new Error(
'Limite de tentativas excedido. Aguarde 15 minutos.'
);
}
throw error;
}
}
async reenviarSMS() {
if (this.bloqueadoAte && new Date() < this.bloqueadoAte) {
throw new Error('Aguarde antes de solicitar novo código');
}
// Resetar tentativas ao reenviar
this.tentativas = 0;
return await enviarSMSTrocaDispositivo();
}
}
⚠️ Tratamento de Erros
| Código | Descrição | Ação Recomendada |
|---|---|---|
| 401 | Credenciais inválidas | Verificar CPF e senha |
| 403 | Dispositivo bloqueado | Contatar suporte |
| 422 | Código SMS inválido | Permitir nova tentativa (máx 3) |
| 429 | Muitas tentativas | Bloquear temporariamente (15 min) |
| 500 | Erro no servidor | Tentar novamente mais tarde |
Exemplo de Tratamento
function tratarErroTrocaDispositivo(error) {
switch (error.status) {
case 401:
return 'CPF ou senha incorretos';
case 403:
return 'Dispositivo bloqueado. Entre em contato com o suporte.';
case 422:
return 'Código SMS inválido. Tente novamente.';
case 429:
return 'Muitas tentativas. Aguarde 15 minutos.';
default:
return 'Erro ao validar dispositivo. Tente novamente mais tarde.';
}
}
💡 Dicas de UX
1. Mensagens Claras
// Informar usuário sobre novo dispositivo
function exibirMensagemNovoDispositivo() {
return (
<Alert>
<AlertIcon name="shield" />
<AlertTitle>Novo dispositivo detectado</AlertTitle>
<AlertDescription>
Por segurança, precisamos validar este dispositivo.
Você receberá um código por SMS no número cadastrado.
</AlertDescription>
</Alert>
);
}
2. Timer Visual para Reenvio
function ComponenteCodigoSMS() {
const [tempoRestante, setTempoRestante] = useState(60);
const [podeReenviar, setPodeReenviar] = useState(false);
useEffect(() => {
if (tempoRestante > 0) {
const timer = setTimeout(() => {
setTempoRestante(tempoRestante - 1);
}, 1000);
return () => clearTimeout(timer);
} else {
setPodeReenviar(true);
}
}, [tempoRestante]);
return (
<View>
<Text>Digite o código recebido por SMS</Text>
<Input onChangeText={setCodigo} maxLength={6} keyboardType="numeric" />
{podeReenviar ? (
<Button onPress={reenviarCodigo}>Reenviar código</Button>
) : (
<Text>Reenviar em {tempoRestante}s</Text>
)}
</View>
);
}
3. Lista de Dispositivos Autorizados
// Mostrar dispositivos autorizados ao usuário
function TelaDispositivos() {
const [dispositivos, setDispositivos] = useState([]);
useEffect(() => {
carregarDispositivos();
}, []);
return (
<View>
<Text>Dispositivos Autorizados</Text>
{dispositivos.map(dispositivo => (
<Card key={dispositivo.id}>
<CardTitle>{dispositivo.modelo}</CardTitle>
<CardDescription>
Autorizado em {formatarData(dispositivo.dataAutorizacao)}
</CardDescription>
<CardDescription>
Último acesso: {formatarData(dispositivo.ultimoAcesso)}
</CardDescription>
{dispositivo.id === dispositivoAtual && (
<Badge>Este dispositivo</Badge>
)}
<Button onPress={() => revogarDispositivo(dispositivo.id)}>
Remover
</Button>
</Card>
))}
</View>
);
}
📚 Recursos Relacionados
- Login - Autenticação de usuários
- Primeiro Acesso - Cadastro de novos usuários
- Recuperar Senha - Redefinir senha esquecida
Última atualização: 03 de Dezembro de 2025