@neofaceid/web-sdk 1.40.2 → 1.40.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +461 -242
- package/dist/index.d.ts +1 -1
- package/dist/neoface-id-sdk.es.js +1 -1
- package/dist/neoface-id-sdk.umd.js +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,6 +1,34 @@
|
|
|
1
1
|
# NeoFace ID SDK Web
|
|
2
2
|
|
|
3
|
-
SDK
|
|
3
|
+
SDK JavaScript/TypeScript (React) para autenticação, cadastro e verificação
|
|
4
|
+
facial em aplicações web, publicado no npm como
|
|
5
|
+
[`@neofaceid/web-sdk`](https://www.npmjs.com/package/@neofaceid/web-sdk).
|
|
6
|
+
|
|
7
|
+
Veja o [`CHANGELOG.md`](CHANGELOG.md) para o histórico de versões.
|
|
8
|
+
|
|
9
|
+
## Índice
|
|
10
|
+
|
|
11
|
+
- [Instalação](#instalação)
|
|
12
|
+
- [Requisitos do consumer](#requisitos-do-consumer)
|
|
13
|
+
- [Início rápido](#início-rápido)
|
|
14
|
+
- [LGPD e consentimento](#lgpd-e-consentimento)
|
|
15
|
+
- [Fluxos principais](#fluxos-principais)
|
|
16
|
+
- [Login facial (`startFaceLogin`)](#login-facial-startfacelogin)
|
|
17
|
+
- [Login por mão / automático](#login-por-mão--automático-starthandlogin-startautologin)
|
|
18
|
+
- [Cadastro biométrico (`startBiometricRegistration`)](#cadastro-biométrico-startbiometricregistration)
|
|
19
|
+
- [Captura de liveness (`startLivenessCapture`)](#captura-de-liveness-startlivenesscapture)
|
|
20
|
+
- [Onboarding completo (`startOnboarding`)](#onboarding-completo-startonboarding)
|
|
21
|
+
- [Captura de documento (`startDocumentCapture`)](#captura-de-documento-startdocumentcapture)
|
|
22
|
+
- [Autorização por biometria (`authorize` / `authorizeOperation`)](#autorização-por-biometria-authorize--authorizeoperation)
|
|
23
|
+
- [Classe `NeoFaceID` (`proofOfLife` e afins)](#classe-neofaceid-proofoflife-e-afins)
|
|
24
|
+
- [Tratamento de erros](#tratamento-de-erros)
|
|
25
|
+
- [Componentes React exportados](#componentes-react-exportados)
|
|
26
|
+
- [Sessão de captura e desafio de liveness (baixo nível)](#sessão-de-captura-e-desafio-de-liveness-baixo-nível)
|
|
27
|
+
- [Trilha de consentimento (auditoria LGPD)](#trilha-de-consentimento-auditoria-lgpd)
|
|
28
|
+
- [API de baixo nível (`api.ts`)](#api-de-baixo-nível-apits)
|
|
29
|
+
- [Versão do SDK](#versão-do-sdk)
|
|
30
|
+
- [Compatibilidade](#compatibilidade)
|
|
31
|
+
- [Licença](#licença)
|
|
4
32
|
|
|
5
33
|
## Instalação
|
|
6
34
|
|
|
@@ -8,318 +36,509 @@ SDK para captura e reconhecimento facial em aplicações web.
|
|
|
8
36
|
npm install @neofaceid/web-sdk
|
|
9
37
|
```
|
|
10
38
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
39
|
+
`react` e `react-dom` são `peerDependencies` (`^18.0.0 || ^19.0.0`) — o
|
|
40
|
+
projeto consumidor precisa já ter as duas instaladas.
|
|
41
|
+
|
|
42
|
+
## Requisitos do consumer
|
|
43
|
+
|
|
44
|
+
Antes de usar qualquer fluxo do SDK, confirme os itens abaixo no projeto que
|
|
45
|
+
vai consumi-lo:
|
|
46
|
+
|
|
47
|
+
1. **HTTPS.** O SDK acessa a câmera via `getUserMedia`, que exige contexto
|
|
48
|
+
seguro. `localhost` funciona em desenvolvimento; produção precisa de
|
|
49
|
+
HTTPS (recomendado configurar HSTS no servidor).
|
|
50
|
+
2. **`face-api.js` já vem como dependência transitiva** do
|
|
51
|
+
`@neofaceid/web-sdk` (não precisa instalar de novo) — mas os **pesos do
|
|
52
|
+
modelo (weights) não são publicados no bundle** e precisam ser
|
|
53
|
+
auto-hospedados pelo consumer. O SDK carrega os modelos em runtime via
|
|
54
|
+
`faceapi.nets.<rede>.loadFromUri('/models')`, ou seja, relativos à própria
|
|
55
|
+
origem do seu app. Baixe os arquivos abaixo do repositório oficial de
|
|
56
|
+
pesos do face-api.js e sirva-os em `public/models/` (ou equivalente do seu
|
|
57
|
+
bundler) para que fiquem acessíveis em `/models/*`:
|
|
58
|
+
- `tiny_face_detector_model-weights_manifest.json` +
|
|
59
|
+
`tiny_face_detector_model-shard1`
|
|
60
|
+
- `face_landmark_68_model-weights_manifest.json` +
|
|
61
|
+
`face_landmark_68_model-shard1`
|
|
62
|
+
|
|
63
|
+
Sem esses arquivos em `/models`, qualquer fluxo que dependa de detecção
|
|
64
|
+
facial local (blink detection do liveness, `biometricDetection`) falha ao
|
|
65
|
+
carregar o modelo.
|
|
66
|
+
3. **Ícone/logo do SDK não precisa de asset externo.** Desde a 1.29.0 a marca
|
|
67
|
+
é renderizada via SVG inline (`src/assets/brand.ts`) — não há mais PNG
|
|
68
|
+
para hospedar.
|
|
69
|
+
4. **`applicationToken`** válido, obtido no painel administrativo do
|
|
70
|
+
NeoFace ID.
|
|
71
|
+
5. **Ambiente correto configurado via `init`** — veja
|
|
72
|
+
[Início rápido](#início-rápido). O default (sem `init`) é `sandbox`.
|
|
73
|
+
|
|
74
|
+
## Início rápido
|
|
75
|
+
|
|
76
|
+
```ts
|
|
77
|
+
import { init } from '@neofaceid/web-sdk';
|
|
78
|
+
|
|
79
|
+
init({
|
|
80
|
+
environment: 'sandbox', // 'development' | 'sandbox' | 'production'
|
|
81
|
+
applicationToken: 'seu-token-de-aplicacao',
|
|
82
|
+
appName: 'Minha Aplicação', // exibido no topo dos modais
|
|
83
|
+
consent: {
|
|
84
|
+
purpose: 'authentication',
|
|
85
|
+
legalBasis: 'fraud_prevention', // 'consent' | 'legal_obligation' | 'fraud_prevention'
|
|
86
|
+
privacyPolicyUrl: 'https://minha-app.com/privacidade',
|
|
87
|
+
retentionDays: 0,
|
|
21
88
|
},
|
|
22
|
-
onError: (code, message) => {
|
|
23
|
-
console.error('Erro:', code, message);
|
|
24
|
-
}
|
|
25
89
|
});
|
|
26
90
|
```
|
|
27
91
|
|
|
28
|
-
|
|
92
|
+
Mapeamento de `environment` → URL base ([`src/config.ts`](src/config.ts)):
|
|
29
93
|
|
|
30
|
-
|
|
94
|
+
| `environment` | URL base |
|
|
95
|
+
| -------------- | ------------------------------------ |
|
|
96
|
+
| `development` | `http://localhost:8000` |
|
|
97
|
+
| `sandbox` | `https://sandbox-core.neofaceid.com` |
|
|
98
|
+
| `production` | `https://core.neofaceid.com.br` |
|
|
31
99
|
|
|
32
|
-
|
|
33
|
-
|
|
100
|
+
Use `baseUrl` em vez de `environment` para apontar para uma URL customizada
|
|
101
|
+
(tem precedência sobre `environment`). Outras opções de `init` — tema visual
|
|
102
|
+
do integrador:
|
|
34
103
|
|
|
35
|
-
|
|
104
|
+
```ts
|
|
105
|
+
init({
|
|
106
|
+
accent: '#0059C4', // cor do botão primário; contraste < 4.5 vs branco = fallback + warn
|
|
107
|
+
radius: 16, // 8 | 16 | 24, fallback 16 se fora da lista
|
|
108
|
+
theme: 'auto', // 'light' | 'dark' | 'auto' (respeita prefers-color-scheme)
|
|
109
|
+
locale: 'pt-BR',
|
|
110
|
+
});
|
|
36
111
|
```
|
|
37
112
|
|
|
38
|
-
|
|
113
|
+
Getters correspondentes: `getBaseUrl()`, `getEnvironment()`,
|
|
114
|
+
`getApplicationToken()`, `getConsent()`, `isInitialized()`, `getConfig()`,
|
|
115
|
+
`getAppName()`, `getAccent()`, `getRadius()`, `getTheme()`, `getLocale()`,
|
|
116
|
+
`getResolvedThemeMode()`, e o mapa `ENVIRONMENT_URLS`.
|
|
117
|
+
|
|
118
|
+
## LGPD e consentimento
|
|
119
|
+
|
|
120
|
+
Todo fluxo que abre a câmera (`start`, `startBiometricRegistration`,
|
|
121
|
+
`startLivenessCapture`, `startFaceLogin`, `startHandLogin`, `startAutoLogin`,
|
|
122
|
+
`startOnboarding`, `startDocumentCapture`, `authorizeOperation`, `authorize`)
|
|
123
|
+
chama internamente `requestConsent({ appName, flow })` **antes** de qualquer
|
|
124
|
+
`getUserMedia()`. Se o titular recusar, o callback de erro recebe
|
|
125
|
+
`NeoFaceError` com `ErrorType.CONSENT_DENIED` e a câmera nunca é aberta.
|
|
126
|
+
|
|
127
|
+
Configure a base legal uma vez em `init({ consent })` (ver
|
|
128
|
+
[Início rápido](#início-rápido)) — omitir gera `console.warn` e usa defaults
|
|
129
|
+
conservadores (`fraud_prevention`, sem URL de política, retenção
|
|
130
|
+
indefinida). Também é possível chamar `requestConsent` diretamente para UI
|
|
131
|
+
customizada:
|
|
132
|
+
|
|
133
|
+
```ts
|
|
134
|
+
import { requestConsent, DEFAULT_CONSENT_INFO } from '@neofaceid/web-sdk';
|
|
135
|
+
|
|
136
|
+
const accepted = await requestConsent({
|
|
137
|
+
...DEFAULT_CONSENT_INFO,
|
|
138
|
+
appName: 'Minha Aplicação',
|
|
139
|
+
flow: 'registration', // 'verification' (imagem descartada) | 'registration' (guarda vetor matemático)
|
|
140
|
+
});
|
|
141
|
+
```
|
|
39
142
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
| 1.0.0 | 2023-06-15 | Lançamento inicial do SDK |
|
|
143
|
+
Toda decisão de consentimento pode ser auditada — veja
|
|
144
|
+
[Trilha de consentimento](#trilha-de-consentimento-auditoria-lgpd).
|
|
43
145
|
|
|
44
|
-
##
|
|
146
|
+
## Fluxos principais
|
|
45
147
|
|
|
46
|
-
###
|
|
47
|
-
Token de autenticação da sua aplicação. Obtenha este token no painel de administração do NeoFace ID.
|
|
148
|
+
### Login facial (`startFaceLogin`)
|
|
48
149
|
|
|
49
|
-
|
|
150
|
+
Overlay minimalista estilo Face ID (não mostra a câmera na tela).
|
|
50
151
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
- Parâmetros: `user` (object) - Contém `name`, `email` e `documentId` do usuário reconhecido.
|
|
152
|
+
```ts
|
|
153
|
+
import { startFaceLogin } from '@neofaceid/web-sdk';
|
|
54
154
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
155
|
+
await startFaceLogin({
|
|
156
|
+
applicationToken: 'seu-token-de-aplicacao',
|
|
157
|
+
appName: 'Minha Aplicação',
|
|
158
|
+
onSuccess: result => console.log('Login ok:', result),
|
|
159
|
+
onError: err => console.error(err.type, err.getFriendlyMessage()),
|
|
160
|
+
onCancel: () => console.log('Usuário cancelou'),
|
|
161
|
+
onFallbackRequest: () => {
|
|
162
|
+
/* opcional — default já abre EmailPasswordModal */
|
|
163
|
+
},
|
|
164
|
+
});
|
|
165
|
+
```
|
|
60
166
|
|
|
61
|
-
###
|
|
167
|
+
### Login por mão / automático (`startHandLogin`, `startAutoLogin`)
|
|
62
168
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
| `TOKEN_VALIDATION_ERROR` | Token de aplicação inválido ou expirado |
|
|
66
|
-
| `NO_CAMERA` | Câmera não disponível ou permissão negada |
|
|
67
|
-
| `FACE_NOT_DETECTED` | Nenhum rosto detectado na imagem |
|
|
68
|
-
| `MULTIPLE_FACES` | Múltiplos rostos detectados na imagem |
|
|
69
|
-
| `FACE_NOT_CENTERED` | Rosto não está centralizado no frame |
|
|
70
|
-
| `LIVENESS_CHECK_FAILED` | Verificação de vivacidade falhou |
|
|
71
|
-
| `RECOGNITION_FAILED` | Falha no reconhecimento facial |
|
|
72
|
-
| `NETWORK_ERROR` | Erro de conexão com o servidor |
|
|
73
|
-
| `TIMEOUT` | Tempo limite excedido durante o processo |
|
|
169
|
+
Mesma assinatura de `startFaceLogin` (`BiometricLoginOptions`); `startAutoLogin`
|
|
170
|
+
detecta rosto ou mão automaticamente.
|
|
74
171
|
|
|
75
|
-
|
|
172
|
+
```ts
|
|
173
|
+
import { startHandLogin, startAutoLogin } from '@neofaceid/web-sdk';
|
|
76
174
|
|
|
77
|
-
|
|
175
|
+
await startHandLogin({ applicationToken, onSuccess, onError });
|
|
176
|
+
await startAutoLogin({ applicationToken, onSuccess, onError });
|
|
177
|
+
```
|
|
78
178
|
|
|
79
|
-
|
|
80
|
-
/* Container principal */
|
|
81
|
-
#neoface-modal-container {
|
|
82
|
-
/* Estilos para o container */
|
|
83
|
-
}
|
|
179
|
+
### Cadastro biométrico (`startBiometricRegistration`)
|
|
84
180
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
/* Estilos para o modal */
|
|
88
|
-
}
|
|
181
|
+
```ts
|
|
182
|
+
import { startBiometricRegistration } from '@neofaceid/web-sdk';
|
|
89
183
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
184
|
+
startBiometricRegistration(
|
|
185
|
+
{
|
|
186
|
+
name: 'Maria Silva',
|
|
187
|
+
birth_date: '1990-05-20',
|
|
188
|
+
cpf: '12345678900',
|
|
189
|
+
email: 'maria@exemplo.com',
|
|
190
|
+
password: 'senha-forte',
|
|
191
|
+
},
|
|
192
|
+
'seu-token-de-aplicacao',
|
|
193
|
+
{
|
|
194
|
+
onSuccess: result => console.log('Cadastro concluído:', result.person_id),
|
|
195
|
+
onError: (code, message) => console.error(code, message),
|
|
196
|
+
},
|
|
197
|
+
{ appName: 'Minha Aplicação' }
|
|
198
|
+
);
|
|
199
|
+
```
|
|
94
200
|
|
|
95
|
-
|
|
96
|
-
#neoface-modal-container .message {
|
|
97
|
-
/* Estilos para as mensagens */
|
|
98
|
-
}
|
|
201
|
+
### Captura de liveness (`startLivenessCapture`)
|
|
99
202
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
203
|
+
Só a captura de fotos com prova de vida, sem cadastro — útil quando o
|
|
204
|
+
consumer já tem seu próprio pipeline de reconhecimento.
|
|
205
|
+
|
|
206
|
+
```ts
|
|
207
|
+
import { startLivenessCapture } from '@neofaceid/web-sdk';
|
|
208
|
+
|
|
209
|
+
startLivenessCapture(
|
|
210
|
+
'seu-token-de-aplicacao',
|
|
211
|
+
{
|
|
212
|
+
onSuccess: (photos: Blob[]) => console.log(`${photos.length} fotos capturadas`),
|
|
213
|
+
onError: (code, message) => console.error(code, message),
|
|
214
|
+
onCancel: () => console.log('Usuário fechou o modal'),
|
|
215
|
+
},
|
|
216
|
+
{ appName: 'Minha Aplicação' }
|
|
217
|
+
);
|
|
104
218
|
```
|
|
105
219
|
|
|
106
|
-
|
|
220
|
+
### Onboarding completo (`startOnboarding`)
|
|
107
221
|
|
|
108
|
-
|
|
109
|
-
|
|
222
|
+
Orquestra: validação do link → consentimento → captura de rosto e
|
|
223
|
+
documento → conclusão no backend.
|
|
110
224
|
|
|
111
|
-
|
|
112
|
-
|
|
225
|
+
```ts
|
|
226
|
+
import { startOnboarding } from '@neofaceid/web-sdk';
|
|
113
227
|
|
|
114
|
-
|
|
115
|
-
|
|
228
|
+
await startOnboarding({
|
|
229
|
+
applicationToken: 'seu-token-de-aplicacao',
|
|
230
|
+
onboardingToken: 'token-do-link-de-onboarding',
|
|
231
|
+
appName: 'Minha Aplicação',
|
|
232
|
+
onSuccess: result => console.log('Onboarding concluído:', result.person_id),
|
|
233
|
+
onError: err => console.error(err.type, err.message),
|
|
234
|
+
onCancel: () => console.log('Usuário cancelou'),
|
|
235
|
+
});
|
|
236
|
+
```
|
|
116
237
|
|
|
117
|
-
###
|
|
118
|
-
O SDK não armazena dados biométricos localmente. Todas as imagens são processadas em tempo real e descartadas após o reconhecimento.
|
|
238
|
+
### Captura de documento (`startDocumentCapture`)
|
|
119
239
|
|
|
120
|
-
|
|
240
|
+
```ts
|
|
241
|
+
import { startDocumentCapture } from '@neofaceid/web-sdk';
|
|
121
242
|
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
243
|
+
const result = await startDocumentCapture({
|
|
244
|
+
appName: 'Minha Aplicação',
|
|
245
|
+
preSelectedDocument: 'CNH', // 'RG' | 'CNH' | 'CPF' — pula a tela de seleção
|
|
246
|
+
useBackCamera: true,
|
|
247
|
+
});
|
|
125
248
|
|
|
126
|
-
|
|
249
|
+
if (result.success) {
|
|
250
|
+
console.log('Frente:', result.frontImage);
|
|
251
|
+
console.log('Verso:', result.backImage); // null se o documento não tem verso
|
|
252
|
+
}
|
|
253
|
+
```
|
|
127
254
|
|
|
128
|
-
###
|
|
129
|
-
- Chrome 60+
|
|
130
|
-
- Firefox 55+
|
|
131
|
-
- Safari 11+
|
|
132
|
-
- Edge 79+
|
|
255
|
+
### Autorização por biometria (`authorize` / `authorizeOperation`)
|
|
133
256
|
|
|
134
|
-
|
|
135
|
-
|
|
257
|
+
`authorize` é a API atual (protocolo Focus Frame, orquestra
|
|
258
|
+
`runLivenessChallenge` — o servidor decide a sequência de gestos e a
|
|
259
|
+
aprovação final):
|
|
136
260
|
|
|
137
|
-
|
|
261
|
+
```ts
|
|
262
|
+
import { authorize } from '@neofaceid/web-sdk';
|
|
138
263
|
|
|
139
|
-
|
|
264
|
+
await authorize({
|
|
265
|
+
applicationToken: 'seu-token-de-aplicacao',
|
|
266
|
+
subject: '12345678900', // CPF, e-mail ou id opaco do titular
|
|
267
|
+
appName: 'Minha Aplicação',
|
|
268
|
+
amount: 1250, // exibido como "R$ 1.250,00"
|
|
269
|
+
challenges: 2, // 0-3, teto sugerido; servidor decide a sequência real
|
|
270
|
+
onChallengeStart: (gesture, index, total) => console.log(gesture, index, total),
|
|
271
|
+
onSuccess: collectResult => {
|
|
272
|
+
// envie collectResult.capturedGestures ao seu próprio endpoint de recognition
|
|
273
|
+
},
|
|
274
|
+
onError: err => console.error(err.type, err.message),
|
|
275
|
+
onCancel: () => console.log('Usuário cancelou'),
|
|
276
|
+
});
|
|
277
|
+
```
|
|
140
278
|
|
|
141
|
-
|
|
279
|
+
`authorizeOperation` é a variante legada, mais baixo nível (captura 4 fotos
|
|
280
|
+
com gestos fixos e chama `recognizeByPurpose('AUTHORIZATION')` diretamente):
|
|
142
281
|
|
|
143
|
-
|
|
282
|
+
```ts
|
|
283
|
+
import { authorizeOperation } from '@neofaceid/web-sdk';
|
|
144
284
|
|
|
145
|
-
|
|
146
|
-
|
|
285
|
+
const result = await authorizeOperation('seu-token-de-aplicacao', '12345678900', {
|
|
286
|
+
appName: 'Minha Aplicação',
|
|
287
|
+
onProgress: (step, total, instruction) => console.log(instruction, step, total),
|
|
288
|
+
onPhotoTaken: (n, blob) => console.log('Foto', n, blob),
|
|
289
|
+
});
|
|
147
290
|
```
|
|
148
291
|
|
|
149
|
-
|
|
292
|
+
### Classe `NeoFaceID` (`proofOfLife` e afins)
|
|
150
293
|
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
294
|
+
`NeoFaceID` reúne operações que não têm (ainda) uma função `start*`
|
|
295
|
+
dedicada: prova de vida com gravação de vídeo, captura de múltiplos frames,
|
|
296
|
+
login com assinatura para integrações externas, registro de aplicação e
|
|
297
|
+
registro de documento pós-cadastro.
|
|
298
|
+
|
|
299
|
+
```ts
|
|
300
|
+
import { NeoFaceID } from '@neofaceid/web-sdk';
|
|
301
|
+
|
|
302
|
+
const sdk = new NeoFaceID({ appToken: 'seu-token-de-aplicacao' });
|
|
154
303
|
|
|
155
|
-
|
|
304
|
+
// Prova de vida: grava vídeo curto, envia para o backend, faz polling do resultado
|
|
305
|
+
const proof = await sdk.proofOfLife({
|
|
306
|
+
videoDurationMs: 3000,
|
|
307
|
+
onRecordingProgress: progress => console.log(`Gravando: ${progress}%`),
|
|
308
|
+
onTaskStatusChange: (status, progress) => console.log(status, progress),
|
|
309
|
+
});
|
|
310
|
+
if (proof.success && proof.isLive) {
|
|
311
|
+
console.log('Liveness score:', proof.livenessScore);
|
|
312
|
+
console.log('Dados da pessoa:', proof.personalData);
|
|
313
|
+
}
|
|
156
314
|
|
|
157
|
-
|
|
315
|
+
// Login com assinatura (integrações externas — ex.: sistemas que já geram HMAC)
|
|
316
|
+
const sdkExterno = new NeoFaceID({
|
|
317
|
+
appToken: 'seu-token-de-aplicacao',
|
|
318
|
+
signature: signatureFromBackend, // HMAC-SHA256 hex, mínimo 64 chars
|
|
319
|
+
sessionData: { email: 'user@exemplo.com', cpf: '12345678900', sessionId: 'session-uuid' },
|
|
320
|
+
});
|
|
321
|
+
const frames = await sdkExterno.captureFaceFrames({ numFrames: 5, livenessCheck: true });
|
|
322
|
+
const login = await sdkExterno.loginRecognition({
|
|
323
|
+
biometricData: frames,
|
|
324
|
+
typeOfIdentification: 'FACE',
|
|
325
|
+
purpose: 'LOGIN',
|
|
326
|
+
});
|
|
158
327
|
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
<add key="NeoFaceId:ApplicationToken" value="seu-token-de-aplicacao" />
|
|
163
|
-
</appSettings>
|
|
164
|
-
</configuration>
|
|
328
|
+
// Registro de documento para pessoa já cadastrada
|
|
329
|
+
const docResult = await sdk.registerDocumentByImage(personId, userJwtToken);
|
|
330
|
+
console.log('Task ID:', docResult.taskId);
|
|
165
331
|
```
|
|
166
332
|
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
{
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
_applicationToken = System.Configuration.ConfigurationManager.AppSettings["NeoFaceId:ApplicationToken"];
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
public ActionResult Index()
|
|
185
|
-
{
|
|
186
|
-
return View();
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
[HttpPost]
|
|
190
|
-
public JsonResult AuthenticateUser(string name, string email, string documentId)
|
|
191
|
-
{
|
|
192
|
-
// Processar a autenticação do usuário
|
|
193
|
-
// Este método é chamado após o reconhecimento facial bem-sucedido
|
|
194
|
-
|
|
195
|
-
// Exemplo: Criar sessão do usuário
|
|
196
|
-
Session["UserName"] = name;
|
|
197
|
-
Session["UserEmail"] = email;
|
|
198
|
-
Session["UserDocumentId"] = documentId;
|
|
199
|
-
|
|
200
|
-
return Json(new { success = true });
|
|
201
|
-
}
|
|
333
|
+
## Tratamento de erros
|
|
334
|
+
|
|
335
|
+
Toda falha do SDK (nos fluxos novos) é uma instância de `NeoFaceError`, com
|
|
336
|
+
`error.type` (`ErrorType`) e `error.getFriendlyMessage()` para exibir ao
|
|
337
|
+
usuário final sem termos técnicos:
|
|
338
|
+
|
|
339
|
+
```ts
|
|
340
|
+
import { NeoFaceError, ErrorType } from '@neofaceid/web-sdk';
|
|
341
|
+
|
|
342
|
+
function onError(err: NeoFaceError) {
|
|
343
|
+
if (err.type === ErrorType.CONSENT_DENIED) {
|
|
344
|
+
// usuário recusou o consentimento — não abriu câmera
|
|
345
|
+
}
|
|
346
|
+
alert(err.getFriendlyMessage());
|
|
202
347
|
}
|
|
203
348
|
```
|
|
204
349
|
|
|
205
|
-
|
|
350
|
+
> Os fluxos legados `start`, `startBiometricRegistration` e
|
|
351
|
+
> `startLivenessCapture` ainda usam `onError(code: string, message: string)`
|
|
352
|
+
> em vez de `onError(err: NeoFaceError)` — consulte o `CHANGELOG.md` antes de
|
|
353
|
+
> migrar código que dependa dessa assinatura.
|
|
354
|
+
|
|
355
|
+
| `ErrorType` | Quando ocorre |
|
|
356
|
+
| ----------------------- | --------------------------------------------------------------- |
|
|
357
|
+
| `NETWORK` | Erro de conexão com o servidor (alias `@deprecated`: `NETWORK_ERROR`) |
|
|
358
|
+
| `INVALID_TOKEN` | `applicationToken` inválido ou expirado |
|
|
359
|
+
| `RECOGNITION_FAILED` | Falha no reconhecimento facial |
|
|
360
|
+
| `LOGIN_FAILED` | Falha no login biométrico |
|
|
361
|
+
| `VALIDATION_ERROR` | Dados inválidos ou rosto não detectado corretamente |
|
|
362
|
+
| `API_ERROR` | Erro genérico de API (400/500) |
|
|
363
|
+
| `PERSON_NOT_FOUND` | Pessoa não encontrada na base |
|
|
364
|
+
| `INITIALIZATION_ERROR` | Falha ao inicializar um fluxo |
|
|
365
|
+
| `CAMERA_ERROR` | Erro genérico de câmera |
|
|
366
|
+
| `NO_CAMERA` | Câmera não disponível ou permissão negada |
|
|
367
|
+
| `CAPTURE_ERROR` | Falha durante a captura de imagem/vídeo |
|
|
368
|
+
| `NOT_FOUND` | Recurso não encontrado |
|
|
369
|
+
| `UNKNOWN` | Erro não categorizado |
|
|
370
|
+
| `CONSENT_DENIED` | Titular recusou o consentimento LGPD |
|
|
371
|
+
| `LIVENESS_BLINK_MISSING`| Piscada real (EAR) não detectada na janela de liveness (3s) |
|
|
372
|
+
|
|
373
|
+
## Componentes React exportados
|
|
374
|
+
|
|
375
|
+
Para quem prefere montar a própria árvore React em vez de usar as funções
|
|
376
|
+
`start*` (que já cuidam de criar/desmontar o container):
|
|
377
|
+
|
|
378
|
+
```tsx
|
|
379
|
+
import {
|
|
380
|
+
FaceCaptureModal,
|
|
381
|
+
BiometricRegistrationModal,
|
|
382
|
+
ConsentModal,
|
|
383
|
+
EmailPasswordModal,
|
|
384
|
+
ForgotPasswordModal,
|
|
385
|
+
BiometricStatusOverlay,
|
|
386
|
+
FallbackPrompt,
|
|
387
|
+
DocumentCaptureModal,
|
|
388
|
+
} from '@neofaceid/web-sdk';
|
|
389
|
+
```
|
|
206
390
|
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
}
|
|
391
|
+
Todos recebem `accessToken`/`applicationToken`, `onSuccess`/`onError` e
|
|
392
|
+
`onClose` conforme o fluxo — consulte a assinatura de cada `start*`
|
|
393
|
+
correspondente acima para as props equivalentes.
|
|
211
394
|
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
}, 1500);
|
|
244
|
-
}
|
|
245
|
-
},
|
|
246
|
-
error: function() {
|
|
247
|
-
document.getElementById('authResult').innerHTML =
|
|
248
|
-
'<div class="alert alert-danger">Erro ao processar autenticação no servidor.</div>';
|
|
249
|
-
}
|
|
250
|
-
});
|
|
251
|
-
},
|
|
252
|
-
onError: function(code, message) {
|
|
253
|
-
document.getElementById('authResult').innerHTML =
|
|
254
|
-
'<div class="alert alert-danger">Erro: ' + message + '</div>';
|
|
255
|
-
}
|
|
256
|
-
});
|
|
257
|
-
});
|
|
258
|
-
</script>
|
|
259
|
-
}
|
|
395
|
+
## Sessão de captura e desafio de liveness (baixo nível)
|
|
396
|
+
|
|
397
|
+
Usado internamente por `authorize`/`authorizeOperation`, mas exportado para
|
|
398
|
+
quem monta o próprio orquestrador de liveness:
|
|
399
|
+
|
|
400
|
+
```ts
|
|
401
|
+
import {
|
|
402
|
+
openCaptureSession,
|
|
403
|
+
requestLivenessChallenge,
|
|
404
|
+
runLivenessChallenge,
|
|
405
|
+
} from '@neofaceid/web-sdk';
|
|
406
|
+
|
|
407
|
+
const session = await openCaptureSession({
|
|
408
|
+
applicationToken: 'seu-token-de-aplicacao',
|
|
409
|
+
purpose: 'authorization', // 'login' | 'onboarding' | 'authorization' | 'identification' | 'liveness'
|
|
410
|
+
});
|
|
411
|
+
|
|
412
|
+
const challenge = await requestLivenessChallenge({
|
|
413
|
+
applicationToken: 'seu-token-de-aplicacao',
|
|
414
|
+
sessionId: session.session_id,
|
|
415
|
+
});
|
|
416
|
+
|
|
417
|
+
// Ou o fluxo completo (session → challenge → coleta via callback → retorno):
|
|
418
|
+
const collected = await runLivenessChallenge({
|
|
419
|
+
applicationToken: 'seu-token-de-aplicacao',
|
|
420
|
+
purpose: 'login',
|
|
421
|
+
collectFramesForGesture: async (gesture, index, total) => {
|
|
422
|
+
// capture o(s) frame(s) correspondente(s) ao gesto e retorne os Blobs
|
|
423
|
+
return [] as Blob[];
|
|
424
|
+
},
|
|
425
|
+
});
|
|
260
426
|
```
|
|
261
427
|
|
|
262
|
-
|
|
428
|
+
## Trilha de consentimento (auditoria LGPD)
|
|
263
429
|
|
|
264
|
-
|
|
430
|
+
```ts
|
|
431
|
+
import { recordConsent, getConsentTrail, clearConsentTrail } from '@neofaceid/web-sdk';
|
|
265
432
|
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
|
|
271
|
-
}
|
|
433
|
+
// Grava no localStorage apenas hash(userAgent+purpose+timestamp), timestamp,
|
|
434
|
+
// finalidade, base legal e versão do SDK — zero PII. Usa por padrão o
|
|
435
|
+
// `consent` configurado em `init()`; pode ser sobrescrito pontualmente:
|
|
436
|
+
await recordConsent({ purpose: 'authentication', legalBasis: 'fraud_prevention' });
|
|
272
437
|
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
border: none;
|
|
277
|
-
padding: 8px 16px;
|
|
278
|
-
border-radius: 4px;
|
|
279
|
-
cursor: pointer;
|
|
280
|
-
}
|
|
438
|
+
const trail = getConsentTrail(); // ConsentRecord[] — histórico local (máx. 100, FIFO)
|
|
439
|
+
clearConsentTrail(); // limpa o histórico (ex.: logout)
|
|
440
|
+
```
|
|
281
441
|
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
442
|
+
## API de baixo nível (`api.ts`)
|
|
443
|
+
|
|
444
|
+
Funções que os fluxos `start*` já usam por baixo dos panos, expostas para
|
|
445
|
+
integrações que precisam de controle fino sobre a chamada HTTP:
|
|
446
|
+
|
|
447
|
+
```ts
|
|
448
|
+
import {
|
|
449
|
+
validateToken,
|
|
450
|
+
validateOnboardingToken,
|
|
451
|
+
recognize,
|
|
452
|
+
recognizeBiometric,
|
|
453
|
+
simpleIdentification,
|
|
454
|
+
recognizeByPurpose,
|
|
455
|
+
loginWithBiometric,
|
|
456
|
+
loginWithEmail,
|
|
457
|
+
registerPersonWithoutFace,
|
|
458
|
+
registerPersonWithBiometric,
|
|
459
|
+
registerBiometric,
|
|
460
|
+
identifyPerson,
|
|
461
|
+
identifyPersonAsync,
|
|
462
|
+
checkUserExistence,
|
|
463
|
+
requestPasswordReset,
|
|
464
|
+
confirmPasswordReset,
|
|
465
|
+
completeOnboarding,
|
|
466
|
+
completeOnboardingWithData,
|
|
467
|
+
} from '@neofaceid/web-sdk';
|
|
468
|
+
|
|
469
|
+
await validateToken('seu-token-de-aplicacao'); // boolean
|
|
470
|
+
await validateOnboardingToken('seu-token-de-aplicacao', 'token-do-onboarding'); // boolean
|
|
471
|
+
|
|
472
|
+
await recognize(faceBlob, 'seu-token-de-aplicacao');
|
|
473
|
+
await recognizeBiometric(faceBlob, 'seu-token-de-aplicacao', /* livenessCheck */ true, 0.8);
|
|
474
|
+
await simpleIdentification('CPF', '12345678900', 'seu-token-de-aplicacao');
|
|
475
|
+
await recognizeByPurpose(faceBlob, 'seu-token-de-aplicacao', 'LOGIN', 0.8);
|
|
476
|
+
|
|
477
|
+
await loginWithBiometric(faceBlob, 'seu-token-de-aplicacao');
|
|
478
|
+
await loginWithEmail('user@exemplo.com', 'senha', 'seu-token-de-aplicacao');
|
|
479
|
+
|
|
480
|
+
await registerPersonWithoutFace(
|
|
481
|
+
{ name: 'Maria Silva', birth_date: '1990-05-20', cpf: '12345678900', email: 'maria@exemplo.com', password: 'senha-forte' },
|
|
482
|
+
'seu-token-de-aplicacao'
|
|
483
|
+
);
|
|
484
|
+
await registerPersonWithBiometric(
|
|
485
|
+
{ name: 'Maria Silva', birth_date: '1990-05-20', cpf: '12345678900', email: 'maria@exemplo.com', password: 'senha-forte' },
|
|
486
|
+
[faceBlob1, faceBlob2],
|
|
487
|
+
'seu-token-de-aplicacao'
|
|
488
|
+
);
|
|
489
|
+
await registerBiometric(personId, faceBlob, 'seu-token-de-aplicacao');
|
|
490
|
+
|
|
491
|
+
await identifyPerson(faceBlob, 'seu-token-de-aplicacao');
|
|
492
|
+
await identifyPersonAsync(faceBlob, 'seu-token-de-aplicacao', { maxRetries: 5, interval: 1000 });
|
|
493
|
+
await checkUserExistence({ email: 'user@exemplo.com' }); // ou { cpf }
|
|
494
|
+
|
|
495
|
+
await requestPasswordReset('user@exemplo.com', 'seu-token-de-aplicacao');
|
|
496
|
+
await confirmPasswordReset('token-do-email', 'nova-senha');
|
|
497
|
+
|
|
498
|
+
await completeOnboarding('seu-token-de-aplicacao', 'token-do-onboarding', faceBlob, documentBlob);
|
|
499
|
+
await completeOnboardingWithData(
|
|
500
|
+
'seu-token-de-aplicacao',
|
|
501
|
+
'token-do-onboarding',
|
|
502
|
+
{ name: 'Maria Silva', cpf: '12345678900' },
|
|
503
|
+
[faceBlob1, faceBlob2],
|
|
504
|
+
documentBlob
|
|
505
|
+
);
|
|
285
506
|
```
|
|
286
507
|
|
|
287
|
-
|
|
508
|
+
Utilitário de performance para pré-carregar os modelos de detecção facial
|
|
509
|
+
antes do primeiro uso (evita o delay do primeiro `loadFromUri` acontecer
|
|
510
|
+
durante a interação do usuário):
|
|
511
|
+
|
|
512
|
+
```ts
|
|
513
|
+
import { preloadFaceDetectionModels } from '@neofaceid/web-sdk';
|
|
288
514
|
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
- Armazene o token de aplicação de forma segura (nunca no código-fonte)
|
|
292
|
-
- Considere implementar rate limiting para evitar abusos
|
|
293
|
-
- Implemente logs de auditoria para rastrear tentativas de autenticação
|
|
515
|
+
preloadFaceDetectionModels(); // dispara o carregamento de /models em background
|
|
516
|
+
```
|
|
294
517
|
|
|
295
|
-
##
|
|
518
|
+
## Versão do SDK
|
|
296
519
|
|
|
297
|
-
|
|
520
|
+
```ts
|
|
521
|
+
import { VERSION, RELEASE_DATE } from '@neofaceid/web-sdk';
|
|
298
522
|
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
3. **Determina** o tipo de bump (patch/minor) baseado no commit message
|
|
302
|
-
4. **Incrementa** a versão em todos os arquivos (package.json, package-lock.json, src/version.ts)
|
|
303
|
-
5. **Commita** e faz push das mudanças com retry logic
|
|
304
|
-
6. **Builda** o pacote
|
|
305
|
-
7. **Publica** no NPM com retry automático em caso de falhas temporárias
|
|
523
|
+
console.log(`NeoFace ID SDK ${VERSION} (${RELEASE_DATE})`);
|
|
524
|
+
```
|
|
306
525
|
|
|
307
|
-
|
|
526
|
+
Histórico completo em [`CHANGELOG.md`](CHANGELOG.md).
|
|
308
527
|
|
|
309
|
-
|
|
310
|
-
- `fix:` ou `patch:` → bump **patch** (1.0.0 → 1.0.1)
|
|
311
|
-
- Outros commits → bump **patch** por padrão
|
|
528
|
+
## Compatibilidade
|
|
312
529
|
|
|
313
|
-
###
|
|
530
|
+
### Navegadores suportados
|
|
314
531
|
|
|
315
|
-
|
|
532
|
+
- Chrome 60+
|
|
533
|
+
- Firefox 55+
|
|
534
|
+
- Safari 11+
|
|
535
|
+
- Edge 79+
|
|
316
536
|
|
|
317
|
-
###
|
|
537
|
+
### Responsividade
|
|
318
538
|
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
- **Erro 409**: Verifica se versão existe antes de falhar
|
|
539
|
+
O SDK é totalmente responsivo. Em dispositivos móveis, os modais ocupam a
|
|
540
|
+
tela inteira.
|
|
322
541
|
|
|
323
542
|
## Licença
|
|
324
543
|
|
|
325
|
-
MIT
|
|
544
|
+
MIT
|