@neofaceid/web-sdk
v2.6.0
Published
NeoFaceId Web SDK for facial authentication
Maintainers
Readme
NeoFace ID SDK Web
SDK JavaScript/TypeScript (React) para autenticação, cadastro e verificação
facial em aplicações web, publicado no npm como
@neofaceid/web-sdk.
Veja o CHANGELOG.md para o histórico de versões.
Índice
- Instalação
- Requisitos do consumer
- Início rápido
- LGPD e consentimento
- Fluxos principais
- Login facial (
startFaceLogin) - Login por mão / automático
- Cadastro biométrico (
startBiometricRegistration) - Captura de liveness (
startLivenessCapture) - Onboarding completo (
startOnboarding) - Captura de documento (
startDocumentCapture) - Autorização por biometria (
authorize/authorizeOperation) - Prova de vida, captura de frames e operações avulsas
- Login facial (
- Tratamento de erros
- Componentes React exportados
- Sessão de captura e desafio de liveness (baixo nível)
- Trilha de consentimento (auditoria LGPD)
- API de baixo nível (
api.ts) - Versão do SDK
- Compatibilidade
- Licença
Instalação
npm install @neofaceid/web-sdkreact e react-dom são peerDependencies (^18.0.0 || ^19.0.0) — o
projeto consumidor precisa já ter as duas instaladas.
Requisitos do consumer
Antes de usar qualquer fluxo do SDK, confirme os itens abaixo no projeto que vai consumi-lo:
HTTPS. O SDK acessa a câmera via
getUserMedia, que exige contexto seguro.localhostfunciona em desenvolvimento; produção precisa de HTTPS (recomendado configurar HSTS no servidor).O detector facial (
@vladmandic/face-api, fork mantido doface-api.js) já vem embutido no@neofaceid/web-sdk, rodando sobre o TF.js 4.x que o SDK instala como dependência (não precisa instalar nada) — mas os pesos do modelo (weights) não são publicados no bundle e precisam ser auto-hospedados pelo consumer. O SDK carrega os modelos em runtime viafaceapi.nets.<rede>.loadFromUri('/models'), ou seja, relativos à própria origem do seu app. Baixe os arquivos abaixo do repositório oficial de pesos do face-api.js (o fork usa os mesmos arquivos) e sirva-os empublic/models/(ou equivalente do seu bundler) para que fiquem acessíveis em/models/*:tiny_face_detector_model-weights_manifest.json+tiny_face_detector_model-shard1face_landmark_68_model-weights_manifest.json+face_landmark_68_model-shard1
Sem esses arquivos em
/models, qualquer fluxo que dependa de detecção facial local (blink detection do liveness,biometricDetection) falha ao carregar o modelo. Se o manifest não for encontrado (404), o console mostra umconsole.erroracionável, com os 4 arquivos e o destino:[NeoFace ID SDK] Modelos de detecção facial não encontrados em /models/. Copie os 4 arquivos abaixo para public/models/ (ou equivalente do seu bundler): - tiny_face_detector_model-weights_manifest.json - tiny_face_detector_model-shard1 - face_landmark_68_model-weights_manifest.json - face_landmark_68_model-shard1 Fonte: https://github.com/vladmandic/face-api/tree/master/model Sem esses arquivos em /models, o fluxo de detecção facial falhará silenciosamente.Ícone/logo do SDK não precisa de asset externo. Desde a 1.29.0 a marca é renderizada via SVG inline (
src/assets/brand.ts) — não há mais PNG para hospedar.applicationTokenválido, obtido no painel administrativo do NeoFace ID.Ambiente correto configurado via
init— veja Início rápido. O default (seminit) ésandbox.
Início rápido
import { init } from '@neofaceid/web-sdk';
init({
environment: 'sandbox', // 'development' | 'sandbox' | 'production'
applicationToken: 'seu-token-de-aplicacao',
appName: 'Minha Aplicação', // exibido no topo dos modais
consent: {
purpose: 'authentication',
legalBasis: 'fraud_prevention', // 'consent' | 'legal_obligation' | 'fraud_prevention'
privacyPolicyUrl: 'https://minha-app.com/privacidade',
retentionDays: 0,
},
});Mapeamento de environment → URL base (src/config.ts):
| environment | URL base |
| -------------- | ------------------------------------ |
| development | http://localhost:8000 |
| sandbox | https://sandbox-core.neofaceid.com |
| production | https://core.neofaceid.com.br |
Use baseUrl em vez de environment para apontar para uma URL customizada
(tem precedência sobre environment). Outras opções de init — tema visual
do integrador:
init({
accent: '#0059C4', // cor do botão primário; contraste < 4.5 vs branco = fallback + warn
radius: 16, // 8 | 16 | 24, fallback 16 se fora da lista
theme: 'auto', // 'light' | 'dark' | 'auto' (respeita prefers-color-scheme)
locale: 'pt-BR',
});Getters correspondentes: getBaseUrl(), getEnvironment(),
getApplicationToken(), getConsent(), isInitialized(), getConfig(),
getAppName(), getAccent(), getRadius(), getTheme(), getLocale(),
getResolvedThemeMode(), e o mapa ENVIRONMENT_URLS.
LGPD e consentimento
Todo fluxo que abre a câmera (start, startBiometricRegistration,
startLivenessCapture, startFaceLogin, startHandLogin, startAutoLogin,
startOnboarding, startDocumentCapture, authorizeOperation, authorize)
chama internamente requestConsent({ appName, flow }) antes de qualquer
getUserMedia(). Se o titular recusar, o callback de erro recebe
NeoFaceError com ErrorType.CONSENT_DENIED e a câmera nunca é aberta.
Configure a base legal uma vez em init({ consent }) (ver
Início rápido) — omitir gera console.warn e usa defaults
conservadores (fraud_prevention, sem URL de política, retenção
indefinida). Também é possível chamar requestConsent diretamente para UI
customizada:
import { requestConsent, DEFAULT_CONSENT_INFO } from '@neofaceid/web-sdk';
const accepted = await requestConsent({
...DEFAULT_CONSENT_INFO,
appName: 'Minha Aplicação',
flow: 'registration', // 'verification' (imagem descartada) | 'registration' (guarda vetor matemático)
});Toda decisão de consentimento pode ser auditada — veja Trilha de consentimento.
Fluxos principais
Login facial (startFaceLogin)
Overlay minimalista estilo Face ID (não mostra a câmera na tela).
import { startFaceLogin } from '@neofaceid/web-sdk';
await startFaceLogin({
applicationToken: 'seu-token-de-aplicacao',
appName: 'Minha Aplicação',
onSuccess: result => console.log('Login ok:', result),
onError: err => console.error(err.type, err.getFriendlyMessage()),
onAttemptError: (err, attempt) => analytics.track('biometric_attempt_failed', { attempt, type: err.type }),
onCancel: () => console.log('Usuário cancelou'),
onFallbackRequest: () => {
/* opcional — default já abre EmailPasswordModal */
},
});
onError×onAttemptError(BiometricLoginOptions, NEO-563):onErroré chamado quando o fluxo guiado termina — usuário cancelou, escolheu email/senha, ou erro fora do loop de tentativas (ex.:CONSENT_DENIEDantes da câmera abrir). JáonAttemptError(opcional) é chamado a cada tentativa individual que falha dentro do loop (401, 429, rosto não detectado, etc.), antes de oFallbackPromptinterno aparecer — útil para registrar cada falha no seu próprio sistema de monitoramento sem esperar o fluxo encerrar.
Login por mão / automático (startHandLogin, startAutoLogin)
Mesma assinatura de startFaceLogin (BiometricLoginOptions); startAutoLogin
detecta rosto ou mão automaticamente.
import { startHandLogin, startAutoLogin } from '@neofaceid/web-sdk';
await startHandLogin({ applicationToken, onSuccess, onError });
await startAutoLogin({ applicationToken, onSuccess, onError });Cadastro biométrico (startBiometricRegistration)
import { startBiometricRegistration } from '@neofaceid/web-sdk';
startBiometricRegistration(
{
name: 'Maria Silva',
birth_date: '1990-05-20',
cpf: '12345678900',
email: '[email protected]',
password: 'senha-forte',
},
'seu-token-de-aplicacao',
{
onSuccess: result => console.log('Cadastro concluído:', result.person_id),
onError: err => console.error(err.type, err.message),
},
{ appName: 'Minha Aplicação' }
);Captura de liveness (startLivenessCapture)
Só a captura de fotos com prova de vida, sem cadastro — útil quando o consumer já tem seu próprio pipeline de reconhecimento.
import { startLivenessCapture } from '@neofaceid/web-sdk';
startLivenessCapture(
'seu-token-de-aplicacao',
{
onSuccess: (photos: Blob[]) => console.log(`${photos.length} fotos capturadas`),
onError: err => console.error(err.type, err.message),
onCancel: () => console.log('Usuário fechou o modal'),
},
{ appName: 'Minha Aplicação' }
);Onboarding completo (startOnboarding)
Orquestra: validação do link → consentimento → captura de rosto e documento → conclusão no backend.
import { startOnboarding } from '@neofaceid/web-sdk';
await startOnboarding({
applicationToken: 'seu-token-de-aplicacao',
onboardingToken: 'token-do-link-de-onboarding',
appName: 'Minha Aplicação',
onSuccess: result => console.log('Onboarding concluído:', result.person_id),
onError: err => console.error(err.type, err.message),
onCancel: () => console.log('Usuário cancelou'),
});Captura de documento (startDocumentCapture)
import { startDocumentCapture } from '@neofaceid/web-sdk';
const result = await startDocumentCapture({
appName: 'Minha Aplicação',
preSelectedDocument: 'CNH', // 'RG' | 'CNH' | 'CPF' — pula a tela de seleção
useBackCamera: true,
});
if (result.success) {
console.log('Frente:', result.frontImage);
console.log('Verso:', result.backImage); // null se o documento não tem verso
}Autorização por biometria (authorize / authorizeOperation)
authorize é a API atual (protocolo Focus Frame, orquestra
runLivenessChallenge — o servidor decide a sequência de gestos e a
aprovação final):
import { authorize } from '@neofaceid/web-sdk';
await authorize({
applicationToken: 'seu-token-de-aplicacao',
subject: '12345678900', // CPF, e-mail ou id opaco do titular
appName: 'Minha Aplicação',
amount: 1250, // exibido como "R$ 1.250,00"
challenges: 2, // 0-3, teto sugerido; servidor decide a sequência real
onChallengeStart: (gesture, index, total) => console.log(gesture, index, total),
onSuccess: collectResult => {
// envie collectResult.capturedGestures ao seu próprio endpoint de recognition
},
onError: err => console.error(err.type, err.message),
onCancel: () => console.log('Usuário cancelou'),
});authorizeOperation é a variante legada, mais baixo nível (captura 4 fotos
com gestos fixos e chama recognizeByPurpose('AUTHORIZATION') diretamente):
import { authorizeOperation } from '@neofaceid/web-sdk';
const result = await authorizeOperation('seu-token-de-aplicacao', '12345678900', {
appName: 'Minha Aplicação',
onProgress: (step, total, instruction) => console.log(instruction, step, total),
onPhotoTaken: (n, blob) => console.log('Foto', n, blob),
});Prova de vida, captura de frames e operações avulsas
Operações que não abrem um fluxo start* completo. Até a 1.x viviam na
classe NeoFaceID; desde a 2.0.0 são funções, como o resto do SDK — ver a
tabela de migração no CHANGELOG.
import {
proofOfLife,
captureFaceFrames,
recognizeByPurpose,
registerDocumentByImage,
} from '@neofaceid/web-sdk';
// Prova de vida: grava vídeo curto, envia ao core e acompanha até o resultado.
// `applicationToken` pode ser omitido se já foi passado a init().
const proof = await proofOfLife({
applicationToken: 'seu-token-de-aplicacao',
videoDurationMs: 3000,
onRecordingProgress: progress => console.log(`Gravando: ${progress}%`),
onTaskStatusChange: (status, progress) => console.log(status, progress),
});
// Reprovar na prova é desfecho, não exceção: confira `success` e `isLive`.
if (proof.success && proof.isLive) {
console.log('Liveness score:', proof.livenessScore);
console.log('Dados da pessoa:', proof.personalData);
}
// Login com assinatura (integrações externas que já geram HMAC)
const [frame] = await captureFaceFrames({ numFrames: 1, livenessCheck: false });
const login = await recognizeByPurpose(
frame,
'seu-token-de-aplicacao',
'LOGIN',
0.8, // limiar de confiança
signatureFromBackend, // HMAC-SHA256 hex, mínimo 64 caracteres
{ email: '[email protected]', cpf: '12345678900', sessionId: 'session-uuid' }
);
// Registro de documento para pessoa já cadastrada
const docResult = await registerDocumentByImage(personId, userJwtToken);
console.log('Task ID:', docResult.taskId);Tratamento de erros
Toda falha do SDK (nos fluxos novos) é uma instância de NeoFaceError, com
error.type (ErrorType) e error.getFriendlyMessage() para exibir ao
usuário final sem termos técnicos:
import { NeoFaceError, ErrorType } from '@neofaceid/web-sdk';
function onError(err: NeoFaceError) {
if (err.type === ErrorType.CONSENT_DENIED) {
// usuário recusou o consentimento — não abriu câmera
}
alert(err.getFriendlyMessage());
}Desde a 1.41.0,
start,startBiometricRegistrationestartLivenessCapturetambém usamonError(err: NeoFaceError)— antes usavamonError(code: string, message: string). Veja oCHANGELOG.md(breaking change) se seu código ainda depende da assinatura antiga.
| ErrorType | Quando ocorre |
| ----------------------- | --------------------------------------------------------------- |
| NETWORK | Erro de conexão com o servidor |
| INVALID_TOKEN | applicationToken inválido ou expirado |
| RECOGNITION_FAILED | Falha no reconhecimento facial |
| LOGIN_FAILED | Falha no login biométrico |
| VALIDATION_ERROR | Dados inválidos ou rosto não detectado corretamente |
| API_ERROR | Erro genérico de API (400/500) |
| PERSON_NOT_FOUND | Pessoa não encontrada na base |
| INITIALIZATION_ERROR | Falha ao inicializar um fluxo |
| CAMERA_ERROR | Erro genérico de câmera |
| NO_CAMERA | Câmera não disponível ou permissão negada |
| CAPTURE_ERROR | Falha durante a captura de imagem/vídeo |
| NOT_FOUND | Recurso não encontrado |
| UNKNOWN | Erro não categorizado |
| CONSENT_DENIED | Titular recusou o consentimento LGPD |
| LIVENESS_BLINK_MISSING| Piscada real (EAR) não detectada na janela de liveness (3s) |
Componentes React exportados
Para quem prefere montar a própria árvore React em vez de usar as funções
start* (que já cuidam de criar/desmontar o container):
import {
FaceCaptureModal,
BiometricRegistrationModal,
ConsentModal,
EmailPasswordModal,
ForgotPasswordModal,
BiometricStatusOverlay,
FallbackPrompt,
DocumentCaptureModal,
} from '@neofaceid/web-sdk';Todos recebem accessToken/applicationToken, onSuccess/onError e
onClose conforme o fluxo — consulte a assinatura de cada start*
correspondente acima para as props equivalentes.
Sessão de captura e desafio de liveness (baixo nível)
Usado internamente por authorize/authorizeOperation, mas exportado para
quem monta o próprio orquestrador de liveness:
import {
openCaptureSession,
requestLivenessChallenge,
runLivenessChallenge,
} from '@neofaceid/web-sdk';
const session = await openCaptureSession({
applicationToken: 'seu-token-de-aplicacao',
purpose: 'authorization', // 'login' | 'onboarding' | 'authorization' | 'identification' | 'liveness'
});
const challenge = await requestLivenessChallenge({
applicationToken: 'seu-token-de-aplicacao',
sessionId: session.session_id,
});
// Ou o fluxo completo (session → challenge → coleta via callback → retorno):
const collected = await runLivenessChallenge({
applicationToken: 'seu-token-de-aplicacao',
purpose: 'login',
collectFramesForGesture: async (gesture, index, total) => {
// capture o(s) frame(s) correspondente(s) ao gesto e retorne os Blobs
return [] as Blob[];
},
});Trilha de consentimento (auditoria LGPD)
import { recordConsent, getConsentTrail, clearConsentTrail } from '@neofaceid/web-sdk';
// Grava no localStorage apenas hash(userAgent+purpose+timestamp), timestamp,
// finalidade, base legal e versão do SDK — zero PII. Usa por padrão o
// `consent` configurado em `init()`; pode ser sobrescrito pontualmente:
await recordConsent({ purpose: 'authentication', legalBasis: 'fraud_prevention' });
const trail = getConsentTrail(); // ConsentRecord[] — histórico local (máx. 100, FIFO)
clearConsentTrail(); // limpa o histórico (ex.: logout)API de baixo nível (api.ts)
Funções que os fluxos start* já usam por baixo dos panos, expostas para
integrações que precisam de controle fino sobre a chamada HTTP:
import {
validateToken,
validateOnboardingToken,
recognize,
recognizeBiometric,
simpleIdentification,
recognizeByPurpose,
loginWithBiometric,
loginWithEmail,
registerPersonWithoutFace,
registerPersonWithBiometric,
registerBiometric,
identifyPerson,
identifyPersonAsync,
checkUserExistence,
requestPasswordReset,
confirmPasswordReset,
completeOnboarding,
completeOnboardingWithData,
} from '@neofaceid/web-sdk';
await validateToken('seu-token-de-aplicacao'); // boolean
await validateOnboardingToken('seu-token-de-aplicacao', 'token-do-onboarding'); // boolean
await recognize(faceBlob, 'seu-token-de-aplicacao');
await recognizeBiometric(faceBlob, 'seu-token-de-aplicacao', /* livenessCheck */ true, 0.8);
await simpleIdentification('CPF', '12345678900', 'seu-token-de-aplicacao');
await recognizeByPurpose(faceBlob, 'seu-token-de-aplicacao', 'LOGIN', 0.8);
await loginWithBiometric(faceBlob, 'seu-token-de-aplicacao');
await loginWithEmail('[email protected]', 'senha', 'seu-token-de-aplicacao');
await registerPersonWithoutFace(
{ name: 'Maria Silva', birth_date: '1990-05-20', cpf: '12345678900', email: '[email protected]', password: 'senha-forte' },
'seu-token-de-aplicacao'
);
await registerPersonWithBiometric(
{ name: 'Maria Silva', birth_date: '1990-05-20', cpf: '12345678900', email: '[email protected]', password: 'senha-forte' },
[faceBlob1, faceBlob2],
'seu-token-de-aplicacao'
);
await registerBiometric(personId, faceBlob, 'seu-token-de-aplicacao');
await identifyPerson(faceBlob, 'seu-token-de-aplicacao');
await identifyPersonAsync(faceBlob, 'seu-token-de-aplicacao', { maxRetries: 5, interval: 1000 });
await checkUserExistence({ email: '[email protected]' }); // ou { cpf }
await requestPasswordReset('[email protected]', 'seu-token-de-aplicacao');
await confirmPasswordReset('token-do-email', 'nova-senha');
await completeOnboarding('seu-token-de-aplicacao', 'token-do-onboarding', faceBlob, documentBlob);
await completeOnboardingWithData(
'seu-token-de-aplicacao',
'token-do-onboarding',
{ name: 'Maria Silva', cpf: '12345678900' },
[faceBlob1, faceBlob2],
documentBlob
);Utilitário de performance para pré-carregar os modelos de detecção facial
antes do primeiro uso (evita o delay do primeiro loadFromUri acontecer
durante a interação do usuário):
import { preloadFaceDetectionModels } from '@neofaceid/web-sdk';
preloadFaceDetectionModels(); // dispara o carregamento de /models em backgroundVersão do SDK
import { VERSION, RELEASE_DATE } from '@neofaceid/web-sdk';
console.log(`NeoFace ID SDK ${VERSION} (${RELEASE_DATE})`);Histórico completo em CHANGELOG.md.
Compatibilidade
Navegadores suportados
- Chrome 60+
- Firefox 55+
- Safari 11+
- Edge 79+
Responsividade
O SDK é totalmente responsivo. Em dispositivos móveis, os modais ocupam a tela inteira.
Licença
MIT
