@neofaceid/web-sdk 1.2.0 → 1.4.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.
|
@@ -1239,6 +1239,41 @@ const getOnboardingDetails = async (applicationToken, onboardingToken) => {
|
|
|
1239
1239
|
throw new NeoFaceError("Erro desconhecido", ErrorType.NETWORK_ERROR);
|
|
1240
1240
|
}
|
|
1241
1241
|
};
|
|
1242
|
+
const validateOnboardingToken = async (applicationToken, onboardingToken) => {
|
|
1243
|
+
ensureSecureContext();
|
|
1244
|
+
const controller = createTimeoutController();
|
|
1245
|
+
try {
|
|
1246
|
+
const response = await fetch(`${API_BASE_URL}/api/v1/onboardings/${encodeURIComponent(onboardingToken)}/`, {
|
|
1247
|
+
method: "GET",
|
|
1248
|
+
headers: {
|
|
1249
|
+
"X-App-Token": applicationToken
|
|
1250
|
+
},
|
|
1251
|
+
signal: controller.signal
|
|
1252
|
+
});
|
|
1253
|
+
if (!response.ok) {
|
|
1254
|
+
if (response.status === 401 || response.status === 403) {
|
|
1255
|
+
throw new NeoFaceError("Token de aplicação inválido ou expirado", ErrorType.INVALID_TOKEN);
|
|
1256
|
+
}
|
|
1257
|
+
if (response.status === 404) {
|
|
1258
|
+
return false;
|
|
1259
|
+
}
|
|
1260
|
+
throw new NeoFaceError(`Falha ao validar token (${response.status})`, ErrorType.NETWORK);
|
|
1261
|
+
}
|
|
1262
|
+
const data = await response.json();
|
|
1263
|
+
return (data == null ? void 0 : data.is_valid) === true;
|
|
1264
|
+
} catch (error) {
|
|
1265
|
+
if (error instanceof Error) {
|
|
1266
|
+
if (error.name === "AbortError") {
|
|
1267
|
+
throw new NeoFaceError("Tempo de requisição excedido", ErrorType.NETWORK);
|
|
1268
|
+
}
|
|
1269
|
+
if (error instanceof NeoFaceError) {
|
|
1270
|
+
throw error;
|
|
1271
|
+
}
|
|
1272
|
+
throw new NeoFaceError(error.message, ErrorType.NETWORK);
|
|
1273
|
+
}
|
|
1274
|
+
throw new NeoFaceError("Erro desconhecido", ErrorType.NETWORK_ERROR);
|
|
1275
|
+
}
|
|
1276
|
+
};
|
|
1242
1277
|
const completeOnboarding = async (applicationToken, onboardingToken, faceImage, documentImage) => {
|
|
1243
1278
|
ensureSecureContext();
|
|
1244
1279
|
const controller = createTimeoutController();
|
|
@@ -1290,6 +1325,82 @@ const completeOnboarding = async (applicationToken, onboardingToken, faceImage,
|
|
|
1290
1325
|
throw new NeoFaceError("Erro desconhecido", ErrorType.NETWORK_ERROR);
|
|
1291
1326
|
}
|
|
1292
1327
|
};
|
|
1328
|
+
const completeOnboardingWithData = async (applicationToken, onboardingToken, personData, personImages, documentImage) => {
|
|
1329
|
+
ensureSecureContext();
|
|
1330
|
+
if (!personImages || personImages.length === 0) {
|
|
1331
|
+
throw new NeoFaceError("Pelo menos uma imagem da pessoa é obrigatória", ErrorType.VALIDATION_ERROR);
|
|
1332
|
+
}
|
|
1333
|
+
const controller = createTimeoutController();
|
|
1334
|
+
try {
|
|
1335
|
+
const compressedPersonImages = await Promise.all(
|
|
1336
|
+
personImages.map((img) => compressImage(img))
|
|
1337
|
+
);
|
|
1338
|
+
const compressedDocument = await compressImage(documentImage);
|
|
1339
|
+
const formData = new FormData();
|
|
1340
|
+
compressedPersonImages.forEach((img, index) => {
|
|
1341
|
+
formData.append("person_images", img, `person_${index + 1}.jpg`);
|
|
1342
|
+
});
|
|
1343
|
+
formData.append("document_image", compressedDocument, "document.jpg");
|
|
1344
|
+
if (personData.name) {
|
|
1345
|
+
formData.append("name", personData.name);
|
|
1346
|
+
}
|
|
1347
|
+
if (personData.birth_date) {
|
|
1348
|
+
formData.append("birth_date", personData.birth_date);
|
|
1349
|
+
}
|
|
1350
|
+
if (personData.cpf) {
|
|
1351
|
+
formData.append("cpf", personData.cpf);
|
|
1352
|
+
}
|
|
1353
|
+
if (personData.cnpj) {
|
|
1354
|
+
formData.append("cnpj", personData.cnpj);
|
|
1355
|
+
}
|
|
1356
|
+
if (personData.email) {
|
|
1357
|
+
formData.append("email", personData.email);
|
|
1358
|
+
}
|
|
1359
|
+
const response = await fetch(
|
|
1360
|
+
`${API_BASE_URL}/api/v1/onboardings/${encodeURIComponent(onboardingToken)}/complete`,
|
|
1361
|
+
{
|
|
1362
|
+
method: "POST",
|
|
1363
|
+
headers: {
|
|
1364
|
+
"X-App-Token": applicationToken
|
|
1365
|
+
},
|
|
1366
|
+
body: formData,
|
|
1367
|
+
signal: controller.signal
|
|
1368
|
+
}
|
|
1369
|
+
);
|
|
1370
|
+
const data = await response.json();
|
|
1371
|
+
if (!response.ok) {
|
|
1372
|
+
if (response.status === 401 || response.status === 403) {
|
|
1373
|
+
throw new NeoFaceError("Token de aplicação inválido ou expirado", ErrorType.INVALID_TOKEN);
|
|
1374
|
+
}
|
|
1375
|
+
if (response.status === 404) {
|
|
1376
|
+
throw new NeoFaceError("Link de onboarding não encontrado", ErrorType.NOT_FOUND);
|
|
1377
|
+
}
|
|
1378
|
+
throw new NeoFaceError((data == null ? void 0 : data.error) || (data == null ? void 0 : data.message) || "Falha ao concluir onboarding", ErrorType.NETWORK);
|
|
1379
|
+
}
|
|
1380
|
+
return {
|
|
1381
|
+
success: !!data.success,
|
|
1382
|
+
message: data.message || "Onboarding concluído com sucesso",
|
|
1383
|
+
person_id: data.person_id,
|
|
1384
|
+
identity_data_id: data.identity_data_id,
|
|
1385
|
+
confidence_score: data.confidence_score,
|
|
1386
|
+
processing_time: data.processing_time,
|
|
1387
|
+
liveness_passed: data.liveness_passed,
|
|
1388
|
+
document_verified: data.document_verified,
|
|
1389
|
+
person_created: data.person_created
|
|
1390
|
+
};
|
|
1391
|
+
} catch (error) {
|
|
1392
|
+
if (error instanceof Error) {
|
|
1393
|
+
if (error.name === "AbortError") {
|
|
1394
|
+
throw new NeoFaceError("Tempo de requisição excedido", ErrorType.NETWORK);
|
|
1395
|
+
}
|
|
1396
|
+
if (error instanceof NeoFaceError) {
|
|
1397
|
+
throw error;
|
|
1398
|
+
}
|
|
1399
|
+
throw new NeoFaceError(error.message, ErrorType.NETWORK);
|
|
1400
|
+
}
|
|
1401
|
+
throw new NeoFaceError("Erro desconhecido", ErrorType.NETWORK_ERROR);
|
|
1402
|
+
}
|
|
1403
|
+
};
|
|
1293
1404
|
const recognize = async (image, applicationToken) => {
|
|
1294
1405
|
ensureSecureContext();
|
|
1295
1406
|
const controller = createTimeoutController();
|
|
@@ -1332,7 +1443,7 @@ const recognize = async (image, applicationToken) => {
|
|
|
1332
1443
|
throw new NeoFaceError("Unknown error occurred", ErrorType.NETWORK_ERROR);
|
|
1333
1444
|
}
|
|
1334
1445
|
};
|
|
1335
|
-
const loginWithBiometric = async (image, applicationToken) => {
|
|
1446
|
+
const loginWithBiometric$1 = async (image, applicationToken) => {
|
|
1336
1447
|
var _a, _b, _c, _d, _e;
|
|
1337
1448
|
ensureSecureContext();
|
|
1338
1449
|
const controller = createTimeoutController();
|
|
@@ -1705,6 +1816,75 @@ const loginWithEmail = async (email, password, applicationToken) => {
|
|
|
1705
1816
|
throw new NeoFaceError("Unknown error occurred", ErrorType.NETWORK_ERROR);
|
|
1706
1817
|
}
|
|
1707
1818
|
};
|
|
1819
|
+
const identifyPerson = async (image, applicationToken) => {
|
|
1820
|
+
var _a, _b;
|
|
1821
|
+
ensureSecureContext();
|
|
1822
|
+
const controller = createTimeoutController();
|
|
1823
|
+
const compressedImage = await compressImage(image);
|
|
1824
|
+
const base64Image = await new Promise((resolve, reject) => {
|
|
1825
|
+
const reader = new FileReader();
|
|
1826
|
+
reader.onload = () => {
|
|
1827
|
+
const result = reader.result;
|
|
1828
|
+
resolve(result.split(",")[1]);
|
|
1829
|
+
};
|
|
1830
|
+
reader.onerror = reject;
|
|
1831
|
+
reader.readAsDataURL(compressedImage);
|
|
1832
|
+
});
|
|
1833
|
+
try {
|
|
1834
|
+
const response = await fetch(`${API_BASE_URL}/api/v1/external/recognition/face/`, {
|
|
1835
|
+
method: "POST",
|
|
1836
|
+
headers: {
|
|
1837
|
+
"Content-Type": "application/json",
|
|
1838
|
+
"X-App-Token": applicationToken
|
|
1839
|
+
},
|
|
1840
|
+
body: JSON.stringify({
|
|
1841
|
+
image: base64Image,
|
|
1842
|
+
purpose: "SIMPLE_IDENTIFICATION_F"
|
|
1843
|
+
}),
|
|
1844
|
+
signal: controller.signal
|
|
1845
|
+
});
|
|
1846
|
+
if (!response.ok) {
|
|
1847
|
+
if (response.status === 401 || response.status === 403) {
|
|
1848
|
+
throw new NeoFaceError("Invalid or expired application token", ErrorType.INVALID_TOKEN);
|
|
1849
|
+
}
|
|
1850
|
+
throw new NeoFaceError(`Server returned status ${response.status}`, ErrorType.NETWORK);
|
|
1851
|
+
}
|
|
1852
|
+
const data = await response.json();
|
|
1853
|
+
if (!data.success) {
|
|
1854
|
+
throw new NeoFaceError(data.message || "Person identification failed", ErrorType.RECOGNITION_FAILED);
|
|
1855
|
+
}
|
|
1856
|
+
const personName = ((_a = data.person) == null ? void 0 : _a.name) || "";
|
|
1857
|
+
const birthDateStr = (_b = data.person) == null ? void 0 : _b.birth_date;
|
|
1858
|
+
if (!personName) {
|
|
1859
|
+
throw new NeoFaceError("Person name not found in response", ErrorType.PERSON_NOT_FOUND);
|
|
1860
|
+
}
|
|
1861
|
+
if (!birthDateStr) {
|
|
1862
|
+
throw new NeoFaceError("Birth date not found in response", ErrorType.PERSON_NOT_FOUND);
|
|
1863
|
+
}
|
|
1864
|
+
const birthDate = new Date(birthDateStr);
|
|
1865
|
+
const today = /* @__PURE__ */ new Date();
|
|
1866
|
+
let age = today.getFullYear() - birthDate.getFullYear();
|
|
1867
|
+
const monthDiff = today.getMonth() - birthDate.getMonth();
|
|
1868
|
+
if (monthDiff < 0 || monthDiff === 0 && today.getDate() < birthDate.getDate()) {
|
|
1869
|
+
age--;
|
|
1870
|
+
}
|
|
1871
|
+
return {
|
|
1872
|
+
name: personName,
|
|
1873
|
+
age
|
|
1874
|
+
};
|
|
1875
|
+
} catch (error) {
|
|
1876
|
+
if (error instanceof Error) {
|
|
1877
|
+
if (error.name === "AbortError") {
|
|
1878
|
+
throw new NeoFaceError("Request timed out", ErrorType.NETWORK);
|
|
1879
|
+
}
|
|
1880
|
+
if (error instanceof NeoFaceError) {
|
|
1881
|
+
throw error;
|
|
1882
|
+
}
|
|
1883
|
+
throw new NeoFaceError(error.message, ErrorType.NETWORK);
|
|
1884
|
+
}
|
|
1885
|
+
throw new NeoFaceError("Unknown error occurred", ErrorType.NETWORK_ERROR);
|
|
1886
|
+
}
|
|
1887
|
+
};
|
|
1708
1888
|
const modalReducer$1 = (state, action) => {
|
|
1709
1889
|
switch (action.type) {
|
|
1710
1890
|
case "PERMISSION_GRANTED":
|
|
@@ -3147,7 +3327,7 @@ function BiometricRegistrationModal({
|
|
|
3147
3327
|
/* @__PURE__ */ jsxRuntimeExports.jsx("canvas", { ref: canvasRef })
|
|
3148
3328
|
] });
|
|
3149
3329
|
}
|
|
3150
|
-
|
|
3330
|
+
let BiometricCaptureModal$1 = class BiometricCaptureModal2 {
|
|
3151
3331
|
constructor(options) {
|
|
3152
3332
|
__publicField(this, "modal", null);
|
|
3153
3333
|
__publicField(this, "video", null);
|
|
@@ -3646,97 +3826,1106 @@ class BiometricCaptureModal {
|
|
|
3646
3826
|
`;
|
|
3647
3827
|
document.head.appendChild(style);
|
|
3648
3828
|
}
|
|
3649
|
-
}
|
|
3650
|
-
|
|
3651
|
-
|
|
3652
|
-
|
|
3653
|
-
|
|
3654
|
-
|
|
3655
|
-
|
|
3656
|
-
|
|
3657
|
-
|
|
3658
|
-
|
|
3659
|
-
|
|
3660
|
-
|
|
3661
|
-
document.body.appendChild(this.modalElement);
|
|
3662
|
-
}
|
|
3663
|
-
/**
|
|
3664
|
-
* Fecha o modal.
|
|
3665
|
-
*/
|
|
3666
|
-
close() {
|
|
3667
|
-
if (this.modalElement) {
|
|
3668
|
-
document.body.removeChild(this.modalElement);
|
|
3669
|
-
this.modalElement = null;
|
|
3670
|
-
}
|
|
3671
|
-
if (this.options.onCancel) {
|
|
3672
|
-
this.options.onCancel();
|
|
3673
|
-
}
|
|
3674
|
-
}
|
|
3675
|
-
createModal() {
|
|
3676
|
-
this.modalElement = document.createElement("div");
|
|
3677
|
-
this.modalElement.className = "neoface-modal";
|
|
3678
|
-
this.modalElement.innerHTML = `
|
|
3679
|
-
<div class="modal-content">
|
|
3680
|
-
<h2>${this.options.title || "Login com Email e Senha"}</h2>
|
|
3681
|
-
<p>${this.options.subtitle || "Informe seus dados para login."}</p>
|
|
3682
|
-
<form id="email-password-form">
|
|
3683
|
-
<input type="email" id="email" placeholder="Email" required />
|
|
3684
|
-
<input type="password" id="password" placeholder="Senha" required />
|
|
3685
|
-
<button type="submit">Entrar</button>
|
|
3686
|
-
</form>
|
|
3687
|
-
<button class="cancel-button">Cancelar</button>
|
|
3688
|
-
</div>
|
|
3689
|
-
`;
|
|
3690
|
-
const form = this.modalElement.querySelector("#email-password-form");
|
|
3691
|
-
form.addEventListener("submit", this.handleSubmit.bind(this));
|
|
3692
|
-
const cancelButton = this.modalElement.querySelector(".cancel-button");
|
|
3693
|
-
cancelButton.addEventListener("click", this.close.bind(this));
|
|
3694
|
-
}
|
|
3695
|
-
async handleSubmit(event) {
|
|
3696
|
-
event.preventDefault();
|
|
3697
|
-
const emailInput = this.modalElement.querySelector("#email");
|
|
3698
|
-
const passwordInput = this.modalElement.querySelector("#password");
|
|
3699
|
-
const email = emailInput.value.trim();
|
|
3700
|
-
const password = passwordInput.value.trim();
|
|
3701
|
-
if (!email || !password) {
|
|
3702
|
-
this.options.onError(new NeoFaceError("Email e senha são obrigatórios", ErrorType.VALIDATION_ERROR));
|
|
3703
|
-
return;
|
|
3704
|
-
}
|
|
3705
|
-
try {
|
|
3706
|
-
const result = await loginWithEmail(email, password, this.options.applicationToken);
|
|
3707
|
-
this.options.onSuccess(result);
|
|
3708
|
-
this.close();
|
|
3709
|
-
} catch (error) {
|
|
3710
|
-
this.options.onError(error);
|
|
3711
|
-
}
|
|
3712
|
-
}
|
|
3713
|
-
}
|
|
3714
|
-
async function startFaceLogin(options) {
|
|
3715
|
-
try {
|
|
3716
|
-
const isValidToken = await validateToken(options.applicationToken);
|
|
3717
|
-
if (!isValidToken) {
|
|
3718
|
-
options.onError(new NeoFaceError("Token de aplicação inválido", ErrorType.INVALID_TOKEN));
|
|
3719
|
-
return;
|
|
3829
|
+
};
|
|
3830
|
+
function BiometricStatusOverlayComponent({ status, onClose }) {
|
|
3831
|
+
const [isVisible, setIsVisible] = useState(true);
|
|
3832
|
+
useEffect(() => {
|
|
3833
|
+
if (status === "success") {
|
|
3834
|
+
const timer = setTimeout(() => {
|
|
3835
|
+
setIsVisible(false);
|
|
3836
|
+
setTimeout(() => {
|
|
3837
|
+
onClose == null ? void 0 : onClose();
|
|
3838
|
+
}, 300);
|
|
3839
|
+
}, 800);
|
|
3840
|
+
return () => clearTimeout(timer);
|
|
3720
3841
|
}
|
|
3721
|
-
|
|
3722
|
-
|
|
3723
|
-
|
|
3724
|
-
|
|
3725
|
-
|
|
3726
|
-
|
|
3727
|
-
|
|
3728
|
-
|
|
3729
|
-
options.onError(error);
|
|
3842
|
+
}, [status, onClose]);
|
|
3843
|
+
if (!isVisible)
|
|
3844
|
+
return null;
|
|
3845
|
+
return /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
|
|
3846
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("style", { children: `
|
|
3847
|
+
@keyframes fadeIn {
|
|
3848
|
+
from { opacity: 0; transform: scale(0.9); }
|
|
3849
|
+
to { opacity: 1; transform: scale(1); }
|
|
3730
3850
|
}
|
|
3731
|
-
|
|
3732
|
-
|
|
3733
|
-
|
|
3734
|
-
|
|
3735
|
-
|
|
3736
|
-
|
|
3737
|
-
|
|
3738
|
-
|
|
3739
|
-
|
|
3851
|
+
|
|
3852
|
+
@keyframes fadeOut {
|
|
3853
|
+
from { opacity: 1; transform: scale(1); }
|
|
3854
|
+
to { opacity: 0; transform: scale(0.9); }
|
|
3855
|
+
}
|
|
3856
|
+
|
|
3857
|
+
@keyframes pulse {
|
|
3858
|
+
0%, 100% {
|
|
3859
|
+
transform: scale(1);
|
|
3860
|
+
opacity: 1;
|
|
3861
|
+
}
|
|
3862
|
+
50% {
|
|
3863
|
+
transform: scale(1.1);
|
|
3864
|
+
opacity: 0.9;
|
|
3865
|
+
}
|
|
3866
|
+
}
|
|
3867
|
+
|
|
3868
|
+
@keyframes spin {
|
|
3869
|
+
from { transform: rotate(0deg); }
|
|
3870
|
+
to { transform: rotate(360deg); }
|
|
3871
|
+
}
|
|
3872
|
+
|
|
3873
|
+
@keyframes checkmark {
|
|
3874
|
+
0% {
|
|
3875
|
+
stroke-dashoffset: 100;
|
|
3876
|
+
opacity: 0;
|
|
3877
|
+
}
|
|
3878
|
+
50% {
|
|
3879
|
+
opacity: 1;
|
|
3880
|
+
}
|
|
3881
|
+
100% {
|
|
3882
|
+
stroke-dashoffset: 0;
|
|
3883
|
+
opacity: 1;
|
|
3884
|
+
}
|
|
3885
|
+
}
|
|
3886
|
+
|
|
3887
|
+
@keyframes errorShake {
|
|
3888
|
+
0%, 100% { transform: translateX(0) scale(1); }
|
|
3889
|
+
10%, 30%, 50%, 70%, 90% { transform: translateX(-4px) scale(1); }
|
|
3890
|
+
20%, 40%, 60%, 80% { transform: translateX(4px) scale(1); }
|
|
3891
|
+
}
|
|
3892
|
+
|
|
3893
|
+
@keyframes scaleIn {
|
|
3894
|
+
0% {
|
|
3895
|
+
transform: scale(0);
|
|
3896
|
+
opacity: 0;
|
|
3897
|
+
}
|
|
3898
|
+
50% {
|
|
3899
|
+
transform: scale(1.1);
|
|
3900
|
+
}
|
|
3901
|
+
100% {
|
|
3902
|
+
transform: scale(1);
|
|
3903
|
+
opacity: 1;
|
|
3904
|
+
}
|
|
3905
|
+
}
|
|
3906
|
+
|
|
3907
|
+
.neofaceid-overlay-container {
|
|
3908
|
+
position: fixed;
|
|
3909
|
+
top: 0;
|
|
3910
|
+
left: 0;
|
|
3911
|
+
right: 0;
|
|
3912
|
+
bottom: 0;
|
|
3913
|
+
display: flex;
|
|
3914
|
+
align-items: center;
|
|
3915
|
+
justify-content: center;
|
|
3916
|
+
z-index: 10000;
|
|
3917
|
+
pointer-events: none;
|
|
3918
|
+
animation: fadeIn 0.3s ease-out;
|
|
3919
|
+
}
|
|
3920
|
+
|
|
3921
|
+
.neofaceid-overlay-container.fade-out {
|
|
3922
|
+
animation: fadeOut 0.3s ease-out;
|
|
3923
|
+
}
|
|
3924
|
+
|
|
3925
|
+
.neofaceid-overlay-content {
|
|
3926
|
+
display: flex;
|
|
3927
|
+
flex-direction: column;
|
|
3928
|
+
align-items: center;
|
|
3929
|
+
justify-content: center;
|
|
3930
|
+
gap: 24px;
|
|
3931
|
+
pointer-events: auto;
|
|
3932
|
+
}
|
|
3933
|
+
|
|
3934
|
+
.neofaceid-logo-wrapper {
|
|
3935
|
+
position: relative;
|
|
3936
|
+
width: 120px;
|
|
3937
|
+
height: 120px;
|
|
3938
|
+
display: flex;
|
|
3939
|
+
align-items: center;
|
|
3940
|
+
justify-content: center;
|
|
3941
|
+
}
|
|
3942
|
+
|
|
3943
|
+
.neofaceid-logo {
|
|
3944
|
+
width: 80px;
|
|
3945
|
+
height: 80px;
|
|
3946
|
+
object-fit: contain;
|
|
3947
|
+
filter: drop-shadow(0 4px 12px rgba(124, 58, 237, 0.3));
|
|
3948
|
+
}
|
|
3949
|
+
|
|
3950
|
+
.neofaceid-logo.pulsing {
|
|
3951
|
+
animation: pulse 2s ease-in-out infinite;
|
|
3952
|
+
}
|
|
3953
|
+
|
|
3954
|
+
.neofaceid-status-icon {
|
|
3955
|
+
position: absolute;
|
|
3956
|
+
inset: 0;
|
|
3957
|
+
display: flex;
|
|
3958
|
+
align-items: center;
|
|
3959
|
+
justify-content: center;
|
|
3960
|
+
}
|
|
3961
|
+
|
|
3962
|
+
.neofaceid-success-icon {
|
|
3963
|
+
width: 60px;
|
|
3964
|
+
height: 60px;
|
|
3965
|
+
color: #10b981;
|
|
3966
|
+
animation: scaleIn 0.6s ease-out;
|
|
3967
|
+
}
|
|
3968
|
+
|
|
3969
|
+
.neofaceid-success-icon svg circle {
|
|
3970
|
+
stroke-dasharray: 100;
|
|
3971
|
+
stroke-dashoffset: 100;
|
|
3972
|
+
animation: checkmark 0.6s ease-out 0.2s forwards;
|
|
3973
|
+
}
|
|
3974
|
+
|
|
3975
|
+
.neofaceid-error-icon {
|
|
3976
|
+
width: 60px;
|
|
3977
|
+
height: 60px;
|
|
3978
|
+
color: #ef4444;
|
|
3979
|
+
animation: errorShake 0.5s ease-out, scaleIn 0.4s ease-out;
|
|
3980
|
+
}
|
|
3981
|
+
|
|
3982
|
+
/* Responsive */
|
|
3983
|
+
@media (max-width: 640px) {
|
|
3984
|
+
.neofaceid-logo-wrapper {
|
|
3985
|
+
width: 100px;
|
|
3986
|
+
height: 100px;
|
|
3987
|
+
}
|
|
3988
|
+
|
|
3989
|
+
.neofaceid-logo {
|
|
3990
|
+
width: 70px;
|
|
3991
|
+
height: 70px;
|
|
3992
|
+
}
|
|
3993
|
+
}
|
|
3994
|
+
` }),
|
|
3995
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: `neofaceid-overlay-container ${!isVisible ? "fade-out" : ""}`, children: /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "neofaceid-overlay-content", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "neofaceid-logo-wrapper", children: [
|
|
3996
|
+
(status === "preparing" || status === "verifying") && /* @__PURE__ */ jsxRuntimeExports.jsx(jsxRuntimeExports.Fragment, { children: /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
3997
|
+
"img",
|
|
3998
|
+
{
|
|
3999
|
+
src: "/public/logo-icone-no-backgorund.png",
|
|
4000
|
+
alt: "NeoFaceId",
|
|
4001
|
+
className: `neofaceid-logo ${status === "verifying" ? "pulsing" : ""}`,
|
|
4002
|
+
onError: (e) => {
|
|
4003
|
+
var _a;
|
|
4004
|
+
const img = e.target;
|
|
4005
|
+
img.style.display = "none";
|
|
4006
|
+
const fallback = document.createElement("div");
|
|
4007
|
+
fallback.innerHTML = `
|
|
4008
|
+
<svg width="80" height="80" viewBox="0 0 80 80" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
4009
|
+
<circle cx="40" cy="40" r="35" fill="#7c3aed" opacity="0.2"/>
|
|
4010
|
+
<path d="M40 20 L40 60 M20 40 L60 40" stroke="#7c3aed" stroke-width="4" stroke-linecap="round"/>
|
|
4011
|
+
</svg>
|
|
4012
|
+
`;
|
|
4013
|
+
fallback.style.cssText = "width: 80px; height: 80px; display: flex; align-items: center; justify-content: center;";
|
|
4014
|
+
(_a = img.parentElement) == null ? void 0 : _a.appendChild(fallback);
|
|
4015
|
+
}
|
|
4016
|
+
}
|
|
4017
|
+
) }),
|
|
4018
|
+
status === "success" && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "neofaceid-status-icon", children: /* @__PURE__ */ jsxRuntimeExports.jsxs(
|
|
4019
|
+
"svg",
|
|
4020
|
+
{
|
|
4021
|
+
className: "neofaceid-success-icon",
|
|
4022
|
+
viewBox: "0 0 60 60",
|
|
4023
|
+
fill: "none",
|
|
4024
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
4025
|
+
children: [
|
|
4026
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
4027
|
+
"circle",
|
|
4028
|
+
{
|
|
4029
|
+
cx: "30",
|
|
4030
|
+
cy: "30",
|
|
4031
|
+
r: "28",
|
|
4032
|
+
stroke: "currentColor",
|
|
4033
|
+
strokeWidth: "3",
|
|
4034
|
+
fill: "none"
|
|
4035
|
+
}
|
|
4036
|
+
),
|
|
4037
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
4038
|
+
"path",
|
|
4039
|
+
{
|
|
4040
|
+
d: "M15 30 L25 40 L45 20",
|
|
4041
|
+
stroke: "currentColor",
|
|
4042
|
+
strokeWidth: "3",
|
|
4043
|
+
strokeLinecap: "round",
|
|
4044
|
+
strokeLinejoin: "round",
|
|
4045
|
+
fill: "none"
|
|
4046
|
+
}
|
|
4047
|
+
)
|
|
4048
|
+
]
|
|
4049
|
+
}
|
|
4050
|
+
) }),
|
|
4051
|
+
status === "error" && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "neofaceid-status-icon", children: /* @__PURE__ */ jsxRuntimeExports.jsxs(
|
|
4052
|
+
"svg",
|
|
4053
|
+
{
|
|
4054
|
+
className: "neofaceid-error-icon",
|
|
4055
|
+
viewBox: "0 0 60 60",
|
|
4056
|
+
fill: "none",
|
|
4057
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
4058
|
+
children: [
|
|
4059
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
4060
|
+
"circle",
|
|
4061
|
+
{
|
|
4062
|
+
cx: "30",
|
|
4063
|
+
cy: "30",
|
|
4064
|
+
r: "28",
|
|
4065
|
+
stroke: "currentColor",
|
|
4066
|
+
strokeWidth: "3",
|
|
4067
|
+
fill: "none"
|
|
4068
|
+
}
|
|
4069
|
+
),
|
|
4070
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
4071
|
+
"path",
|
|
4072
|
+
{
|
|
4073
|
+
d: "M20 20 L40 40 M40 20 L20 40",
|
|
4074
|
+
stroke: "currentColor",
|
|
4075
|
+
strokeWidth: "3",
|
|
4076
|
+
strokeLinecap: "round"
|
|
4077
|
+
}
|
|
4078
|
+
)
|
|
4079
|
+
]
|
|
4080
|
+
}
|
|
4081
|
+
) })
|
|
4082
|
+
] }) }) })
|
|
4083
|
+
] });
|
|
4084
|
+
}
|
|
4085
|
+
class BiometricStatusOverlay {
|
|
4086
|
+
constructor() {
|
|
4087
|
+
__publicField(this, "container", null);
|
|
4088
|
+
__publicField(this, "root", null);
|
|
4089
|
+
__publicField(this, "currentStatus", "preparing");
|
|
4090
|
+
}
|
|
4091
|
+
/**
|
|
4092
|
+
* Cria e exibe o overlay
|
|
4093
|
+
*/
|
|
4094
|
+
show(status = "preparing") {
|
|
4095
|
+
if (this.container) {
|
|
4096
|
+
this.updateStatus(status);
|
|
4097
|
+
return;
|
|
4098
|
+
}
|
|
4099
|
+
this.container = document.createElement("div");
|
|
4100
|
+
this.container.id = "neofaceid-status-overlay";
|
|
4101
|
+
document.body.appendChild(this.container);
|
|
4102
|
+
this.root = createRoot(this.container);
|
|
4103
|
+
this.currentStatus = status;
|
|
4104
|
+
this.render();
|
|
4105
|
+
}
|
|
4106
|
+
/**
|
|
4107
|
+
* Atualiza o status do overlay
|
|
4108
|
+
*/
|
|
4109
|
+
updateStatus(status) {
|
|
4110
|
+
this.currentStatus = status;
|
|
4111
|
+
if (this.root) {
|
|
4112
|
+
this.render();
|
|
4113
|
+
}
|
|
4114
|
+
}
|
|
4115
|
+
/**
|
|
4116
|
+
* Fecha e remove o overlay
|
|
4117
|
+
*/
|
|
4118
|
+
close() {
|
|
4119
|
+
if (this.container) {
|
|
4120
|
+
if (this.root) {
|
|
4121
|
+
this.root.render(
|
|
4122
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
4123
|
+
BiometricStatusOverlayComponent,
|
|
4124
|
+
{
|
|
4125
|
+
status: this.currentStatus,
|
|
4126
|
+
onClose: () => {
|
|
4127
|
+
if (this.container && document.body.contains(this.container)) {
|
|
4128
|
+
document.body.removeChild(this.container);
|
|
4129
|
+
}
|
|
4130
|
+
this.container = null;
|
|
4131
|
+
this.root = null;
|
|
4132
|
+
}
|
|
4133
|
+
}
|
|
4134
|
+
)
|
|
4135
|
+
);
|
|
4136
|
+
}
|
|
4137
|
+
setTimeout(() => {
|
|
4138
|
+
if (this.container && document.body.contains(this.container)) {
|
|
4139
|
+
document.body.removeChild(this.container);
|
|
4140
|
+
}
|
|
4141
|
+
this.container = null;
|
|
4142
|
+
this.root = null;
|
|
4143
|
+
}, 300);
|
|
4144
|
+
}
|
|
4145
|
+
}
|
|
4146
|
+
/**
|
|
4147
|
+
* Renderiza o componente
|
|
4148
|
+
*/
|
|
4149
|
+
render() {
|
|
4150
|
+
if (!this.root || !this.container)
|
|
4151
|
+
return;
|
|
4152
|
+
this.root.render(
|
|
4153
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(BiometricStatusOverlayComponent, { status: this.currentStatus, onClose: () => this.close() })
|
|
4154
|
+
);
|
|
4155
|
+
}
|
|
4156
|
+
}
|
|
4157
|
+
function FallbackPromptComponent({
|
|
4158
|
+
error,
|
|
4159
|
+
onRetry,
|
|
4160
|
+
onCredentials,
|
|
4161
|
+
onCancel
|
|
4162
|
+
}) {
|
|
4163
|
+
return /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
|
|
4164
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("style", { children: `
|
|
4165
|
+
@keyframes slideUp {
|
|
4166
|
+
from {
|
|
4167
|
+
opacity: 0;
|
|
4168
|
+
transform: translateY(20px);
|
|
4169
|
+
}
|
|
4170
|
+
to {
|
|
4171
|
+
opacity: 1;
|
|
4172
|
+
transform: translateY(0);
|
|
4173
|
+
}
|
|
4174
|
+
}
|
|
4175
|
+
|
|
4176
|
+
@keyframes fadeIn {
|
|
4177
|
+
from { opacity: 0; }
|
|
4178
|
+
to { opacity: 1; }
|
|
4179
|
+
}
|
|
4180
|
+
|
|
4181
|
+
.neofaceid-fallback-overlay {
|
|
4182
|
+
position: fixed;
|
|
4183
|
+
inset: 0;
|
|
4184
|
+
background: rgba(0, 0, 0, 0.6);
|
|
4185
|
+
backdrop-filter: blur(8px);
|
|
4186
|
+
display: flex;
|
|
4187
|
+
align-items: center;
|
|
4188
|
+
justify-content: center;
|
|
4189
|
+
z-index: 10001;
|
|
4190
|
+
padding: 20px;
|
|
4191
|
+
animation: fadeIn 0.3s ease-out;
|
|
4192
|
+
}
|
|
4193
|
+
|
|
4194
|
+
.neofaceid-fallback-prompt {
|
|
4195
|
+
background: linear-gradient(135deg, #1a1a2e 0%, #16213e 100%);
|
|
4196
|
+
border-radius: 20px;
|
|
4197
|
+
padding: 32px;
|
|
4198
|
+
max-width: 400px;
|
|
4199
|
+
width: 100%;
|
|
4200
|
+
box-shadow: 0 24px 48px rgba(0, 0, 0, 0.4), 0 0 0 1px rgba(255, 255, 255, 0.1);
|
|
4201
|
+
animation: slideUp 0.4s ease-out;
|
|
4202
|
+
text-align: center;
|
|
4203
|
+
}
|
|
4204
|
+
|
|
4205
|
+
.neofaceid-fallback-icon {
|
|
4206
|
+
width: 64px;
|
|
4207
|
+
height: 64px;
|
|
4208
|
+
margin: 0 auto 20px;
|
|
4209
|
+
color: #ef4444;
|
|
4210
|
+
animation: scaleIn 0.4s ease-out;
|
|
4211
|
+
}
|
|
4212
|
+
|
|
4213
|
+
@keyframes scaleIn {
|
|
4214
|
+
0% {
|
|
4215
|
+
transform: scale(0);
|
|
4216
|
+
opacity: 0;
|
|
4217
|
+
}
|
|
4218
|
+
50% {
|
|
4219
|
+
transform: scale(1.1);
|
|
4220
|
+
}
|
|
4221
|
+
100% {
|
|
4222
|
+
transform: scale(1);
|
|
4223
|
+
opacity: 1;
|
|
4224
|
+
}
|
|
4225
|
+
}
|
|
4226
|
+
|
|
4227
|
+
.neofaceid-fallback-title {
|
|
4228
|
+
font-size: 22px;
|
|
4229
|
+
font-weight: 700;
|
|
4230
|
+
color: white;
|
|
4231
|
+
margin: 0 0 12px 0;
|
|
4232
|
+
}
|
|
4233
|
+
|
|
4234
|
+
.neofaceid-fallback-message {
|
|
4235
|
+
font-size: 15px;
|
|
4236
|
+
color: rgba(255, 255, 255, 0.7);
|
|
4237
|
+
margin: 0 0 28px 0;
|
|
4238
|
+
line-height: 1.5;
|
|
4239
|
+
}
|
|
4240
|
+
|
|
4241
|
+
.neofaceid-fallback-actions {
|
|
4242
|
+
display: flex;
|
|
4243
|
+
flex-direction: column;
|
|
4244
|
+
gap: 12px;
|
|
4245
|
+
}
|
|
4246
|
+
|
|
4247
|
+
.neofaceid-fallback-button {
|
|
4248
|
+
padding: 14px 24px;
|
|
4249
|
+
border-radius: 12px;
|
|
4250
|
+
border: none;
|
|
4251
|
+
font-size: 16px;
|
|
4252
|
+
font-weight: 600;
|
|
4253
|
+
cursor: pointer;
|
|
4254
|
+
transition: all 0.2s;
|
|
4255
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
4256
|
+
}
|
|
4257
|
+
|
|
4258
|
+
.neofaceid-fallback-button-primary {
|
|
4259
|
+
background: linear-gradient(135deg, #7c3aed 0%, #6d28d9 100%);
|
|
4260
|
+
color: white;
|
|
4261
|
+
box-shadow: 0 4px 16px rgba(124, 58, 237, 0.3);
|
|
4262
|
+
}
|
|
4263
|
+
|
|
4264
|
+
.neofaceid-fallback-button-primary:hover {
|
|
4265
|
+
transform: translateY(-2px);
|
|
4266
|
+
box-shadow: 0 8px 24px rgba(124, 58, 237, 0.4);
|
|
4267
|
+
}
|
|
4268
|
+
|
|
4269
|
+
.neofaceid-fallback-button-primary:active {
|
|
4270
|
+
transform: translateY(0);
|
|
4271
|
+
}
|
|
4272
|
+
|
|
4273
|
+
.neofaceid-fallback-button-secondary {
|
|
4274
|
+
background: rgba(255, 255, 255, 0.1);
|
|
4275
|
+
color: white;
|
|
4276
|
+
border: 1px solid rgba(255, 255, 255, 0.2);
|
|
4277
|
+
}
|
|
4278
|
+
|
|
4279
|
+
.neofaceid-fallback-button-secondary:hover {
|
|
4280
|
+
background: rgba(255, 255, 255, 0.15);
|
|
4281
|
+
border-color: rgba(255, 255, 255, 0.3);
|
|
4282
|
+
}
|
|
4283
|
+
|
|
4284
|
+
.neofaceid-fallback-button-ghost {
|
|
4285
|
+
background: transparent;
|
|
4286
|
+
color: rgba(255, 255, 255, 0.7);
|
|
4287
|
+
padding: 10px;
|
|
4288
|
+
}
|
|
4289
|
+
|
|
4290
|
+
.neofaceid-fallback-button-ghost:hover {
|
|
4291
|
+
color: white;
|
|
4292
|
+
background: rgba(255, 255, 255, 0.05);
|
|
4293
|
+
}
|
|
4294
|
+
|
|
4295
|
+
@media (max-width: 640px) {
|
|
4296
|
+
.neofaceid-fallback-prompt {
|
|
4297
|
+
padding: 24px;
|
|
4298
|
+
border-radius: 16px;
|
|
4299
|
+
}
|
|
4300
|
+
|
|
4301
|
+
.neofaceid-fallback-title {
|
|
4302
|
+
font-size: 20px;
|
|
4303
|
+
}
|
|
4304
|
+
}
|
|
4305
|
+
` }),
|
|
4306
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "neofaceid-fallback-overlay", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "neofaceid-fallback-prompt", children: [
|
|
4307
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "neofaceid-fallback-icon", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("svg", { viewBox: "0 0 64 64", fill: "none", xmlns: "http://www.w3.org/2000/svg", children: [
|
|
4308
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("circle", { cx: "32", cy: "32", r: "30", stroke: "currentColor", strokeWidth: "3", fill: "none" }),
|
|
4309
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
4310
|
+
"path",
|
|
4311
|
+
{
|
|
4312
|
+
d: "M20 20 L44 44 M44 20 L20 44",
|
|
4313
|
+
stroke: "currentColor",
|
|
4314
|
+
strokeWidth: "3",
|
|
4315
|
+
strokeLinecap: "round"
|
|
4316
|
+
}
|
|
4317
|
+
)
|
|
4318
|
+
] }) }),
|
|
4319
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("h3", { className: "neofaceid-fallback-title", children: "Não foi possível reconhecer" }),
|
|
4320
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "neofaceid-fallback-message", children: (error == null ? void 0 : error.message) || "Não conseguimos reconhecer sua face. Tente novamente ou use email e senha." }),
|
|
4321
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "neofaceid-fallback-actions", children: [
|
|
4322
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
4323
|
+
"button",
|
|
4324
|
+
{
|
|
4325
|
+
type: "button",
|
|
4326
|
+
className: "neofaceid-fallback-button neofaceid-fallback-button-primary",
|
|
4327
|
+
onClick: onRetry,
|
|
4328
|
+
children: "Tentar Novamente"
|
|
4329
|
+
}
|
|
4330
|
+
),
|
|
4331
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
4332
|
+
"button",
|
|
4333
|
+
{
|
|
4334
|
+
type: "button",
|
|
4335
|
+
className: "neofaceid-fallback-button neofaceid-fallback-button-secondary",
|
|
4336
|
+
onClick: onCredentials,
|
|
4337
|
+
children: "Entrar com Email e Senha"
|
|
4338
|
+
}
|
|
4339
|
+
),
|
|
4340
|
+
onCancel && /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
4341
|
+
"button",
|
|
4342
|
+
{
|
|
4343
|
+
type: "button",
|
|
4344
|
+
className: "neofaceid-fallback-button neofaceid-fallback-button-ghost",
|
|
4345
|
+
onClick: onCancel,
|
|
4346
|
+
children: "Cancelar"
|
|
4347
|
+
}
|
|
4348
|
+
)
|
|
4349
|
+
] })
|
|
4350
|
+
] }) })
|
|
4351
|
+
] });
|
|
4352
|
+
}
|
|
4353
|
+
class FallbackPrompt {
|
|
4354
|
+
constructor() {
|
|
4355
|
+
__publicField(this, "container", null);
|
|
4356
|
+
__publicField(this, "root", null);
|
|
4357
|
+
}
|
|
4358
|
+
/**
|
|
4359
|
+
* Exibe o prompt de fallback
|
|
4360
|
+
*/
|
|
4361
|
+
show(error, onRetry, onCredentials, onCancel) {
|
|
4362
|
+
if (this.container) {
|
|
4363
|
+
return;
|
|
4364
|
+
}
|
|
4365
|
+
this.container = document.createElement("div");
|
|
4366
|
+
this.container.id = "neofaceid-fallback-prompt";
|
|
4367
|
+
document.body.appendChild(this.container);
|
|
4368
|
+
this.root = createRoot(this.container);
|
|
4369
|
+
this.render(error, onRetry, onCredentials, onCancel);
|
|
4370
|
+
}
|
|
4371
|
+
/**
|
|
4372
|
+
* Fecha o prompt
|
|
4373
|
+
*/
|
|
4374
|
+
close() {
|
|
4375
|
+
if (this.container && document.body.contains(this.container)) {
|
|
4376
|
+
if (this.root) {
|
|
4377
|
+
this.root.unmount();
|
|
4378
|
+
}
|
|
4379
|
+
document.body.removeChild(this.container);
|
|
4380
|
+
this.container = null;
|
|
4381
|
+
this.root = null;
|
|
4382
|
+
}
|
|
4383
|
+
}
|
|
4384
|
+
/**
|
|
4385
|
+
* Renderiza o componente
|
|
4386
|
+
*/
|
|
4387
|
+
render(error, onRetry, onCredentials, onCancel) {
|
|
4388
|
+
if (!this.root || !this.container)
|
|
4389
|
+
return;
|
|
4390
|
+
this.root.render(
|
|
4391
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
4392
|
+
FallbackPromptComponent,
|
|
4393
|
+
{
|
|
4394
|
+
error,
|
|
4395
|
+
onRetry: () => {
|
|
4396
|
+
this.close();
|
|
4397
|
+
onRetry();
|
|
4398
|
+
},
|
|
4399
|
+
onCredentials: () => {
|
|
4400
|
+
this.close();
|
|
4401
|
+
onCredentials();
|
|
4402
|
+
},
|
|
4403
|
+
onCancel: () => {
|
|
4404
|
+
this.close();
|
|
4405
|
+
onCancel == null ? void 0 : onCancel();
|
|
4406
|
+
}
|
|
4407
|
+
}
|
|
4408
|
+
)
|
|
4409
|
+
);
|
|
4410
|
+
}
|
|
4411
|
+
}
|
|
4412
|
+
class EmailPasswordModal {
|
|
4413
|
+
constructor(options) {
|
|
4414
|
+
__publicField(this, "options");
|
|
4415
|
+
__publicField(this, "modalElement", null);
|
|
4416
|
+
this.options = options;
|
|
4417
|
+
}
|
|
4418
|
+
/**
|
|
4419
|
+
* Abre o modal de login com email e senha.
|
|
4420
|
+
*/
|
|
4421
|
+
async open() {
|
|
4422
|
+
this.createModal();
|
|
4423
|
+
document.body.appendChild(this.modalElement);
|
|
4424
|
+
}
|
|
4425
|
+
/**
|
|
4426
|
+
* Fecha o modal.
|
|
4427
|
+
*/
|
|
4428
|
+
close() {
|
|
4429
|
+
if (this.modalElement) {
|
|
4430
|
+
document.body.removeChild(this.modalElement);
|
|
4431
|
+
this.modalElement = null;
|
|
4432
|
+
}
|
|
4433
|
+
if (this.options.onCancel) {
|
|
4434
|
+
this.options.onCancel();
|
|
4435
|
+
}
|
|
4436
|
+
}
|
|
4437
|
+
createModal() {
|
|
4438
|
+
this.addStyles();
|
|
4439
|
+
this.modalElement = document.createElement("div");
|
|
4440
|
+
this.modalElement.className = "neoface-email-password-modal";
|
|
4441
|
+
this.modalElement.innerHTML = `
|
|
4442
|
+
<div class="neoface-email-modal-overlay">
|
|
4443
|
+
<div class="neoface-email-modal-content">
|
|
4444
|
+
<h2>${this.options.title || "Login com Email e Senha"}</h2>
|
|
4445
|
+
<p>${this.options.subtitle || "Informe seus dados para login."}</p>
|
|
4446
|
+
<form id="email-password-form">
|
|
4447
|
+
<input type="email" id="email" placeholder="Email" required />
|
|
4448
|
+
<input type="password" id="password" placeholder="Senha" required />
|
|
4449
|
+
<button type="submit" class="neoface-email-submit-btn">Entrar</button>
|
|
4450
|
+
</form>
|
|
4451
|
+
<button class="neoface-email-cancel-btn">Cancelar</button>
|
|
4452
|
+
|
|
4453
|
+
<!-- Branding -->
|
|
4454
|
+
<div class="neoface-email-branding">
|
|
4455
|
+
<img src="/public/logo-icone-no-backgorund.png" alt="NeoFaceId" class="neoface-email-brand-logo" onerror="this.style.display='none'" />
|
|
4456
|
+
<span>NeoFaceId by</span>
|
|
4457
|
+
<span class="neoface-email-brand-name">OCTA</span>
|
|
4458
|
+
</div>
|
|
4459
|
+
</div>
|
|
4460
|
+
</div>
|
|
4461
|
+
`;
|
|
4462
|
+
const form = this.modalElement.querySelector("#email-password-form");
|
|
4463
|
+
form.addEventListener("submit", this.handleSubmit.bind(this));
|
|
4464
|
+
const cancelButton = this.modalElement.querySelector(
|
|
4465
|
+
".neoface-email-cancel-btn"
|
|
4466
|
+
);
|
|
4467
|
+
cancelButton.addEventListener("click", this.close.bind(this));
|
|
4468
|
+
}
|
|
4469
|
+
addStyles() {
|
|
4470
|
+
const styleId = "neoface-email-password-modal-styles";
|
|
4471
|
+
const existingStyles = document.getElementById(styleId);
|
|
4472
|
+
if (existingStyles) {
|
|
4473
|
+
return;
|
|
4474
|
+
}
|
|
4475
|
+
const style = document.createElement("style");
|
|
4476
|
+
style.id = styleId;
|
|
4477
|
+
style.textContent = `
|
|
4478
|
+
.neoface-email-password-modal {
|
|
4479
|
+
position: fixed;
|
|
4480
|
+
top: 0;
|
|
4481
|
+
left: 0;
|
|
4482
|
+
width: 100%;
|
|
4483
|
+
height: 100%;
|
|
4484
|
+
z-index: 10002;
|
|
4485
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
4486
|
+
}
|
|
4487
|
+
|
|
4488
|
+
.neoface-email-modal-overlay {
|
|
4489
|
+
position: absolute;
|
|
4490
|
+
inset: 0;
|
|
4491
|
+
background: rgba(0, 0, 0, 0.6);
|
|
4492
|
+
backdrop-filter: blur(8px);
|
|
4493
|
+
display: flex;
|
|
4494
|
+
align-items: center;
|
|
4495
|
+
justify-content: center;
|
|
4496
|
+
padding: 20px;
|
|
4497
|
+
animation: fadeIn 0.3s ease-out;
|
|
4498
|
+
}
|
|
4499
|
+
|
|
4500
|
+
@keyframes fadeIn {
|
|
4501
|
+
from { opacity: 0; }
|
|
4502
|
+
to { opacity: 1; }
|
|
4503
|
+
}
|
|
4504
|
+
|
|
4505
|
+
@keyframes slideUp {
|
|
4506
|
+
from {
|
|
4507
|
+
opacity: 0;
|
|
4508
|
+
transform: translateY(20px);
|
|
4509
|
+
}
|
|
4510
|
+
to {
|
|
4511
|
+
opacity: 1;
|
|
4512
|
+
transform: translateY(0);
|
|
4513
|
+
}
|
|
4514
|
+
}
|
|
4515
|
+
|
|
4516
|
+
.neoface-email-modal-content {
|
|
4517
|
+
background: linear-gradient(135deg, #1a1a2e 0%, #16213e 100%);
|
|
4518
|
+
border-radius: 20px;
|
|
4519
|
+
padding: 32px;
|
|
4520
|
+
max-width: 400px;
|
|
4521
|
+
width: 100%;
|
|
4522
|
+
box-shadow: 0 24px 48px rgba(0, 0, 0, 0.4), 0 0 0 1px rgba(255, 255, 255, 0.1);
|
|
4523
|
+
animation: slideUp 0.4s ease-out;
|
|
4524
|
+
}
|
|
4525
|
+
|
|
4526
|
+
.neoface-email-modal-content h2 {
|
|
4527
|
+
font-size: 24px;
|
|
4528
|
+
font-weight: 700;
|
|
4529
|
+
color: white;
|
|
4530
|
+
margin: 0 0 12px 0;
|
|
4531
|
+
text-align: center;
|
|
4532
|
+
}
|
|
4533
|
+
|
|
4534
|
+
.neoface-email-modal-content p {
|
|
4535
|
+
font-size: 15px;
|
|
4536
|
+
color: rgba(255, 255, 255, 0.7);
|
|
4537
|
+
margin: 0 0 24px 0;
|
|
4538
|
+
text-align: center;
|
|
4539
|
+
line-height: 1.5;
|
|
4540
|
+
}
|
|
4541
|
+
|
|
4542
|
+
.neoface-email-modal-content form {
|
|
4543
|
+
display: flex;
|
|
4544
|
+
flex-direction: column;
|
|
4545
|
+
gap: 16px;
|
|
4546
|
+
margin-bottom: 16px;
|
|
4547
|
+
}
|
|
4548
|
+
|
|
4549
|
+
.neoface-email-modal-content input {
|
|
4550
|
+
width: 100%;
|
|
4551
|
+
padding: 14px 16px;
|
|
4552
|
+
border: 2px solid rgba(255, 255, 255, 0.2);
|
|
4553
|
+
border-radius: 12px;
|
|
4554
|
+
font-size: 15px;
|
|
4555
|
+
background: rgba(255, 255, 255, 0.1);
|
|
4556
|
+
color: white;
|
|
4557
|
+
font-family: inherit;
|
|
4558
|
+
transition: all 0.2s;
|
|
4559
|
+
box-sizing: border-box;
|
|
4560
|
+
}
|
|
4561
|
+
|
|
4562
|
+
.neoface-email-modal-content input::placeholder {
|
|
4563
|
+
color: rgba(255, 255, 255, 0.5);
|
|
4564
|
+
}
|
|
4565
|
+
|
|
4566
|
+
.neoface-email-modal-content input:focus {
|
|
4567
|
+
outline: none;
|
|
4568
|
+
border-color: #7c3aed;
|
|
4569
|
+
box-shadow: 0 0 0 3px rgba(124, 58, 237, 0.2);
|
|
4570
|
+
background: rgba(255, 255, 255, 0.15);
|
|
4571
|
+
}
|
|
4572
|
+
|
|
4573
|
+
.neoface-email-submit-btn {
|
|
4574
|
+
width: 100%;
|
|
4575
|
+
padding: 14px 24px;
|
|
4576
|
+
border-radius: 12px;
|
|
4577
|
+
border: none;
|
|
4578
|
+
background: linear-gradient(135deg, #7c3aed 0%, #6d28d9 100%);
|
|
4579
|
+
color: white;
|
|
4580
|
+
font-size: 16px;
|
|
4581
|
+
font-weight: 600;
|
|
4582
|
+
cursor: pointer;
|
|
4583
|
+
transition: all 0.2s;
|
|
4584
|
+
box-shadow: 0 4px 16px rgba(124, 58, 237, 0.3);
|
|
4585
|
+
font-family: inherit;
|
|
4586
|
+
}
|
|
4587
|
+
|
|
4588
|
+
.neoface-email-submit-btn:hover {
|
|
4589
|
+
transform: translateY(-2px);
|
|
4590
|
+
box-shadow: 0 8px 24px rgba(124, 58, 237, 0.4);
|
|
4591
|
+
}
|
|
4592
|
+
|
|
4593
|
+
.neoface-email-submit-btn:active {
|
|
4594
|
+
transform: translateY(0);
|
|
4595
|
+
}
|
|
4596
|
+
|
|
4597
|
+
.neoface-email-submit-btn:disabled {
|
|
4598
|
+
opacity: 0.6;
|
|
4599
|
+
cursor: not-allowed;
|
|
4600
|
+
transform: none;
|
|
4601
|
+
}
|
|
4602
|
+
|
|
4603
|
+
.neoface-email-cancel-btn {
|
|
4604
|
+
width: 100%;
|
|
4605
|
+
padding: 12px 24px;
|
|
4606
|
+
border-radius: 12px;
|
|
4607
|
+
border: 1px solid rgba(255, 255, 255, 0.2);
|
|
4608
|
+
background: transparent;
|
|
4609
|
+
color: rgba(255, 255, 255, 0.7);
|
|
4610
|
+
font-size: 15px;
|
|
4611
|
+
font-weight: 600;
|
|
4612
|
+
cursor: pointer;
|
|
4613
|
+
transition: all 0.2s;
|
|
4614
|
+
font-family: inherit;
|
|
4615
|
+
}
|
|
4616
|
+
|
|
4617
|
+
.neoface-email-cancel-btn:hover {
|
|
4618
|
+
background: rgba(255, 255, 255, 0.1);
|
|
4619
|
+
color: white;
|
|
4620
|
+
border-color: rgba(255, 255, 255, 0.3);
|
|
4621
|
+
}
|
|
4622
|
+
|
|
4623
|
+
.neoface-email-branding {
|
|
4624
|
+
display: flex;
|
|
4625
|
+
align-items: center;
|
|
4626
|
+
justify-content: center;
|
|
4627
|
+
gap: 8px;
|
|
4628
|
+
margin-top: 32px;
|
|
4629
|
+
padding-top: 24px;
|
|
4630
|
+
border-top: 1px solid rgba(255, 255, 255, 0.1);
|
|
4631
|
+
font-size: 13px;
|
|
4632
|
+
color: rgba(255, 255, 255, 0.5);
|
|
4633
|
+
}
|
|
4634
|
+
|
|
4635
|
+
.neoface-email-brand-logo {
|
|
4636
|
+
height: 20px;
|
|
4637
|
+
width: auto;
|
|
4638
|
+
}
|
|
4639
|
+
|
|
4640
|
+
.neoface-email-brand-name {
|
|
4641
|
+
font-weight: 600;
|
|
4642
|
+
font-family: 'SF Pro Display', -apple-system, system-ui, sans-serif;
|
|
4643
|
+
color: #ffffff;
|
|
4644
|
+
letter-spacing: 0.3px;
|
|
4645
|
+
}
|
|
4646
|
+
|
|
4647
|
+
@media (max-width: 640px) {
|
|
4648
|
+
.neoface-email-modal-content {
|
|
4649
|
+
padding: 24px;
|
|
4650
|
+
border-radius: 16px;
|
|
4651
|
+
}
|
|
4652
|
+
|
|
4653
|
+
.neoface-email-modal-content h2 {
|
|
4654
|
+
font-size: 20px;
|
|
4655
|
+
}
|
|
4656
|
+
}
|
|
4657
|
+
`;
|
|
4658
|
+
document.head.appendChild(style);
|
|
4659
|
+
}
|
|
4660
|
+
async handleSubmit(event) {
|
|
4661
|
+
event.preventDefault();
|
|
4662
|
+
const emailInput = this.modalElement.querySelector("#email");
|
|
4663
|
+
const passwordInput = this.modalElement.querySelector("#password");
|
|
4664
|
+
const email = emailInput.value.trim();
|
|
4665
|
+
const password = passwordInput.value.trim();
|
|
4666
|
+
if (!email || !password) {
|
|
4667
|
+
this.options.onError(
|
|
4668
|
+
new NeoFaceError("Email e senha são obrigatórios", ErrorType.VALIDATION_ERROR)
|
|
4669
|
+
);
|
|
4670
|
+
return;
|
|
4671
|
+
}
|
|
4672
|
+
try {
|
|
4673
|
+
const result = await loginWithEmail(email, password, this.options.applicationToken);
|
|
4674
|
+
this.options.onSuccess(result);
|
|
4675
|
+
this.close();
|
|
4676
|
+
} catch (error) {
|
|
4677
|
+
this.options.onError(error);
|
|
4678
|
+
}
|
|
4679
|
+
}
|
|
4680
|
+
}
|
|
4681
|
+
const MAX_ATTEMPTS = 3;
|
|
4682
|
+
const RETRY_DELAY = 500;
|
|
4683
|
+
async function detectFaceContinuously(video, minDetectionTime = 3e3, detectionInterval = 200) {
|
|
4684
|
+
return new Promise((resolve) => {
|
|
4685
|
+
let faceDetectedCount = 0;
|
|
4686
|
+
let totalChecks = 0;
|
|
4687
|
+
const requiredChecks = Math.ceil(minDetectionTime / detectionInterval);
|
|
4688
|
+
const minSuccessRate = 0.6;
|
|
4689
|
+
let timeoutId = null;
|
|
4690
|
+
let isResolved = false;
|
|
4691
|
+
const finish = (success) => {
|
|
4692
|
+
if (isResolved)
|
|
4693
|
+
return;
|
|
4694
|
+
isResolved = true;
|
|
4695
|
+
if (timeoutId) {
|
|
4696
|
+
clearTimeout(timeoutId);
|
|
4697
|
+
}
|
|
4698
|
+
resolve(success);
|
|
4699
|
+
};
|
|
4700
|
+
const checkFace = async () => {
|
|
4701
|
+
if (isResolved)
|
|
4702
|
+
return;
|
|
4703
|
+
try {
|
|
4704
|
+
const detection = await faceapi.detectSingleFace(
|
|
4705
|
+
video,
|
|
4706
|
+
new faceapi.TinyFaceDetectorOptions({ inputSize: 160, scoreThreshold: 0.4 })
|
|
4707
|
+
);
|
|
4708
|
+
totalChecks++;
|
|
4709
|
+
if (detection) {
|
|
4710
|
+
faceDetectedCount++;
|
|
4711
|
+
}
|
|
4712
|
+
if (totalChecks >= requiredChecks) {
|
|
4713
|
+
const successRate = faceDetectedCount / totalChecks;
|
|
4714
|
+
finish(successRate >= minSuccessRate);
|
|
4715
|
+
return;
|
|
4716
|
+
}
|
|
4717
|
+
timeoutId = window.setTimeout(checkFace, detectionInterval);
|
|
4718
|
+
} catch (error) {
|
|
4719
|
+
totalChecks++;
|
|
4720
|
+
if (totalChecks >= requiredChecks) {
|
|
4721
|
+
const successRate = faceDetectedCount / totalChecks;
|
|
4722
|
+
finish(successRate >= minSuccessRate);
|
|
4723
|
+
} else {
|
|
4724
|
+
timeoutId = window.setTimeout(checkFace, detectionInterval);
|
|
4725
|
+
}
|
|
4726
|
+
}
|
|
4727
|
+
};
|
|
4728
|
+
const safetyTimeout = window.setTimeout(() => {
|
|
4729
|
+
if (!isResolved) {
|
|
4730
|
+
const successRate = totalChecks > 0 ? faceDetectedCount / totalChecks : 0;
|
|
4731
|
+
finish(successRate >= minSuccessRate);
|
|
4732
|
+
}
|
|
4733
|
+
}, minDetectionTime + 1e3);
|
|
4734
|
+
timeoutId = window.setTimeout(() => {
|
|
4735
|
+
checkFace();
|
|
4736
|
+
clearTimeout(safetyTimeout);
|
|
4737
|
+
}, detectionInterval);
|
|
4738
|
+
});
|
|
4739
|
+
}
|
|
4740
|
+
async function attemptLogin(applicationToken, overlay) {
|
|
4741
|
+
overlay.updateStatus("verifying");
|
|
4742
|
+
const video = document.createElement("video");
|
|
4743
|
+
video.style.position = "fixed";
|
|
4744
|
+
video.style.top = "-9999px";
|
|
4745
|
+
video.style.left = "-9999px";
|
|
4746
|
+
video.style.width = "1px";
|
|
4747
|
+
video.style.height = "1px";
|
|
4748
|
+
video.style.opacity = "0";
|
|
4749
|
+
video.setAttribute("autoplay", "");
|
|
4750
|
+
video.setAttribute("muted", "");
|
|
4751
|
+
video.setAttribute("playsinline", "");
|
|
4752
|
+
document.body.appendChild(video);
|
|
4753
|
+
let stream = null;
|
|
4754
|
+
try {
|
|
4755
|
+
stream = await navigator.mediaDevices.getUserMedia({
|
|
4756
|
+
video: {
|
|
4757
|
+
width: { ideal: 640 },
|
|
4758
|
+
height: { ideal: 480 },
|
|
4759
|
+
facingMode: "user"
|
|
4760
|
+
},
|
|
4761
|
+
audio: false
|
|
4762
|
+
});
|
|
4763
|
+
video.srcObject = stream;
|
|
4764
|
+
await video.play();
|
|
4765
|
+
await new Promise((resolve) => {
|
|
4766
|
+
if (video.readyState >= 2) {
|
|
4767
|
+
resolve(void 0);
|
|
4768
|
+
} else {
|
|
4769
|
+
video.onloadedmetadata = () => resolve(void 0);
|
|
4770
|
+
}
|
|
4771
|
+
});
|
|
4772
|
+
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
4773
|
+
const faceDetected = await detectFaceContinuously(video, 3e3);
|
|
4774
|
+
if (!faceDetected) {
|
|
4775
|
+
if (stream) {
|
|
4776
|
+
stream.getTracks().forEach((track) => track.stop());
|
|
4777
|
+
}
|
|
4778
|
+
if (video.parentElement) {
|
|
4779
|
+
video.parentElement.removeChild(video);
|
|
4780
|
+
}
|
|
4781
|
+
throw new NeoFaceError("Rosto não detectado. Posicione seu rosto na frente da câmera.", ErrorType.VALIDATION_ERROR);
|
|
4782
|
+
}
|
|
4783
|
+
const canvas = document.createElement("canvas");
|
|
4784
|
+
canvas.width = video.videoWidth;
|
|
4785
|
+
canvas.height = video.videoHeight;
|
|
4786
|
+
const ctx = canvas.getContext("2d");
|
|
4787
|
+
if (!ctx) {
|
|
4788
|
+
if (stream) {
|
|
4789
|
+
stream.getTracks().forEach((track) => track.stop());
|
|
4790
|
+
}
|
|
4791
|
+
if (video.parentElement) {
|
|
4792
|
+
video.parentElement.removeChild(video);
|
|
4793
|
+
}
|
|
4794
|
+
throw new NeoFaceError("Erro ao capturar imagem", ErrorType.CAPTURE_ERROR);
|
|
4795
|
+
}
|
|
4796
|
+
ctx.drawImage(video, 0, 0, canvas.width, canvas.height);
|
|
4797
|
+
if (stream) {
|
|
4798
|
+
stream.getTracks().forEach((track) => track.stop());
|
|
4799
|
+
}
|
|
4800
|
+
if (video.parentElement) {
|
|
4801
|
+
video.parentElement.removeChild(video);
|
|
4802
|
+
}
|
|
4803
|
+
const imageBlob = await new Promise((resolve, reject) => {
|
|
4804
|
+
canvas.toBlob(
|
|
4805
|
+
(blob) => {
|
|
4806
|
+
canvas.remove();
|
|
4807
|
+
if (blob) {
|
|
4808
|
+
resolve(blob);
|
|
4809
|
+
} else {
|
|
4810
|
+
reject(new Error("Failed to capture image"));
|
|
4811
|
+
}
|
|
4812
|
+
},
|
|
4813
|
+
"image/jpeg",
|
|
4814
|
+
0.85
|
|
4815
|
+
);
|
|
4816
|
+
});
|
|
4817
|
+
const result = await loginWithBiometric$1(imageBlob, applicationToken);
|
|
4818
|
+
if (!result.success) {
|
|
4819
|
+
throw new NeoFaceError("Login falhou", ErrorType.LOGIN_FAILED);
|
|
4820
|
+
}
|
|
4821
|
+
return result;
|
|
4822
|
+
} catch (error) {
|
|
4823
|
+
if (stream) {
|
|
4824
|
+
stream.getTracks().forEach((track) => track.stop());
|
|
4825
|
+
}
|
|
4826
|
+
if (video.parentElement) {
|
|
4827
|
+
video.parentElement.removeChild(video);
|
|
4828
|
+
}
|
|
4829
|
+
if (error instanceof NeoFaceError) {
|
|
4830
|
+
throw error;
|
|
4831
|
+
}
|
|
4832
|
+
throw new NeoFaceError(
|
|
4833
|
+
error instanceof Error ? error.message : "Erro desconhecido",
|
|
4834
|
+
ErrorType.UNKNOWN
|
|
4835
|
+
);
|
|
4836
|
+
}
|
|
4837
|
+
}
|
|
4838
|
+
async function executeBiometricLoginFlow(options) {
|
|
4839
|
+
const { applicationToken, onSuccess, onError, onFallbackRequest, onCancel } = options;
|
|
4840
|
+
const overlay = new BiometricStatusOverlay();
|
|
4841
|
+
const fallbackPrompt = new FallbackPrompt();
|
|
4842
|
+
let attempts = 0;
|
|
4843
|
+
let lastError;
|
|
4844
|
+
try {
|
|
4845
|
+
const MODEL_URL = "https://cdn.jsdelivr.net/npm/@vladmandic/face-api/model";
|
|
4846
|
+
await Promise.all([
|
|
4847
|
+
faceapi.nets.tinyFaceDetector.loadFromUri(MODEL_URL),
|
|
4848
|
+
faceapi.nets.faceLandmark68Net.loadFromUri(MODEL_URL)
|
|
4849
|
+
]);
|
|
4850
|
+
} catch (error) {
|
|
4851
|
+
console.warn("Face-api.js models not loaded, continuing without face detection");
|
|
4852
|
+
}
|
|
4853
|
+
overlay.show("preparing");
|
|
4854
|
+
const tryLogin = async () => {
|
|
4855
|
+
try {
|
|
4856
|
+
attempts++;
|
|
4857
|
+
const result = await attemptLogin(applicationToken, overlay);
|
|
4858
|
+
overlay.updateStatus("success");
|
|
4859
|
+
setTimeout(() => {
|
|
4860
|
+
overlay.close();
|
|
4861
|
+
onSuccess(result);
|
|
4862
|
+
}, 800);
|
|
4863
|
+
} catch (error) {
|
|
4864
|
+
lastError = error instanceof NeoFaceError ? error : new NeoFaceError(
|
|
4865
|
+
error instanceof Error ? error.message : "Erro desconhecido",
|
|
4866
|
+
ErrorType.UNKNOWN
|
|
4867
|
+
);
|
|
4868
|
+
overlay.updateStatus("error");
|
|
4869
|
+
if (attempts < MAX_ATTEMPTS) {
|
|
4870
|
+
setTimeout(() => {
|
|
4871
|
+
tryLogin();
|
|
4872
|
+
}, RETRY_DELAY);
|
|
4873
|
+
} else {
|
|
4874
|
+
setTimeout(() => {
|
|
4875
|
+
overlay.close();
|
|
4876
|
+
fallbackPrompt.show(
|
|
4877
|
+
lastError,
|
|
4878
|
+
() => {
|
|
4879
|
+
attempts = 0;
|
|
4880
|
+
overlay.show("preparing");
|
|
4881
|
+
setTimeout(() => tryLogin(), 500);
|
|
4882
|
+
},
|
|
4883
|
+
() => {
|
|
4884
|
+
if (onFallbackRequest) {
|
|
4885
|
+
onFallbackRequest();
|
|
4886
|
+
} else {
|
|
4887
|
+
onError(
|
|
4888
|
+
new NeoFaceError(
|
|
4889
|
+
"Por favor, use email e senha para fazer login",
|
|
4890
|
+
ErrorType.LOGIN_FAILED
|
|
4891
|
+
)
|
|
4892
|
+
);
|
|
4893
|
+
}
|
|
4894
|
+
},
|
|
4895
|
+
onCancel
|
|
4896
|
+
);
|
|
4897
|
+
}, 1e3);
|
|
4898
|
+
}
|
|
4899
|
+
}
|
|
4900
|
+
};
|
|
4901
|
+
setTimeout(() => {
|
|
4902
|
+
tryLogin();
|
|
4903
|
+
}, 300);
|
|
4904
|
+
}
|
|
4905
|
+
async function startFaceLogin(options) {
|
|
4906
|
+
try {
|
|
4907
|
+
const isValidToken = await validateToken(options.applicationToken);
|
|
4908
|
+
if (!isValidToken) {
|
|
4909
|
+
options.onError(new NeoFaceError("Token de aplicação inválido", ErrorType.INVALID_TOKEN));
|
|
4910
|
+
return;
|
|
4911
|
+
}
|
|
4912
|
+
await executeBiometricLoginFlow({
|
|
4913
|
+
applicationToken: options.applicationToken,
|
|
4914
|
+
onSuccess: options.onSuccess,
|
|
4915
|
+
onError: options.onError,
|
|
4916
|
+
onFallbackRequest: options.onFallbackRequest || (() => {
|
|
4917
|
+
const fallbackOptions = {
|
|
4918
|
+
applicationToken: options.applicationToken,
|
|
4919
|
+
onSuccess: options.onSuccess,
|
|
4920
|
+
onError: options.onError,
|
|
4921
|
+
title: "Login Alternativo",
|
|
4922
|
+
subtitle: "Biometria não reconhecida. Por favor, informe email e senha."
|
|
4923
|
+
};
|
|
4924
|
+
const modal = new EmailPasswordModal(fallbackOptions);
|
|
4925
|
+
modal.open();
|
|
4926
|
+
}),
|
|
4927
|
+
onCancel: options.onCancel
|
|
4928
|
+
});
|
|
3740
4929
|
} catch (error) {
|
|
3741
4930
|
options.onError(
|
|
3742
4931
|
new NeoFaceError(
|
|
@@ -4040,8 +5229,8 @@ const biometricDetection = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.
|
|
|
4040
5229
|
initializeBiometricDetection,
|
|
4041
5230
|
isAdvancedDetectionAvailable
|
|
4042
5231
|
}, Symbol.toStringTag, { value: "Module" }));
|
|
4043
|
-
const VERSION = "1.
|
|
4044
|
-
const RELEASE_DATE = "2025-11-
|
|
5232
|
+
const VERSION = "1.4.0";
|
|
5233
|
+
const RELEASE_DATE = "2025-11-23";
|
|
4045
5234
|
class OnboardingCaptureModal {
|
|
4046
5235
|
constructor(options) {
|
|
4047
5236
|
__publicField(this, "overlay", null);
|
|
@@ -4327,19 +5516,25 @@ function startBiometricRegistration(personData, applicationToken, callbacks, opt
|
|
|
4327
5516
|
});
|
|
4328
5517
|
}
|
|
4329
5518
|
export {
|
|
4330
|
-
BiometricCaptureModal,
|
|
5519
|
+
BiometricCaptureModal$1 as BiometricCaptureModal,
|
|
4331
5520
|
BiometricRegistrationModal,
|
|
5521
|
+
BiometricStatusOverlay,
|
|
5522
|
+
EmailPasswordModal,
|
|
4332
5523
|
ErrorType,
|
|
4333
5524
|
FaceCaptureModal,
|
|
5525
|
+
FallbackPrompt,
|
|
4334
5526
|
NeoFaceError,
|
|
4335
5527
|
RELEASE_DATE,
|
|
4336
5528
|
VERSION,
|
|
4337
5529
|
biometricLogin,
|
|
4338
5530
|
biometricLoginWithFallback,
|
|
5531
|
+
completeOnboarding,
|
|
5532
|
+
completeOnboardingWithData,
|
|
4339
5533
|
detectBiometricType,
|
|
5534
|
+
identifyPerson,
|
|
4340
5535
|
initializeBiometricDetection,
|
|
4341
5536
|
isAdvancedDetectionAvailable,
|
|
4342
|
-
loginWithBiometric,
|
|
5537
|
+
loginWithBiometric$1 as loginWithBiometric,
|
|
4343
5538
|
recognize,
|
|
4344
5539
|
recognizeBiometric,
|
|
4345
5540
|
recognizeByPurpose,
|
|
@@ -4352,6 +5547,7 @@ export {
|
|
|
4352
5547
|
startFaceLogin,
|
|
4353
5548
|
startHandLogin,
|
|
4354
5549
|
startOnboarding,
|
|
5550
|
+
validateOnboardingToken,
|
|
4355
5551
|
validateToken
|
|
4356
5552
|
};
|
|
4357
5553
|
//# sourceMappingURL=neoface-id-sdk.es.js.map
|