@neofaceid/web-sdk 1.43.0 → 1.44.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 +71 -3
- package/dist/neoface-id-sdk.es.js +419 -20
- package/dist/neoface-id-sdk.umd.js +1 -1
- package/package.json +2 -1
package/dist/index.d.ts
CHANGED
|
@@ -645,9 +645,41 @@ export declare interface DetectionResult {
|
|
|
645
645
|
details?: any;
|
|
646
646
|
}
|
|
647
647
|
|
|
648
|
+
/** Desfecho da inscrição de aparelho. */
|
|
649
|
+
export declare type DeviceEnrollmentOutcome = {
|
|
650
|
+
status: 'enrolled';
|
|
651
|
+
deviceLabel: string | null;
|
|
652
|
+
enrolledAt?: string;
|
|
653
|
+
} | {
|
|
654
|
+
status: 'expired';
|
|
655
|
+
} | {
|
|
656
|
+
status: 'cancelled';
|
|
657
|
+
};
|
|
658
|
+
|
|
648
659
|
/** Token de inscrição de aparelho, devolvido por {@link requestDeviceEnrollmentToken}. */
|
|
649
660
|
export declare interface DeviceEnrollmentToken {
|
|
650
|
-
/**
|
|
661
|
+
/**
|
|
662
|
+
* Identificador curto, no formato `XXXX-XXXX`, de um alfabeto sem os pares
|
|
663
|
+
* que se confundem (`0/O`, `1/I/L`, `U/V`).
|
|
664
|
+
*
|
|
665
|
+
* **É isto que o QR deve carregar**, não o {@link token}. O JWT inteiro vira
|
|
666
|
+
* uma grade densa que exige tela grande e câmera boa, e deixa o segredo
|
|
667
|
+
* óptico — exposto em qualquer foto da tela. O handle fotografado vale muito
|
|
668
|
+
* menos: não carrega `person_id`, `consumer_id` nem assinatura, e morre na
|
|
669
|
+
* primeira troca. Como é legível, também serve para ditar por telefone ou
|
|
670
|
+
* digitar à mão quando a câmera falha.
|
|
671
|
+
*/
|
|
672
|
+
handle: string;
|
|
673
|
+
/**
|
|
674
|
+
* Identificador da solicitação. É a chave do canal SSE que avisa quando o
|
|
675
|
+
* aparelho for inscrito: `/api/v1/devices/enrollment/{jti}/events/`.
|
|
676
|
+
*/
|
|
677
|
+
jti: string;
|
|
678
|
+
/**
|
|
679
|
+
* JWT RS256 de uso único. Continua vindo por compatibilidade, mas **não deve
|
|
680
|
+
* ir para o QR** — quem resolve o handle é o Push, contra o core, por canal
|
|
681
|
+
* interno.
|
|
682
|
+
*/
|
|
651
683
|
token: string;
|
|
652
684
|
/** Validade em segundos (o core emite 300). */
|
|
653
685
|
expiresIn: number;
|
|
@@ -1603,7 +1635,7 @@ export declare const registerPersonWithoutFace: (personData: {
|
|
|
1603
1635
|
/**
|
|
1604
1636
|
* Data de lançamento da versão atual
|
|
1605
1637
|
*/
|
|
1606
|
-
export declare const RELEASE_DATE = "2026-09-
|
|
1638
|
+
export declare const RELEASE_DATE = "2026-09-16";
|
|
1607
1639
|
|
|
1608
1640
|
declare interface RequestChallengeWithSessionParams {
|
|
1609
1641
|
applicationToken: string;
|
|
@@ -1908,6 +1940,42 @@ export declare function startBiometricRegistration(personData: {
|
|
|
1908
1940
|
onCannotGesture?: () => void;
|
|
1909
1941
|
}): void;
|
|
1910
1942
|
|
|
1943
|
+
/**
|
|
1944
|
+
* Abre o fluxo de inscrição de aparelho: mostra o código, aguarda o celular
|
|
1945
|
+
* lê-lo e confirma o vínculo.
|
|
1946
|
+
*
|
|
1947
|
+
* Exige uma pessoa autenticada — o core pede o Bearer do DONOR junto do
|
|
1948
|
+
* `X-App-Token`, e por isso a inscrição só existe no autosserviço.
|
|
1949
|
+
*
|
|
1950
|
+
* O QR carrega o **handle curto**, nunca o JWT: um código denso exige tela
|
|
1951
|
+
* grande e câmera boa, e o token inteiro ficaria exposto em qualquer foto da
|
|
1952
|
+
* tela. Quem resolve o handle é a plataforma Push, contra o core, por canal
|
|
1953
|
+
* interno — o navegador não participa dessa troca.
|
|
1954
|
+
*
|
|
1955
|
+
* @example
|
|
1956
|
+
* ```ts
|
|
1957
|
+
* const r = await startDeviceEnrollment({
|
|
1958
|
+
* userAccessToken: sessao.accessToken,
|
|
1959
|
+
* applicationToken: TOKEN,
|
|
1960
|
+
* });
|
|
1961
|
+
* if (r.status === 'enrolled') {
|
|
1962
|
+
* // r.deviceLabel pode ser null enquanto o Push não envia o rótulo
|
|
1963
|
+
* }
|
|
1964
|
+
* ```
|
|
1965
|
+
*/
|
|
1966
|
+
export declare function startDeviceEnrollment(options: StartDeviceEnrollmentOptions): Promise<DeviceEnrollmentOutcome>;
|
|
1967
|
+
|
|
1968
|
+
export declare interface StartDeviceEnrollmentOptions {
|
|
1969
|
+
/**
|
|
1970
|
+
* Access token do DONOR autenticado. A inscrição só existe no autosserviço,
|
|
1971
|
+
* com a pessoa logada — o core exige este Bearer **junto** do `X-App-Token`.
|
|
1972
|
+
*/
|
|
1973
|
+
userAccessToken: string;
|
|
1974
|
+
applicationToken: string;
|
|
1975
|
+
/** Nome do integrador (fallback: `init({ appName })`). */
|
|
1976
|
+
appName?: string;
|
|
1977
|
+
}
|
|
1978
|
+
|
|
1911
1979
|
/**
|
|
1912
1980
|
* Função auxiliar para iniciar a captura de documento
|
|
1913
1981
|
*/
|
|
@@ -2040,7 +2108,7 @@ export declare const validateToken: (applicationToken: string) => Promise<boolea
|
|
|
2040
2108
|
* MINOR: Incrementado quando adicionamos funcionalidades mantendo compatibilidade
|
|
2041
2109
|
* PATCH: Incrementado quando corrigimos bugs mantendo compatibilidade
|
|
2042
2110
|
*/
|
|
2043
|
-
export declare const VERSION = "1.
|
|
2111
|
+
export declare const VERSION = "1.44.0";
|
|
2044
2112
|
|
|
2045
2113
|
/**
|
|
2046
2114
|
* Executa `fn(sessionId)`. Se o servidor devolver 410 (sessão consumida/expirada),
|
|
@@ -5678,7 +5678,6 @@ const loginWithEmail = async (email2, password, applicationToken) => {
|
|
|
5678
5678
|
}
|
|
5679
5679
|
};
|
|
5680
5680
|
const requestDeviceEnrollmentToken = async (userAccessToken, applicationToken) => {
|
|
5681
|
-
var _a2, _b, _c;
|
|
5682
5681
|
if (!userAccessToken) {
|
|
5683
5682
|
throw new NeoFaceError(
|
|
5684
5683
|
"Inscrição de aparelho exige um usuário autenticado (access token ausente).",
|
|
@@ -5697,23 +5696,36 @@ const requestDeviceEnrollmentToken = async (userAccessToken, applicationToken) =
|
|
|
5697
5696
|
signal: controller.signal
|
|
5698
5697
|
});
|
|
5699
5698
|
if (!response.ok) {
|
|
5700
|
-
if (response.status === 401
|
|
5699
|
+
if (response.status === 401) {
|
|
5700
|
+
throw new NeoFaceError(
|
|
5701
|
+
"Sua sessão expirou. Entre novamente para inscrever o aparelho.",
|
|
5702
|
+
ErrorType.INVALID_TOKEN
|
|
5703
|
+
);
|
|
5704
|
+
}
|
|
5705
|
+
if (response.status === 403) {
|
|
5701
5706
|
throw new NeoFaceError(
|
|
5702
|
-
"
|
|
5707
|
+
"Esta conta não pode inscrever aparelhos. Procure o suporte.",
|
|
5703
5708
|
ErrorType.INVALID_TOKEN
|
|
5704
5709
|
);
|
|
5705
5710
|
}
|
|
5706
5711
|
throw new NeoFaceError(`API Error ${response.status}`, ErrorType.API_ERROR);
|
|
5707
5712
|
}
|
|
5708
5713
|
const data = await response.json();
|
|
5709
|
-
const
|
|
5710
|
-
|
|
5711
|
-
|
|
5714
|
+
const body = data.data ?? data;
|
|
5715
|
+
const token = body.token;
|
|
5716
|
+
const handle = body.handle;
|
|
5717
|
+
if (!token || !handle) {
|
|
5718
|
+
throw new NeoFaceError(
|
|
5719
|
+
"Resposta de inscrição incompleta (token ou handle ausente)",
|
|
5720
|
+
ErrorType.API_ERROR
|
|
5721
|
+
);
|
|
5712
5722
|
}
|
|
5713
5723
|
return {
|
|
5724
|
+
handle,
|
|
5725
|
+
jti: body.jti ?? "",
|
|
5714
5726
|
token,
|
|
5715
|
-
expiresIn:
|
|
5716
|
-
purpose:
|
|
5727
|
+
expiresIn: body.expires_in ?? 300,
|
|
5728
|
+
purpose: body.purpose ?? "device-enrollment"
|
|
5717
5729
|
};
|
|
5718
5730
|
} catch (error) {
|
|
5719
5731
|
if (error instanceof NeoFaceError) throw error;
|
|
@@ -8886,8 +8898,8 @@ function BiometricRegistrationModal({
|
|
|
8886
8898
|
/* @__PURE__ */ jsx("canvas", { ref: canvasRef })
|
|
8887
8899
|
] });
|
|
8888
8900
|
}
|
|
8889
|
-
const VERSION = "1.
|
|
8890
|
-
const RELEASE_DATE = "2026-09-
|
|
8901
|
+
const VERSION = "1.44.0";
|
|
8902
|
+
const RELEASE_DATE = "2026-09-16";
|
|
8891
8903
|
const CONSENT_TRAIL_STORAGE_KEY = "neoface_consent_trail_v1";
|
|
8892
8904
|
const CONSENT_TRAIL_MAX_LIMIT = 100;
|
|
8893
8905
|
async function hashString(inputStr) {
|
|
@@ -9599,8 +9611,8 @@ function StatusPill({ tone, children, icon, className }) {
|
|
|
9599
9611
|
}
|
|
9600
9612
|
);
|
|
9601
9613
|
}
|
|
9602
|
-
const STYLE_ID$
|
|
9603
|
-
const CSS$
|
|
9614
|
+
const STYLE_ID$3 = "neofaceid-awaiting-push-styles";
|
|
9615
|
+
const CSS$2 = `
|
|
9604
9616
|
.neofaceid-root .nfid-push {
|
|
9605
9617
|
display: flex; flex-direction: column; align-items: center; text-align: center; gap: 10px;
|
|
9606
9618
|
padding: 8px 8px 4px;
|
|
@@ -9651,7 +9663,7 @@ const CSS$1 = `
|
|
|
9651
9663
|
.neofaceid-root .nfid-push-pulse { animation: none; }
|
|
9652
9664
|
}
|
|
9653
9665
|
`;
|
|
9654
|
-
function formatRemaining(seconds) {
|
|
9666
|
+
function formatRemaining$1(seconds) {
|
|
9655
9667
|
const safe = Math.max(0, seconds);
|
|
9656
9668
|
const mm = Math.floor(safe / 60);
|
|
9657
9669
|
const ss = safe % 60;
|
|
@@ -9665,7 +9677,7 @@ function AwaitingPushView({
|
|
|
9665
9677
|
}) {
|
|
9666
9678
|
useEffect(() => {
|
|
9667
9679
|
injectThemeStyles();
|
|
9668
|
-
injectScopedStyles(STYLE_ID$
|
|
9680
|
+
injectScopedStyles(STYLE_ID$3, CSS$2);
|
|
9669
9681
|
}, []);
|
|
9670
9682
|
return /* @__PURE__ */ jsxs("div", { className: "nfid-push", role: "status", "aria-live": "polite", children: [
|
|
9671
9683
|
/* @__PURE__ */ jsx(StatusPill, { tone: "attention", children: "Aguardando confirmação" }),
|
|
@@ -9705,7 +9717,7 @@ function AwaitingPushView({
|
|
|
9705
9717
|
] }),
|
|
9706
9718
|
/* @__PURE__ */ jsxs("p", { className: "nfid-push-countdown", children: [
|
|
9707
9719
|
"Expira em ",
|
|
9708
|
-
formatRemaining(remainingSeconds)
|
|
9720
|
+
formatRemaining$1(remainingSeconds)
|
|
9709
9721
|
] }),
|
|
9710
9722
|
/* @__PURE__ */ jsx("div", { className: "nfid-push-actions", children: /* @__PURE__ */ jsx(Button, { variant: "ghost", onClick: onCancel, children: "Cancelar" }) }),
|
|
9711
9723
|
/* @__PURE__ */ jsx("div", { className: "nfid-push-footer", children: /* @__PURE__ */ jsx(BrandFooter, {}) })
|
|
@@ -9714,7 +9726,7 @@ function AwaitingPushView({
|
|
|
9714
9726
|
function PushExpiredView({ onFallback, onCancel }) {
|
|
9715
9727
|
useEffect(() => {
|
|
9716
9728
|
injectThemeStyles();
|
|
9717
|
-
injectScopedStyles(STYLE_ID$
|
|
9729
|
+
injectScopedStyles(STYLE_ID$3, CSS$2);
|
|
9718
9730
|
}, []);
|
|
9719
9731
|
return /* @__PURE__ */ jsxs("div", { className: "nfid-push", role: "alert", "aria-live": "assertive", children: [
|
|
9720
9732
|
/* @__PURE__ */ jsxs(
|
|
@@ -9751,7 +9763,7 @@ function PushCancelledView({
|
|
|
9751
9763
|
}) {
|
|
9752
9764
|
useEffect(() => {
|
|
9753
9765
|
injectThemeStyles();
|
|
9754
|
-
injectScopedStyles(STYLE_ID$
|
|
9766
|
+
injectScopedStyles(STYLE_ID$3, CSS$2);
|
|
9755
9767
|
}, []);
|
|
9756
9768
|
return /* @__PURE__ */ jsxs("div", { className: "nfid-push", role: "alert", "aria-live": "assertive", children: [
|
|
9757
9769
|
/* @__PURE__ */ jsxs(
|
|
@@ -9789,7 +9801,7 @@ function PushCancelledView({
|
|
|
9789
9801
|
function PushDeniedView({ appName, onCancel }) {
|
|
9790
9802
|
useEffect(() => {
|
|
9791
9803
|
injectThemeStyles();
|
|
9792
|
-
injectScopedStyles(STYLE_ID$
|
|
9804
|
+
injectScopedStyles(STYLE_ID$3, CSS$2);
|
|
9793
9805
|
}, []);
|
|
9794
9806
|
return /* @__PURE__ */ jsxs("div", { className: "nfid-push", role: "alert", "aria-live": "assertive", children: [
|
|
9795
9807
|
/* @__PURE__ */ jsxs(
|
|
@@ -9820,7 +9832,7 @@ function PushDeniedView({ appName, onCancel }) {
|
|
|
9820
9832
|
/* @__PURE__ */ jsx("div", { className: "nfid-push-footer", children: /* @__PURE__ */ jsx(BrandFooter, {}) })
|
|
9821
9833
|
] });
|
|
9822
9834
|
}
|
|
9823
|
-
const CONTAINER_ID = "neofaceid-push-approval-container";
|
|
9835
|
+
const CONTAINER_ID$1 = "neofaceid-push-approval-container";
|
|
9824
9836
|
function classifyAuthorizationFrame(frame) {
|
|
9825
9837
|
var _a2, _b, _c;
|
|
9826
9838
|
switch (frame.status) {
|
|
@@ -9976,7 +9988,7 @@ function PushApprovalModal({
|
|
|
9976
9988
|
function awaitPushApproval(options) {
|
|
9977
9989
|
return new Promise((resolve) => {
|
|
9978
9990
|
const container = document.createElement("div");
|
|
9979
|
-
container.id = CONTAINER_ID;
|
|
9991
|
+
container.id = CONTAINER_ID$1;
|
|
9980
9992
|
document.body.appendChild(container);
|
|
9981
9993
|
const root = createRoot(container);
|
|
9982
9994
|
const settle = (outcome) => {
|
|
@@ -9987,6 +9999,392 @@ function awaitPushApproval(options) {
|
|
|
9987
9999
|
root.render(/* @__PURE__ */ jsx(PushApprovalModal, { ...options, onSettle: settle }));
|
|
9988
10000
|
});
|
|
9989
10001
|
}
|
|
10002
|
+
const STYLE_ID$2 = "neofaceid-device-enrollment-styles";
|
|
10003
|
+
const CSS$1 = `
|
|
10004
|
+
.neofaceid-root .nfid-enroll {
|
|
10005
|
+
display: flex; flex-direction: column; align-items: center; text-align: center; gap: 10px;
|
|
10006
|
+
padding: 8px 8px 4px;
|
|
10007
|
+
}
|
|
10008
|
+
.neofaceid-root .nfid-enroll h2 {
|
|
10009
|
+
margin: 6px 0 0; font-size: 20px; font-weight: 700; color: var(--nfid-text); line-height: 1.25;
|
|
10010
|
+
letter-spacing: -0.015em;
|
|
10011
|
+
}
|
|
10012
|
+
.neofaceid-root .nfid-enroll p {
|
|
10013
|
+
margin: 0; font-size: 14px; color: var(--nfid-text-muted); line-height: 1.5;
|
|
10014
|
+
}
|
|
10015
|
+
/* O QR é sempre escuro sobre branco, independente do tema: leitor de código
|
|
10016
|
+
falha com contraste invertido, e a moldura clara faz a zona de silêncio. */
|
|
10017
|
+
.neofaceid-root .nfid-enroll-qr {
|
|
10018
|
+
background: #FFFFFF; padding: 14px; border-radius: 16px; margin-top: 12px;
|
|
10019
|
+
line-height: 0; box-shadow: 0 0 0 1px var(--nfid-line);
|
|
10020
|
+
}
|
|
10021
|
+
.neofaceid-root .nfid-enroll-qr svg { display: block; width: 184px; height: 184px; }
|
|
10022
|
+
.neofaceid-root .nfid-enroll-handle {
|
|
10023
|
+
display: flex; flex-direction: column; align-items: center; gap: 4px;
|
|
10024
|
+
margin-top: 14px; padding: 12px 24px;
|
|
10025
|
+
background: var(--nfid-surface-muted); border-radius: 12px; width: 100%;
|
|
10026
|
+
}
|
|
10027
|
+
.neofaceid-root .nfid-enroll-handle-label {
|
|
10028
|
+
font-size: 11px; font-weight: 600; letter-spacing: 0.08em; text-transform: uppercase;
|
|
10029
|
+
color: var(--nfid-text-subtle);
|
|
10030
|
+
}
|
|
10031
|
+
.neofaceid-root .nfid-enroll-handle-value {
|
|
10032
|
+
font-size: 26px; font-weight: 800; line-height: 1.1; letter-spacing: 0.12em;
|
|
10033
|
+
color: var(--nfid-text); font-variant-numeric: tabular-nums;
|
|
10034
|
+
font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
|
|
10035
|
+
}
|
|
10036
|
+
.neofaceid-root .nfid-enroll-countdown {
|
|
10037
|
+
font-size: 13px; color: var(--nfid-text-muted); font-variant-numeric: tabular-nums;
|
|
10038
|
+
margin-top: 10px;
|
|
10039
|
+
}
|
|
10040
|
+
.neofaceid-root .nfid-enroll-actions {
|
|
10041
|
+
display: flex; flex-direction: column; gap: 10px; width: 100%; margin-top: 16px;
|
|
10042
|
+
}
|
|
10043
|
+
.neofaceid-root .nfid-enroll-note {
|
|
10044
|
+
font-size: 12px; line-height: 1.45; color: var(--nfid-text-muted);
|
|
10045
|
+
background: var(--nfid-surface-muted); border-radius: 10px;
|
|
10046
|
+
padding: 10px 14px; margin-top: 12px; text-align: left; width: 100%;
|
|
10047
|
+
}
|
|
10048
|
+
.neofaceid-root .nfid-enroll-icon { width: 56px; height: 56px; }
|
|
10049
|
+
.neofaceid-root .nfid-enroll-footer {
|
|
10050
|
+
display: flex; justify-content: center; margin-top: 16px; padding-top: 12px;
|
|
10051
|
+
border-top: 1px solid var(--nfid-line); width: 100%;
|
|
10052
|
+
}
|
|
10053
|
+
`;
|
|
10054
|
+
function formatRemaining(seconds) {
|
|
10055
|
+
const safe = Math.max(0, seconds);
|
|
10056
|
+
const mm = Math.floor(safe / 60);
|
|
10057
|
+
const ss = safe % 60;
|
|
10058
|
+
return `${mm}:${String(ss).padStart(2, "0")}`;
|
|
10059
|
+
}
|
|
10060
|
+
function QrCode({ value }) {
|
|
10061
|
+
const [path, setPath] = useState(null);
|
|
10062
|
+
useEffect(() => {
|
|
10063
|
+
let cancelled = false;
|
|
10064
|
+
import("./qrcode-DYfGiWu6.js").then((mod) => {
|
|
10065
|
+
if (cancelled) return;
|
|
10066
|
+
const qrcode = mod.default ?? mod;
|
|
10067
|
+
const qr = qrcode(0, "M");
|
|
10068
|
+
qr.addData(value);
|
|
10069
|
+
qr.make();
|
|
10070
|
+
const count = qr.getModuleCount();
|
|
10071
|
+
const parts = [];
|
|
10072
|
+
for (let r = 0; r < count; r += 1) {
|
|
10073
|
+
for (let c = 0; c < count; c += 1) {
|
|
10074
|
+
if (qr.isDark(r, c)) parts.push(`M${c} ${r}h1v1h-1z`);
|
|
10075
|
+
}
|
|
10076
|
+
}
|
|
10077
|
+
setPath({ d: parts.join(""), count });
|
|
10078
|
+
}).catch(() => {
|
|
10079
|
+
if (!cancelled) setPath(null);
|
|
10080
|
+
});
|
|
10081
|
+
return () => {
|
|
10082
|
+
cancelled = true;
|
|
10083
|
+
};
|
|
10084
|
+
}, [value]);
|
|
10085
|
+
if (!path) {
|
|
10086
|
+
return /* @__PURE__ */ jsx("svg", { viewBox: "0 0 21 21", "aria-hidden": "true" });
|
|
10087
|
+
}
|
|
10088
|
+
return /* @__PURE__ */ jsx(
|
|
10089
|
+
"svg",
|
|
10090
|
+
{
|
|
10091
|
+
viewBox: `0 0 ${path.count} ${path.count}`,
|
|
10092
|
+
shapeRendering: "crispEdges",
|
|
10093
|
+
role: "img",
|
|
10094
|
+
"aria-label": `Código para inscrição do aparelho: ${value.split("").join(" ")}`,
|
|
10095
|
+
children: /* @__PURE__ */ jsx("path", { d: path.d, fill: "#000000" })
|
|
10096
|
+
}
|
|
10097
|
+
);
|
|
10098
|
+
}
|
|
10099
|
+
function DeviceEnrollmentView({
|
|
10100
|
+
appName,
|
|
10101
|
+
handle,
|
|
10102
|
+
remainingSeconds,
|
|
10103
|
+
onCancel
|
|
10104
|
+
}) {
|
|
10105
|
+
useEffect(() => {
|
|
10106
|
+
injectThemeStyles();
|
|
10107
|
+
injectScopedStyles(STYLE_ID$2, CSS$1);
|
|
10108
|
+
}, []);
|
|
10109
|
+
return /* @__PURE__ */ jsxs("div", { className: "nfid-enroll", role: "status", "aria-live": "polite", children: [
|
|
10110
|
+
/* @__PURE__ */ jsx(StatusPill, { tone: "attention", children: "Aguardando o aparelho" }),
|
|
10111
|
+
/* @__PURE__ */ jsx("h2", { children: "Vincule seu celular" }),
|
|
10112
|
+
/* @__PURE__ */ jsxs("p", { children: [
|
|
10113
|
+
"Abra o NeoFaceID Push no celular e aponte para este código para vinculá-lo à sua conta no",
|
|
10114
|
+
" ",
|
|
10115
|
+
appName,
|
|
10116
|
+
"."
|
|
10117
|
+
] }),
|
|
10118
|
+
/* @__PURE__ */ jsx("div", { className: "nfid-enroll-qr", children: /* @__PURE__ */ jsx(QrCode, { value: handle }) }),
|
|
10119
|
+
/* @__PURE__ */ jsxs("div", { className: "nfid-enroll-handle", children: [
|
|
10120
|
+
/* @__PURE__ */ jsx("span", { className: "nfid-enroll-handle-label", children: "Ou digite este código" }),
|
|
10121
|
+
/* @__PURE__ */ jsx("span", { className: "nfid-enroll-handle-value", children: handle })
|
|
10122
|
+
] }),
|
|
10123
|
+
/* @__PURE__ */ jsxs("p", { className: "nfid-enroll-countdown", children: [
|
|
10124
|
+
"O código expira em ",
|
|
10125
|
+
formatRemaining(remainingSeconds)
|
|
10126
|
+
] }),
|
|
10127
|
+
/* @__PURE__ */ jsx("div", { className: "nfid-enroll-actions", children: /* @__PURE__ */ jsx(Button, { variant: "ghost", onClick: onCancel, children: "Cancelar" }) }),
|
|
10128
|
+
/* @__PURE__ */ jsx("div", { className: "nfid-enroll-footer", children: /* @__PURE__ */ jsx(BrandFooter, {}) })
|
|
10129
|
+
] });
|
|
10130
|
+
}
|
|
10131
|
+
function DeviceEnrolledView({ deviceLabel, onDone }) {
|
|
10132
|
+
useEffect(() => {
|
|
10133
|
+
injectThemeStyles();
|
|
10134
|
+
injectScopedStyles(STYLE_ID$2, CSS$1);
|
|
10135
|
+
}, []);
|
|
10136
|
+
return /* @__PURE__ */ jsxs("div", { className: "nfid-enroll", role: "alert", "aria-live": "assertive", children: [
|
|
10137
|
+
/* @__PURE__ */ jsxs(
|
|
10138
|
+
"svg",
|
|
10139
|
+
{
|
|
10140
|
+
className: "nfid-enroll-icon",
|
|
10141
|
+
viewBox: "0 0 24 24",
|
|
10142
|
+
fill: "none",
|
|
10143
|
+
stroke: "currentColor",
|
|
10144
|
+
strokeWidth: "1.75",
|
|
10145
|
+
strokeLinecap: "round",
|
|
10146
|
+
strokeLinejoin: "round",
|
|
10147
|
+
"aria-hidden": "true",
|
|
10148
|
+
style: { color: "#1E9E6A" },
|
|
10149
|
+
children: [
|
|
10150
|
+
/* @__PURE__ */ jsx("circle", { cx: "12", cy: "12", r: "9" }),
|
|
10151
|
+
/* @__PURE__ */ jsx("path", { d: "M8.5 12.5l2.5 2.5 4.5-5" })
|
|
10152
|
+
]
|
|
10153
|
+
}
|
|
10154
|
+
),
|
|
10155
|
+
/* @__PURE__ */ jsx("h2", { children: "Aparelho vinculado" }),
|
|
10156
|
+
deviceLabel ? /* @__PURE__ */ jsxs("p", { children: [
|
|
10157
|
+
/* @__PURE__ */ jsx("strong", { children: deviceLabel }),
|
|
10158
|
+
" agora pode confirmar suas operações."
|
|
10159
|
+
] }) : /* @__PURE__ */ jsx("p", { children: "Um aparelho acabou de ser vinculado à sua conta." }),
|
|
10160
|
+
/* @__PURE__ */ jsx("div", { className: "nfid-enroll-note", children: deviceLabel ? /* @__PURE__ */ jsx(Fragment, { children: "Se este não é o seu aparelho, procure o suporte agora — alguém pode ter vinculado um celular à sua conta." }) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
10161
|
+
/* @__PURE__ */ jsx("strong", { children: "Não conseguimos identificar qual aparelho foi vinculado." }),
|
|
10162
|
+
" Se não foi você quem acabou de fazer isso, procure o suporte agora — alguém pode ter vinculado um celular à sua conta."
|
|
10163
|
+
] }) }),
|
|
10164
|
+
/* @__PURE__ */ jsx("div", { className: "nfid-enroll-actions", children: /* @__PURE__ */ jsx(Button, { variant: "primary", onClick: onDone, children: "Entendi" }) }),
|
|
10165
|
+
/* @__PURE__ */ jsx("div", { className: "nfid-enroll-footer", children: /* @__PURE__ */ jsx(BrandFooter, {}) })
|
|
10166
|
+
] });
|
|
10167
|
+
}
|
|
10168
|
+
function DeviceEnrollmentExpiredView({
|
|
10169
|
+
onRetry,
|
|
10170
|
+
onCancel
|
|
10171
|
+
}) {
|
|
10172
|
+
useEffect(() => {
|
|
10173
|
+
injectThemeStyles();
|
|
10174
|
+
injectScopedStyles(STYLE_ID$2, CSS$1);
|
|
10175
|
+
}, []);
|
|
10176
|
+
return /* @__PURE__ */ jsxs("div", { className: "nfid-enroll", role: "alert", "aria-live": "assertive", children: [
|
|
10177
|
+
/* @__PURE__ */ jsxs(
|
|
10178
|
+
"svg",
|
|
10179
|
+
{
|
|
10180
|
+
className: "nfid-enroll-icon",
|
|
10181
|
+
viewBox: "0 0 24 24",
|
|
10182
|
+
fill: "none",
|
|
10183
|
+
stroke: "currentColor",
|
|
10184
|
+
strokeWidth: "1.75",
|
|
10185
|
+
strokeLinecap: "round",
|
|
10186
|
+
strokeLinejoin: "round",
|
|
10187
|
+
"aria-hidden": "true",
|
|
10188
|
+
style: { color: "var(--nfid-text-muted)" },
|
|
10189
|
+
children: [
|
|
10190
|
+
/* @__PURE__ */ jsx("circle", { cx: "12", cy: "12", r: "9" }),
|
|
10191
|
+
/* @__PURE__ */ jsx("path", { d: "M12 7v5l3 2" })
|
|
10192
|
+
]
|
|
10193
|
+
}
|
|
10194
|
+
),
|
|
10195
|
+
/* @__PURE__ */ jsx("h2", { children: "O código expirou" }),
|
|
10196
|
+
/* @__PURE__ */ jsx("p", { children: "Códigos de vinculação valem poucos minutos, por segurança. Gere um novo para tentar." }),
|
|
10197
|
+
/* @__PURE__ */ jsxs("div", { className: "nfid-enroll-actions", children: [
|
|
10198
|
+
/* @__PURE__ */ jsx(Button, { variant: "primary", onClick: onRetry, children: "Gerar novo código" }),
|
|
10199
|
+
/* @__PURE__ */ jsx(Button, { variant: "ghost", onClick: onCancel, children: "Agora não" })
|
|
10200
|
+
] }),
|
|
10201
|
+
/* @__PURE__ */ jsx("div", { className: "nfid-enroll-footer", children: /* @__PURE__ */ jsx(BrandFooter, {}) })
|
|
10202
|
+
] });
|
|
10203
|
+
}
|
|
10204
|
+
const CONTAINER_ID = "neofaceid-device-enrollment-container";
|
|
10205
|
+
function classifyEnrollmentFrame(frame) {
|
|
10206
|
+
var _a2, _b, _c;
|
|
10207
|
+
switch (frame.status) {
|
|
10208
|
+
case "enrolled":
|
|
10209
|
+
return {
|
|
10210
|
+
kind: "resolve",
|
|
10211
|
+
value: {
|
|
10212
|
+
status: "enrolled",
|
|
10213
|
+
// Vem `null` enquanto a PUSH-133 não sai. Propagamos o nulo em vez de
|
|
10214
|
+
// inventar um rótulo: a tela precisa poder dizer que não identificou.
|
|
10215
|
+
deviceLabel: ((_a2 = frame.data) == null ? void 0 : _a2.device_label) ?? null,
|
|
10216
|
+
enrolledAt: (_b = frame.data) == null ? void 0 : _b.enrolled_at
|
|
10217
|
+
}
|
|
10218
|
+
};
|
|
10219
|
+
case "expired":
|
|
10220
|
+
return { kind: "resolve", value: { status: "expired" } };
|
|
10221
|
+
case "error":
|
|
10222
|
+
case "failed":
|
|
10223
|
+
return {
|
|
10224
|
+
kind: "reject",
|
|
10225
|
+
error: new NeoFaceError(
|
|
10226
|
+
frame.message ?? ((_c = frame.data) == null ? void 0 : _c.message) ?? "Falha ao inscrever o aparelho",
|
|
10227
|
+
ErrorType.API_ERROR
|
|
10228
|
+
)
|
|
10229
|
+
};
|
|
10230
|
+
default:
|
|
10231
|
+
return { kind: "pending" };
|
|
10232
|
+
}
|
|
10233
|
+
}
|
|
10234
|
+
function DeviceEnrollmentModal({
|
|
10235
|
+
userAccessToken,
|
|
10236
|
+
applicationToken,
|
|
10237
|
+
appName,
|
|
10238
|
+
attempt,
|
|
10239
|
+
onSettle,
|
|
10240
|
+
onRetry,
|
|
10241
|
+
onFatal
|
|
10242
|
+
}) {
|
|
10243
|
+
const headerAppName = appName ?? getAppName() ?? "sua aplicação";
|
|
10244
|
+
const [phase, setPhase] = useState({ kind: "loading" });
|
|
10245
|
+
const [remaining, setRemaining] = useState(0);
|
|
10246
|
+
useEffect(() => {
|
|
10247
|
+
const controller = new AbortController();
|
|
10248
|
+
let done = false;
|
|
10249
|
+
(async () => {
|
|
10250
|
+
let issued;
|
|
10251
|
+
try {
|
|
10252
|
+
issued = await requestDeviceEnrollmentToken(userAccessToken, applicationToken);
|
|
10253
|
+
} catch (err) {
|
|
10254
|
+
if (!done) onFatal(err);
|
|
10255
|
+
return;
|
|
10256
|
+
}
|
|
10257
|
+
if (done) return;
|
|
10258
|
+
setPhase({
|
|
10259
|
+
kind: "waiting",
|
|
10260
|
+
handle: issued.handle,
|
|
10261
|
+
jti: issued.jti,
|
|
10262
|
+
expiresIn: issued.expiresIn
|
|
10263
|
+
});
|
|
10264
|
+
setRemaining(issued.expiresIn);
|
|
10265
|
+
const base = getBaseUrl();
|
|
10266
|
+
const channel = `${base}/api/v1/devices/enrollment/${encodeURIComponent(issued.jti)}/events/`;
|
|
10267
|
+
try {
|
|
10268
|
+
const outcome = await consumeSseStream({
|
|
10269
|
+
signal: controller.signal,
|
|
10270
|
+
timeoutMs: (issued.expiresIn + 30) * 1e3,
|
|
10271
|
+
requestTicket: async () => {
|
|
10272
|
+
var _a2;
|
|
10273
|
+
const res = await fetch(`${channel}ticket/`, {
|
|
10274
|
+
method: "POST",
|
|
10275
|
+
headers: { "Content-Type": "application/json", "X-App-Token": applicationToken }
|
|
10276
|
+
});
|
|
10277
|
+
if (!res.ok) {
|
|
10278
|
+
throw new NeoFaceError(
|
|
10279
|
+
`Falha ao abrir o canal de inscrição: ${res.status}`,
|
|
10280
|
+
res.status === 401 || res.status === 403 ? ErrorType.INVALID_TOKEN : ErrorType.NETWORK
|
|
10281
|
+
);
|
|
10282
|
+
}
|
|
10283
|
+
const body = await res.json();
|
|
10284
|
+
const ticket = ((_a2 = body == null ? void 0 : body.data) == null ? void 0 : _a2.ticket) ?? (body == null ? void 0 : body.ticket);
|
|
10285
|
+
if (!ticket) {
|
|
10286
|
+
throw new NeoFaceError("Canal de inscrição sem ticket", ErrorType.API_ERROR);
|
|
10287
|
+
}
|
|
10288
|
+
return ticket;
|
|
10289
|
+
},
|
|
10290
|
+
buildUrl: (ticket) => `${channel}?ticket=${encodeURIComponent(ticket)}`,
|
|
10291
|
+
classify: classifyEnrollmentFrame
|
|
10292
|
+
});
|
|
10293
|
+
if (done) return;
|
|
10294
|
+
if (outcome.status === "enrolled") {
|
|
10295
|
+
setPhase({
|
|
10296
|
+
kind: "enrolled",
|
|
10297
|
+
deviceLabel: outcome.deviceLabel,
|
|
10298
|
+
enrolledAt: outcome.enrolledAt
|
|
10299
|
+
});
|
|
10300
|
+
return;
|
|
10301
|
+
}
|
|
10302
|
+
setPhase({ kind: "expired" });
|
|
10303
|
+
} catch {
|
|
10304
|
+
if (done || controller.signal.aborted) return;
|
|
10305
|
+
setPhase({ kind: "expired" });
|
|
10306
|
+
}
|
|
10307
|
+
})();
|
|
10308
|
+
return () => {
|
|
10309
|
+
done = true;
|
|
10310
|
+
controller.abort();
|
|
10311
|
+
};
|
|
10312
|
+
}, [attempt]);
|
|
10313
|
+
useEffect(() => {
|
|
10314
|
+
if (phase.kind !== "waiting") return void 0;
|
|
10315
|
+
const id = setInterval(() => setRemaining((r) => r > 0 ? r - 1 : 0), 1e3);
|
|
10316
|
+
return () => clearInterval(id);
|
|
10317
|
+
}, [phase.kind]);
|
|
10318
|
+
if (phase.kind === "loading") return null;
|
|
10319
|
+
if (phase.kind === "enrolled") {
|
|
10320
|
+
return /* @__PURE__ */ jsx(Sheet, { open: true, dismissOnOverlay: false, ariaLabel: "Aparelho vinculado", children: /* @__PURE__ */ jsx(
|
|
10321
|
+
DeviceEnrolledView,
|
|
10322
|
+
{
|
|
10323
|
+
deviceLabel: phase.deviceLabel,
|
|
10324
|
+
onDone: () => onSettle({
|
|
10325
|
+
status: "enrolled",
|
|
10326
|
+
deviceLabel: phase.deviceLabel,
|
|
10327
|
+
enrolledAt: phase.enrolledAt
|
|
10328
|
+
})
|
|
10329
|
+
}
|
|
10330
|
+
) });
|
|
10331
|
+
}
|
|
10332
|
+
if (phase.kind === "expired") {
|
|
10333
|
+
return /* @__PURE__ */ jsx(Sheet, { open: true, dismissOnOverlay: false, ariaLabel: "Código expirado", children: /* @__PURE__ */ jsx(
|
|
10334
|
+
DeviceEnrollmentExpiredView,
|
|
10335
|
+
{
|
|
10336
|
+
onRetry,
|
|
10337
|
+
onCancel: () => onSettle({ status: "expired" })
|
|
10338
|
+
}
|
|
10339
|
+
) });
|
|
10340
|
+
}
|
|
10341
|
+
return /* @__PURE__ */ jsx(Sheet, { open: true, dismissOnOverlay: false, ariaLabel: "Vincular aparelho", children: /* @__PURE__ */ jsx(
|
|
10342
|
+
DeviceEnrollmentView,
|
|
10343
|
+
{
|
|
10344
|
+
appName: headerAppName,
|
|
10345
|
+
handle: phase.handle,
|
|
10346
|
+
remainingSeconds: remaining,
|
|
10347
|
+
onCancel: () => onSettle({ status: "cancelled" })
|
|
10348
|
+
}
|
|
10349
|
+
) });
|
|
10350
|
+
}
|
|
10351
|
+
function startDeviceEnrollment(options) {
|
|
10352
|
+
return new Promise((resolve, reject) => {
|
|
10353
|
+
const container = document.createElement("div");
|
|
10354
|
+
container.id = CONTAINER_ID;
|
|
10355
|
+
document.body.appendChild(container);
|
|
10356
|
+
const root = createRoot(container);
|
|
10357
|
+
let attempt = 0;
|
|
10358
|
+
const cleanup = () => {
|
|
10359
|
+
root.unmount();
|
|
10360
|
+
if (document.body.contains(container)) document.body.removeChild(container);
|
|
10361
|
+
};
|
|
10362
|
+
const render = () => {
|
|
10363
|
+
root.render(
|
|
10364
|
+
/* @__PURE__ */ jsx(
|
|
10365
|
+
DeviceEnrollmentModal,
|
|
10366
|
+
{
|
|
10367
|
+
...options,
|
|
10368
|
+
attempt,
|
|
10369
|
+
onSettle: (outcome) => {
|
|
10370
|
+
cleanup();
|
|
10371
|
+
resolve(outcome);
|
|
10372
|
+
},
|
|
10373
|
+
onRetry: () => {
|
|
10374
|
+
attempt += 1;
|
|
10375
|
+
render();
|
|
10376
|
+
},
|
|
10377
|
+
onFatal: (error) => {
|
|
10378
|
+
cleanup();
|
|
10379
|
+
reject(error);
|
|
10380
|
+
}
|
|
10381
|
+
}
|
|
10382
|
+
)
|
|
10383
|
+
);
|
|
10384
|
+
};
|
|
10385
|
+
render();
|
|
10386
|
+
});
|
|
10387
|
+
}
|
|
9990
10388
|
function evaluateFacePosition(box, videoWidth, videoHeight) {
|
|
9991
10389
|
if (!box || !videoWidth || !videoHeight) {
|
|
9992
10390
|
return {
|
|
@@ -15503,6 +15901,7 @@ export {
|
|
|
15503
15901
|
start,
|
|
15504
15902
|
startAutoLogin,
|
|
15505
15903
|
startBiometricRegistration,
|
|
15904
|
+
startDeviceEnrollment,
|
|
15506
15905
|
startDocumentCapture,
|
|
15507
15906
|
startFaceLogin,
|
|
15508
15907
|
startHandLogin,
|