@neofaceid/web-sdk 1.26.0 → 1.27.0
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/dist/index.d.ts +131 -2
- package/dist/neoface-id-sdk.es.js +266 -11
- package/dist/neoface-id-sdk.umd.js +1 -1
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -258,6 +258,19 @@ export declare interface CaptureFaceFramesOptions {
|
|
|
258
258
|
livenessCheck: boolean;
|
|
259
259
|
}
|
|
260
260
|
|
|
261
|
+
export declare type CapturePurpose = 'login' | 'onboarding' | 'authorization' | 'identification' | 'liveness';
|
|
262
|
+
|
|
263
|
+
export declare interface CaptureSession {
|
|
264
|
+
session_id: string;
|
|
265
|
+
expires_at: string;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
export declare interface ChallengeGesture {
|
|
269
|
+
index: number;
|
|
270
|
+
gesture_key: GestureKey | string;
|
|
271
|
+
instruction_i18n?: Record<string, string>;
|
|
272
|
+
}
|
|
273
|
+
|
|
261
274
|
/**
|
|
262
275
|
* Check if a user exists by email or CPF
|
|
263
276
|
* @param params Object with email or cpf to check (only one at a time)
|
|
@@ -557,6 +570,14 @@ export declare class ForgotPasswordModal {
|
|
|
557
570
|
private addStyles;
|
|
558
571
|
}
|
|
559
572
|
|
|
573
|
+
export declare type GestureKey = 'turn_left' | 'turn_right' | 'turn_up' | 'turn_down' | 'blink' | 'smile' | 'open_mouth';
|
|
574
|
+
|
|
575
|
+
export declare interface GestureResponse {
|
|
576
|
+
gesture_key: string;
|
|
577
|
+
/** Frames capturados como data URL ou base64. SDK só empacota — servidor valida. */
|
|
578
|
+
images: string[];
|
|
579
|
+
}
|
|
580
|
+
|
|
560
581
|
/**
|
|
561
582
|
* Retorna o token de aplicação configurado, se houver.
|
|
562
583
|
*/
|
|
@@ -568,6 +589,8 @@ export declare function getApplicationToken(): string | null;
|
|
|
568
589
|
*/
|
|
569
590
|
export declare function getBaseUrl(): string;
|
|
570
591
|
|
|
592
|
+
export declare function getCachedCaptureSession(): CaptureSession | null;
|
|
593
|
+
|
|
571
594
|
/**
|
|
572
595
|
* Retorna toda a configuração atual (para debug).
|
|
573
596
|
*/
|
|
@@ -656,6 +679,19 @@ export declare function isAdvancedDetectionAvailable(): boolean;
|
|
|
656
679
|
*/
|
|
657
680
|
export declare function isInitialized(): boolean;
|
|
658
681
|
|
|
682
|
+
export declare interface LivenessChallenge {
|
|
683
|
+
challenge_id: string;
|
|
684
|
+
gestures: ChallengeGesture[];
|
|
685
|
+
expires_at?: string;
|
|
686
|
+
}
|
|
687
|
+
|
|
688
|
+
export declare interface LivenessChallengeResult {
|
|
689
|
+
success: boolean;
|
|
690
|
+
liveness_passed?: boolean;
|
|
691
|
+
score?: number;
|
|
692
|
+
raw: unknown;
|
|
693
|
+
}
|
|
694
|
+
|
|
659
695
|
/**
|
|
660
696
|
* Options for login recognition
|
|
661
697
|
*/
|
|
@@ -942,6 +978,19 @@ export declare interface OnboardingPersonData {
|
|
|
942
978
|
email?: string;
|
|
943
979
|
}
|
|
944
980
|
|
|
981
|
+
/**
|
|
982
|
+
* Abre uma sessão de captura no servidor. Server-decides: o `session_id`
|
|
983
|
+
* vem do backend, nunca é inventado no cliente.
|
|
984
|
+
*
|
|
985
|
+
* @throws NeoFaceError em 4xx/5xx ou payload inválido.
|
|
986
|
+
*/
|
|
987
|
+
export declare function openCaptureSession({ applicationToken, purpose, }: OpenCaptureSessionParams): Promise<CaptureSession>;
|
|
988
|
+
|
|
989
|
+
export declare interface OpenCaptureSessionParams {
|
|
990
|
+
applicationToken: string;
|
|
991
|
+
purpose: CapturePurpose;
|
|
992
|
+
}
|
|
993
|
+
|
|
945
994
|
export declare type OverlayStatus = 'preparing' | 'detecting' | 'waiting-for-face' | 'capturing' | 'verifying' | 'success' | 'error';
|
|
946
995
|
|
|
947
996
|
/**
|
|
@@ -1172,7 +1221,13 @@ export declare const registerPersonWithoutFace: (personData: {
|
|
|
1172
1221
|
/**
|
|
1173
1222
|
* Data de lançamento da versão atual
|
|
1174
1223
|
*/
|
|
1175
|
-
export declare const RELEASE_DATE = "2026-
|
|
1224
|
+
export declare const RELEASE_DATE = "2026-09-01";
|
|
1225
|
+
|
|
1226
|
+
declare interface RequestChallengeWithSessionParams {
|
|
1227
|
+
applicationToken: string;
|
|
1228
|
+
purpose: CapturePurpose;
|
|
1229
|
+
sessionId?: string;
|
|
1230
|
+
}
|
|
1176
1231
|
|
|
1177
1232
|
/**
|
|
1178
1233
|
* Exibe o modal de consentimento e resolve `true` se o titular aceitou,
|
|
@@ -1180,6 +1235,24 @@ export declare const RELEASE_DATE = "2026-08-31";
|
|
|
1180
1235
|
*/
|
|
1181
1236
|
export declare function requestConsent(info?: Partial<ConsentInfo>): Promise<boolean>;
|
|
1182
1237
|
|
|
1238
|
+
/**
|
|
1239
|
+
* Pede ao servidor a sequência de gestos do desafio de liveness.
|
|
1240
|
+
* O SDK apenas exibe e coleta — validação de gesto/yaw/pitch é responsabilidade
|
|
1241
|
+
* exclusiva do servidor (regra "client collects, server decides").
|
|
1242
|
+
*/
|
|
1243
|
+
export declare function requestLivenessChallenge({ sessionId, applicationToken, }: RequestLivenessChallengeParams): Promise<LivenessChallenge>;
|
|
1244
|
+
|
|
1245
|
+
export declare interface RequestLivenessChallengeParams {
|
|
1246
|
+
sessionId: string;
|
|
1247
|
+
applicationToken: string;
|
|
1248
|
+
}
|
|
1249
|
+
|
|
1250
|
+
/**
|
|
1251
|
+
* Garante uma sessão válida e retorna o desafio.
|
|
1252
|
+
* Em 410 (sessão expirada), reabre 1x e reenvia. Segunda falha propaga.
|
|
1253
|
+
*/
|
|
1254
|
+
export declare function requestLivenessChallengeWithSession({ applicationToken, purpose, sessionId, }: RequestChallengeWithSessionParams): Promise<LivenessChallenge>;
|
|
1255
|
+
|
|
1183
1256
|
/**
|
|
1184
1257
|
* Request a password reset for a user account
|
|
1185
1258
|
* @param email User email address
|
|
@@ -1189,6 +1262,33 @@ export declare function requestPasswordReset(email: string, applicationToken: st
|
|
|
1189
1262
|
message: string;
|
|
1190
1263
|
}>;
|
|
1191
1264
|
|
|
1265
|
+
export declare function resetCaptureSession(): void;
|
|
1266
|
+
|
|
1267
|
+
/**
|
|
1268
|
+
* Orquestra o fluxo completo:
|
|
1269
|
+
* 1. Abre `capture/session`
|
|
1270
|
+
* 2. Pede `liveness/challenge` (sequência sorteada pelo servidor)
|
|
1271
|
+
* 3. Delega a coleta de frames ao consumidor gesto a gesto
|
|
1272
|
+
* 4. Submete a resposta para o servidor validar
|
|
1273
|
+
*
|
|
1274
|
+
* Retry: em 410 no submit, reabre a sessão + refaz o desafio + reenvia UMA vez.
|
|
1275
|
+
* Segunda falha propaga.
|
|
1276
|
+
*/
|
|
1277
|
+
export declare function runLivenessChallenge({ applicationToken, purpose, collectFramesForGesture, retryOnSessionExpired, }: RunLivenessChallengeParams): Promise<LivenessChallengeResult>;
|
|
1278
|
+
|
|
1279
|
+
export declare interface RunLivenessChallengeParams {
|
|
1280
|
+
applicationToken: string;
|
|
1281
|
+
purpose: CapturePurpose;
|
|
1282
|
+
/**
|
|
1283
|
+
* Callback fornecido pelo consumidor: para cada gesto na sequência sorteada
|
|
1284
|
+
* pelo servidor, retorna os frames coletados. SDK NÃO decide se o gesto foi
|
|
1285
|
+
* cumprido — só coleta e envia; validação é 100% servidor.
|
|
1286
|
+
*/
|
|
1287
|
+
collectFramesForGesture: (gesture: ChallengeGesture, index: number, total: number) => Promise<string[]>;
|
|
1288
|
+
/** Se true, quando o servidor devolver 410 no submit, reabre sessão + refaz o desafio 1x. */
|
|
1289
|
+
retryOnSessionExpired?: boolean;
|
|
1290
|
+
}
|
|
1291
|
+
|
|
1192
1292
|
/**
|
|
1193
1293
|
* Configuração interna do SDK
|
|
1194
1294
|
*/
|
|
@@ -1327,6 +1427,20 @@ export declare interface StartOnboardingOptions {
|
|
|
1327
1427
|
onCancel?: () => void;
|
|
1328
1428
|
}
|
|
1329
1429
|
|
|
1430
|
+
/**
|
|
1431
|
+
* Envia os frames coletados para o servidor validar o desafio de liveness.
|
|
1432
|
+
* Endpoint: `POST /api/v1/auth/liveness/challenge/{challenge_id}/submit/`
|
|
1433
|
+
* O SDK apenas empacota e envia — nenhuma decisão de aprovar/reprovar aqui.
|
|
1434
|
+
*/
|
|
1435
|
+
export declare function submitLivenessChallenge({ challengeId, sessionId, applicationToken, gestures, }: SubmitLivenessChallengeParams): Promise<LivenessChallengeResult>;
|
|
1436
|
+
|
|
1437
|
+
export declare interface SubmitLivenessChallengeParams {
|
|
1438
|
+
challengeId: string;
|
|
1439
|
+
sessionId: string;
|
|
1440
|
+
applicationToken: string;
|
|
1441
|
+
gestures: GestureResponse[];
|
|
1442
|
+
}
|
|
1443
|
+
|
|
1330
1444
|
/**
|
|
1331
1445
|
* Result of user existence check
|
|
1332
1446
|
*/
|
|
@@ -1363,6 +1477,21 @@ export declare const validateToken: (applicationToken: string) => Promise<boolea
|
|
|
1363
1477
|
* MINOR: Incrementado quando adicionamos funcionalidades mantendo compatibilidade
|
|
1364
1478
|
* PATCH: Incrementado quando corrigimos bugs mantendo compatibilidade
|
|
1365
1479
|
*/
|
|
1366
|
-
export declare const VERSION = "1.
|
|
1480
|
+
export declare const VERSION = "1.27.0";
|
|
1481
|
+
|
|
1482
|
+
/**
|
|
1483
|
+
* Executa `fn(sessionId)`. Se o servidor devolver 410 (sessão consumida/expirada),
|
|
1484
|
+
* reabre a sessão automaticamente 1x e reexecuta. Segunda falha propaga.
|
|
1485
|
+
*/
|
|
1486
|
+
export declare function withCaptureSession<T>(params: OpenCaptureSessionParams, fn: (sessionId: string) => Promise<T>): Promise<T>;
|
|
1487
|
+
|
|
1488
|
+
/**
|
|
1489
|
+
* Injeta `session_id` cacheado no payload. Se não houver sessão aberta,
|
|
1490
|
+
* retorna o payload original inalterado — a decisão de exigir sessão
|
|
1491
|
+
* fica no servidor.
|
|
1492
|
+
*/
|
|
1493
|
+
export declare function withSessionId<T extends Record<string, unknown>>(payload: T): T & {
|
|
1494
|
+
session_id?: string;
|
|
1495
|
+
};
|
|
1367
1496
|
|
|
1368
1497
|
export { }
|
|
@@ -4614,7 +4614,7 @@ const completeOnboarding = async (applicationToken, onboardingToken, faceImage,
|
|
|
4614
4614
|
reader.readAsDataURL(compressedDocument);
|
|
4615
4615
|
});
|
|
4616
4616
|
const response = await fetch(
|
|
4617
|
-
`${getApiBaseUrl()}/api/v1/onboardings/${encodeURIComponent(onboardingToken)}/complete
|
|
4617
|
+
`${getApiBaseUrl()}/api/v1/onboardings/${encodeURIComponent(onboardingToken)}/complete/`,
|
|
4618
4618
|
{
|
|
4619
4619
|
method: "POST",
|
|
4620
4620
|
headers: {
|
|
@@ -4700,7 +4700,7 @@ const completeOnboardingWithData = async (applicationToken, onboardingToken, per
|
|
|
4700
4700
|
email: personData.email
|
|
4701
4701
|
};
|
|
4702
4702
|
const response = await fetch(
|
|
4703
|
-
`${getApiBaseUrl()}/api/v1/onboardings/${encodeURIComponent(onboardingToken)}/complete
|
|
4703
|
+
`${getApiBaseUrl()}/api/v1/onboardings/${encodeURIComponent(onboardingToken)}/complete/`,
|
|
4704
4704
|
{
|
|
4705
4705
|
method: "POST",
|
|
4706
4706
|
headers: {
|
|
@@ -4762,7 +4762,7 @@ const recognize = async (image, applicationToken) => {
|
|
|
4762
4762
|
reader.onerror = reject;
|
|
4763
4763
|
reader.readAsDataURL(compressedImage);
|
|
4764
4764
|
});
|
|
4765
|
-
const response = await fetch(`${getApiBaseUrl()}/api/v1/
|
|
4765
|
+
const response = await fetch(`${getApiBaseUrl()}/api/v1/auth/recognition/face/`, {
|
|
4766
4766
|
method: "POST",
|
|
4767
4767
|
headers: {
|
|
4768
4768
|
"Content-Type": "application/json",
|
|
@@ -4881,7 +4881,7 @@ const recognizeBiometric = async (image, applicationToken, livenessCheck = true,
|
|
|
4881
4881
|
reader.onerror = reject;
|
|
4882
4882
|
reader.readAsDataURL(compressedImage);
|
|
4883
4883
|
});
|
|
4884
|
-
const response = await fetch(`${getApiBaseUrl()}/api/v1/
|
|
4884
|
+
const response = await fetch(`${getApiBaseUrl()}/api/v1/auth/recognition/face/`, {
|
|
4885
4885
|
method: "POST",
|
|
4886
4886
|
headers: {
|
|
4887
4887
|
"Content-Type": "application/json",
|
|
@@ -4925,7 +4925,7 @@ const simpleIdentification = async (documentType, documentNumber, applicationTok
|
|
|
4925
4925
|
ensureSecureContext();
|
|
4926
4926
|
const controller = createTimeoutController();
|
|
4927
4927
|
try {
|
|
4928
|
-
const response = await fetch(`${getApiBaseUrl()}/api/v1/
|
|
4928
|
+
const response = await fetch(`${getApiBaseUrl()}/api/v1/auth/recognition/face/`, {
|
|
4929
4929
|
method: "POST",
|
|
4930
4930
|
headers: {
|
|
4931
4931
|
"Content-Type": "application/json",
|
|
@@ -4991,7 +4991,7 @@ const recognizeByPurpose = async (image, applicationToken, purpose, confidenceTh
|
|
|
4991
4991
|
if (signature) {
|
|
4992
4992
|
requestBody.signature = signature;
|
|
4993
4993
|
}
|
|
4994
|
-
const response = await fetch(`${getApiBaseUrl()}/api/v1/
|
|
4994
|
+
const response = await fetch(`${getApiBaseUrl()}/api/v1/auth/recognition/face/`, {
|
|
4995
4995
|
method: "POST",
|
|
4996
4996
|
headers: {
|
|
4997
4997
|
"Content-Type": "application/json",
|
|
@@ -5361,7 +5361,7 @@ const identifyPerson = async (image, applicationToken) => {
|
|
|
5361
5361
|
reader.readAsDataURL(compressedImage);
|
|
5362
5362
|
});
|
|
5363
5363
|
try {
|
|
5364
|
-
const response = await fetch(`${getApiBaseUrl()}/api/v1/auth/recognition/face
|
|
5364
|
+
const response = await fetch(`${getApiBaseUrl()}/api/v1/auth/recognition/face/`, {
|
|
5365
5365
|
method: "POST",
|
|
5366
5366
|
headers: {
|
|
5367
5367
|
"Content-Type": "application/json",
|
|
@@ -5708,7 +5708,7 @@ const startProofOfLife = async (videoBase64, applicationToken) => {
|
|
|
5708
5708
|
ensureSecureContext();
|
|
5709
5709
|
const controller = createTimeoutController();
|
|
5710
5710
|
try {
|
|
5711
|
-
const response = await fetch(`${getApiBaseUrl()}/api/v1/auth/recognition/face
|
|
5711
|
+
const response = await fetch(`${getApiBaseUrl()}/api/v1/auth/recognition/face/`, {
|
|
5712
5712
|
method: "POST",
|
|
5713
5713
|
headers: {
|
|
5714
5714
|
"Content-Type": "application/json",
|
|
@@ -10417,8 +10417,254 @@ const biometricDetection = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.
|
|
|
10417
10417
|
initializeBiometricDetection,
|
|
10418
10418
|
isAdvancedDetectionAvailable
|
|
10419
10419
|
}, Symbol.toStringTag, { value: "Module" }));
|
|
10420
|
-
const VERSION = "1.
|
|
10421
|
-
const RELEASE_DATE = "2026-
|
|
10420
|
+
const VERSION = "1.27.0";
|
|
10421
|
+
const RELEASE_DATE = "2026-09-01";
|
|
10422
|
+
let cachedSession = null;
|
|
10423
|
+
function getCachedCaptureSession() {
|
|
10424
|
+
return cachedSession;
|
|
10425
|
+
}
|
|
10426
|
+
function resetCaptureSession() {
|
|
10427
|
+
cachedSession = null;
|
|
10428
|
+
}
|
|
10429
|
+
async function openCaptureSession({
|
|
10430
|
+
applicationToken,
|
|
10431
|
+
purpose
|
|
10432
|
+
}) {
|
|
10433
|
+
if (!applicationToken) {
|
|
10434
|
+
throw new NeoFaceError(
|
|
10435
|
+
"applicationToken é obrigatório para abrir sessão de captura",
|
|
10436
|
+
ErrorType.INVALID_TOKEN
|
|
10437
|
+
);
|
|
10438
|
+
}
|
|
10439
|
+
const url = `${getBaseUrl()}/api/v1/auth/capture/session/`;
|
|
10440
|
+
let response;
|
|
10441
|
+
try {
|
|
10442
|
+
response = await fetch(url, {
|
|
10443
|
+
method: "POST",
|
|
10444
|
+
headers: {
|
|
10445
|
+
"Content-Type": "application/json",
|
|
10446
|
+
"X-App-Token": applicationToken
|
|
10447
|
+
},
|
|
10448
|
+
body: JSON.stringify({ purpose })
|
|
10449
|
+
});
|
|
10450
|
+
} catch (err) {
|
|
10451
|
+
const message = err instanceof Error ? err.message : "Erro de rede";
|
|
10452
|
+
throw new NeoFaceError(`Falha de rede ao abrir sessão: ${message}`, ErrorType.NETWORK);
|
|
10453
|
+
}
|
|
10454
|
+
if (response.status === 401 || response.status === 403) {
|
|
10455
|
+
throw new NeoFaceError("Token de aplicação inválido ou expirado", ErrorType.INVALID_TOKEN);
|
|
10456
|
+
}
|
|
10457
|
+
if (!response.ok) {
|
|
10458
|
+
throw new NeoFaceError(
|
|
10459
|
+
`Falha ao abrir sessão de captura (${response.status})`,
|
|
10460
|
+
ErrorType.API_ERROR
|
|
10461
|
+
);
|
|
10462
|
+
}
|
|
10463
|
+
const raw = await response.json().catch(() => null);
|
|
10464
|
+
const payload = (raw == null ? void 0 : raw.data) ?? raw;
|
|
10465
|
+
const sessionId = payload == null ? void 0 : payload.session_id;
|
|
10466
|
+
const expiresAt = payload == null ? void 0 : payload.expires_at;
|
|
10467
|
+
if (!sessionId || !expiresAt) {
|
|
10468
|
+
throw new NeoFaceError(
|
|
10469
|
+
"Resposta inválida ao abrir sessão de captura (campos ausentes)",
|
|
10470
|
+
ErrorType.API_ERROR
|
|
10471
|
+
);
|
|
10472
|
+
}
|
|
10473
|
+
cachedSession = { session_id: sessionId, expires_at: expiresAt };
|
|
10474
|
+
return cachedSession;
|
|
10475
|
+
}
|
|
10476
|
+
async function withCaptureSession(params, fn) {
|
|
10477
|
+
const session = cachedSession ?? await openCaptureSession(params);
|
|
10478
|
+
try {
|
|
10479
|
+
return await fn(session.session_id);
|
|
10480
|
+
} catch (err) {
|
|
10481
|
+
if (err instanceof NeoFaceError && err.message.includes("410")) {
|
|
10482
|
+
resetCaptureSession();
|
|
10483
|
+
const fresh = await openCaptureSession(params);
|
|
10484
|
+
return fn(fresh.session_id);
|
|
10485
|
+
}
|
|
10486
|
+
throw err;
|
|
10487
|
+
}
|
|
10488
|
+
}
|
|
10489
|
+
function withSessionId(payload) {
|
|
10490
|
+
const session = cachedSession;
|
|
10491
|
+
if (!session) return payload;
|
|
10492
|
+
return { ...payload, session_id: session.session_id };
|
|
10493
|
+
}
|
|
10494
|
+
async function requestLivenessChallenge({
|
|
10495
|
+
sessionId,
|
|
10496
|
+
applicationToken
|
|
10497
|
+
}) {
|
|
10498
|
+
if (!applicationToken) {
|
|
10499
|
+
throw new NeoFaceError(
|
|
10500
|
+
"applicationToken é obrigatório para solicitar desafio de liveness",
|
|
10501
|
+
ErrorType.INVALID_TOKEN
|
|
10502
|
+
);
|
|
10503
|
+
}
|
|
10504
|
+
if (!sessionId) {
|
|
10505
|
+
throw new NeoFaceError(
|
|
10506
|
+
"session_id é obrigatório para solicitar desafio de liveness",
|
|
10507
|
+
ErrorType.VALIDATION_ERROR
|
|
10508
|
+
);
|
|
10509
|
+
}
|
|
10510
|
+
const url = `${getBaseUrl()}/api/v1/auth/liveness/challenge/`;
|
|
10511
|
+
let response;
|
|
10512
|
+
try {
|
|
10513
|
+
response = await fetch(url, {
|
|
10514
|
+
method: "POST",
|
|
10515
|
+
headers: {
|
|
10516
|
+
"Content-Type": "application/json",
|
|
10517
|
+
"X-App-Token": applicationToken
|
|
10518
|
+
},
|
|
10519
|
+
body: JSON.stringify({ session_id: sessionId })
|
|
10520
|
+
});
|
|
10521
|
+
} catch (err) {
|
|
10522
|
+
const message = err instanceof Error ? err.message : "Erro de rede";
|
|
10523
|
+
throw new NeoFaceError(`Falha de rede ao solicitar desafio: ${message}`, ErrorType.NETWORK);
|
|
10524
|
+
}
|
|
10525
|
+
if (response.status === 401 || response.status === 403) {
|
|
10526
|
+
throw new NeoFaceError("Token de aplicação inválido ou expirado", ErrorType.INVALID_TOKEN);
|
|
10527
|
+
}
|
|
10528
|
+
if (response.status === 410) {
|
|
10529
|
+
throw new NeoFaceError(
|
|
10530
|
+
"Sessão de captura expirada (410) — reabrir sessão",
|
|
10531
|
+
ErrorType.API_ERROR
|
|
10532
|
+
);
|
|
10533
|
+
}
|
|
10534
|
+
if (!response.ok) {
|
|
10535
|
+
throw new NeoFaceError(
|
|
10536
|
+
`Falha ao solicitar desafio de liveness (${response.status})`,
|
|
10537
|
+
ErrorType.API_ERROR
|
|
10538
|
+
);
|
|
10539
|
+
}
|
|
10540
|
+
const raw = await response.json().catch(() => null);
|
|
10541
|
+
const payload = (raw == null ? void 0 : raw.data) ?? raw;
|
|
10542
|
+
const challengeId = payload == null ? void 0 : payload.challenge_id;
|
|
10543
|
+
const gestures = payload == null ? void 0 : payload.gestures;
|
|
10544
|
+
if (!challengeId || !Array.isArray(gestures) || gestures.length === 0) {
|
|
10545
|
+
throw new NeoFaceError(
|
|
10546
|
+
"Resposta inválida ao solicitar desafio (campos ausentes)",
|
|
10547
|
+
ErrorType.API_ERROR
|
|
10548
|
+
);
|
|
10549
|
+
}
|
|
10550
|
+
return {
|
|
10551
|
+
challenge_id: challengeId,
|
|
10552
|
+
gestures,
|
|
10553
|
+
expires_at: payload == null ? void 0 : payload.expires_at
|
|
10554
|
+
};
|
|
10555
|
+
}
|
|
10556
|
+
async function requestLivenessChallengeWithSession({
|
|
10557
|
+
applicationToken,
|
|
10558
|
+
purpose,
|
|
10559
|
+
sessionId
|
|
10560
|
+
}) {
|
|
10561
|
+
let currentSessionId = sessionId ?? (await openCaptureSession({ applicationToken, purpose })).session_id;
|
|
10562
|
+
try {
|
|
10563
|
+
return await requestLivenessChallenge({ sessionId: currentSessionId, applicationToken });
|
|
10564
|
+
} catch (err) {
|
|
10565
|
+
if (err instanceof NeoFaceError && err.message.includes("410")) {
|
|
10566
|
+
resetCaptureSession();
|
|
10567
|
+
currentSessionId = (await openCaptureSession({ applicationToken, purpose })).session_id;
|
|
10568
|
+
return requestLivenessChallenge({ sessionId: currentSessionId, applicationToken });
|
|
10569
|
+
}
|
|
10570
|
+
throw err;
|
|
10571
|
+
}
|
|
10572
|
+
}
|
|
10573
|
+
async function submitLivenessChallenge({
|
|
10574
|
+
challengeId,
|
|
10575
|
+
sessionId,
|
|
10576
|
+
applicationToken,
|
|
10577
|
+
gestures
|
|
10578
|
+
}) {
|
|
10579
|
+
if (!applicationToken) {
|
|
10580
|
+
throw new NeoFaceError(
|
|
10581
|
+
"applicationToken é obrigatório para submeter desafio",
|
|
10582
|
+
ErrorType.INVALID_TOKEN
|
|
10583
|
+
);
|
|
10584
|
+
}
|
|
10585
|
+
if (!challengeId || !sessionId) {
|
|
10586
|
+
throw new NeoFaceError(
|
|
10587
|
+
"challenge_id e session_id são obrigatórios para submeter desafio",
|
|
10588
|
+
ErrorType.VALIDATION_ERROR
|
|
10589
|
+
);
|
|
10590
|
+
}
|
|
10591
|
+
if (!Array.isArray(gestures) || gestures.length === 0) {
|
|
10592
|
+
throw new NeoFaceError("gestures[] vazio — nada para submeter", ErrorType.VALIDATION_ERROR);
|
|
10593
|
+
}
|
|
10594
|
+
const url = `${getBaseUrl()}/api/v1/auth/liveness/challenge/${encodeURIComponent(challengeId)}/submit/`;
|
|
10595
|
+
let response;
|
|
10596
|
+
try {
|
|
10597
|
+
response = await fetch(url, {
|
|
10598
|
+
method: "POST",
|
|
10599
|
+
headers: {
|
|
10600
|
+
"Content-Type": "application/json",
|
|
10601
|
+
"X-App-Token": applicationToken
|
|
10602
|
+
},
|
|
10603
|
+
body: JSON.stringify({ session_id: sessionId, gestures })
|
|
10604
|
+
});
|
|
10605
|
+
} catch (err) {
|
|
10606
|
+
const message = err instanceof Error ? err.message : "Erro de rede";
|
|
10607
|
+
throw new NeoFaceError(`Falha de rede ao submeter desafio: ${message}`, ErrorType.NETWORK);
|
|
10608
|
+
}
|
|
10609
|
+
if (response.status === 401 || response.status === 403) {
|
|
10610
|
+
throw new NeoFaceError("Token de aplicação inválido ou expirado", ErrorType.INVALID_TOKEN);
|
|
10611
|
+
}
|
|
10612
|
+
if (response.status === 410) {
|
|
10613
|
+
throw new NeoFaceError(
|
|
10614
|
+
"Sessão ou desafio expirado (410) — reabrir sessão",
|
|
10615
|
+
ErrorType.API_ERROR
|
|
10616
|
+
);
|
|
10617
|
+
}
|
|
10618
|
+
if (!response.ok) {
|
|
10619
|
+
throw new NeoFaceError(
|
|
10620
|
+
`Falha ao submeter desafio de liveness (${response.status})`,
|
|
10621
|
+
ErrorType.API_ERROR
|
|
10622
|
+
);
|
|
10623
|
+
}
|
|
10624
|
+
const raw = await response.json().catch(() => null);
|
|
10625
|
+
const payload = (raw == null ? void 0 : raw.data) ?? raw ?? {};
|
|
10626
|
+
return {
|
|
10627
|
+
success: (payload == null ? void 0 : payload.success) ?? true,
|
|
10628
|
+
liveness_passed: payload == null ? void 0 : payload.liveness_passed,
|
|
10629
|
+
score: payload == null ? void 0 : payload.score,
|
|
10630
|
+
raw
|
|
10631
|
+
};
|
|
10632
|
+
}
|
|
10633
|
+
async function runLivenessChallenge({
|
|
10634
|
+
applicationToken,
|
|
10635
|
+
purpose,
|
|
10636
|
+
collectFramesForGesture,
|
|
10637
|
+
retryOnSessionExpired = true
|
|
10638
|
+
}) {
|
|
10639
|
+
const attempt = async () => {
|
|
10640
|
+
const session = await openCaptureSession({ applicationToken, purpose });
|
|
10641
|
+
const challenge = await requestLivenessChallenge({
|
|
10642
|
+
sessionId: session.session_id,
|
|
10643
|
+
applicationToken
|
|
10644
|
+
});
|
|
10645
|
+
const gestures = [];
|
|
10646
|
+
for (let i = 0; i < challenge.gestures.length; i += 1) {
|
|
10647
|
+
const g = challenge.gestures[i];
|
|
10648
|
+
const images = await collectFramesForGesture(g, i, challenge.gestures.length);
|
|
10649
|
+
gestures.push({ gesture_key: String(g.gesture_key), images });
|
|
10650
|
+
}
|
|
10651
|
+
return submitLivenessChallenge({
|
|
10652
|
+
challengeId: challenge.challenge_id,
|
|
10653
|
+
sessionId: session.session_id,
|
|
10654
|
+
applicationToken,
|
|
10655
|
+
gestures
|
|
10656
|
+
});
|
|
10657
|
+
};
|
|
10658
|
+
try {
|
|
10659
|
+
return await attempt();
|
|
10660
|
+
} catch (err) {
|
|
10661
|
+
if (retryOnSessionExpired && err instanceof NeoFaceError && err.message.includes("410")) {
|
|
10662
|
+
resetCaptureSession();
|
|
10663
|
+
return attempt();
|
|
10664
|
+
}
|
|
10665
|
+
throw err;
|
|
10666
|
+
}
|
|
10667
|
+
}
|
|
10422
10668
|
class OnboardingCaptureModal {
|
|
10423
10669
|
constructor(options) {
|
|
10424
10670
|
__publicField(this, "overlay", null);
|
|
@@ -12275,6 +12521,7 @@ export {
|
|
|
12275
12521
|
detectBiometricType,
|
|
12276
12522
|
getApplicationToken,
|
|
12277
12523
|
getBaseUrl,
|
|
12524
|
+
getCachedCaptureSession,
|
|
12278
12525
|
getConfig,
|
|
12279
12526
|
getEnvironment,
|
|
12280
12527
|
identifyPerson,
|
|
@@ -12285,6 +12532,7 @@ export {
|
|
|
12285
12532
|
isInitialized,
|
|
12286
12533
|
loginWithBiometric,
|
|
12287
12534
|
loginWithEmail,
|
|
12535
|
+
openCaptureSession,
|
|
12288
12536
|
preloadFaceDetectionModels,
|
|
12289
12537
|
recognize,
|
|
12290
12538
|
recognizeBiometric,
|
|
@@ -12293,7 +12541,11 @@ export {
|
|
|
12293
12541
|
registerPersonWithBiometric,
|
|
12294
12542
|
registerPersonWithoutFace,
|
|
12295
12543
|
requestConsent,
|
|
12544
|
+
requestLivenessChallenge,
|
|
12545
|
+
requestLivenessChallengeWithSession,
|
|
12296
12546
|
requestPasswordReset,
|
|
12547
|
+
resetCaptureSession,
|
|
12548
|
+
runLivenessChallenge,
|
|
12297
12549
|
simpleIdentification,
|
|
12298
12550
|
start,
|
|
12299
12551
|
startAutoLogin,
|
|
@@ -12303,6 +12555,9 @@ export {
|
|
|
12303
12555
|
startHandLogin,
|
|
12304
12556
|
startLivenessCapture,
|
|
12305
12557
|
startOnboarding,
|
|
12558
|
+
submitLivenessChallenge,
|
|
12306
12559
|
validateOnboardingToken,
|
|
12307
|
-
validateToken
|
|
12560
|
+
validateToken,
|
|
12561
|
+
withCaptureSession,
|
|
12562
|
+
withSessionId
|
|
12308
12563
|
};
|