cauthy-auth 0.12.0 → 0.14.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/README.md +8 -0
- package/dist/client.d.mts +86 -1
- package/dist/client.d.ts +86 -1
- package/dist/client.js +21 -7
- package/dist/client.js.map +1 -1
- package/dist/client.mjs +21 -7
- package/dist/client.mjs.map +1 -1
- package/dist/index.js +81 -2
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +81 -2
- package/dist/index.mjs.map +1 -1
- package/dist/mcp.d.mts +103 -0
- package/dist/mcp.d.ts +103 -0
- package/dist/mcp.js +218 -0
- package/dist/mcp.js.map +1 -0
- package/dist/mcp.mjs +191 -0
- package/dist/mcp.mjs.map +1 -0
- package/dist/react.d.mts +104 -1
- package/dist/react.d.ts +104 -1
- package/dist/react.js +259 -12
- package/dist/react.js.map +1 -1
- package/dist/react.mjs +259 -12
- package/dist/react.mjs.map +1 -1
- package/dist/styles.css +5 -0
- package/dist/svelte.js +21 -7
- package/dist/svelte.js.map +1 -1
- package/dist/svelte.mjs +21 -7
- package/dist/svelte.mjs.map +1 -1
- package/dist/vue.js +21 -7
- package/dist/vue.js.map +1 -1
- package/dist/vue.mjs +21 -7
- package/dist/vue.mjs.map +1 -1
- package/package.json +6 -1
package/dist/react.mjs
CHANGED
|
@@ -203,13 +203,13 @@ function createAuthClient(options = {}) {
|
|
|
203
203
|
const said = typeof options.locale === "function" ? options.locale() : options.locale;
|
|
204
204
|
return (said ?? (typeof document === "undefined" ? "" : document.documentElement?.lang) ?? "").trim();
|
|
205
205
|
};
|
|
206
|
-
async function post(path, form, bad) {
|
|
206
|
+
async function post(path, form, bad, captchaToken) {
|
|
207
207
|
if (bad) return { ok: false, error: { code: bad.code, message: bad.message, field: bad.field } };
|
|
208
208
|
try {
|
|
209
209
|
const res = await doFetch(`${base}${path}`, {
|
|
210
210
|
method: "POST",
|
|
211
211
|
credentials: "same-origin",
|
|
212
|
-
headers: { "content-type": "application/json", accept: "application/json", ...locale() ? { "x-cauthy-locale": locale() } : {} },
|
|
212
|
+
headers: { "content-type": "application/json", accept: "application/json", ...locale() ? { "x-cauthy-locale": locale() } : {}, ...captchaToken ? { "x-cauthy-captcha": captchaToken } : {} },
|
|
213
213
|
body: JSON.stringify(form)
|
|
214
214
|
});
|
|
215
215
|
const body = await res.json().catch(() => null);
|
|
@@ -261,9 +261,11 @@ function createAuthClient(options = {}) {
|
|
|
261
261
|
}
|
|
262
262
|
return {
|
|
263
263
|
signIn: {
|
|
264
|
-
email: (f) => post("/sign-in/email", { email: f.email, password: f.password }, firstProblem(checkEmail(f.email), checkPassword(f.password))),
|
|
264
|
+
email: (f) => post("/sign-in/email", { email: f.email, password: f.password }, firstProblem(checkEmail(f.email), checkPassword(f.password)), f.captchaToken),
|
|
265
265
|
mfa: (f) => post("/sign-in/mfa", { code: f.code }, checkCode(f.code)),
|
|
266
|
-
|
|
266
|
+
mfaMethods: () => post("/sign-in/mfa/methods", {}, null),
|
|
267
|
+
mfaSendCode: () => post("/sign-in/mfa/send", {}, null),
|
|
268
|
+
magicLink: (f) => post("/sign-in/magic-link", { email: f.email }, checkEmail(f.email), f.captchaToken),
|
|
267
269
|
magicLinkVerify: (f) => post("/sign-in/magic-link/verify", { token: f.token }, null),
|
|
268
270
|
emailCode: (f) => post("/sign-in/email-code", { code: f.code }, checkCode(f.code)),
|
|
269
271
|
passkeyUrl,
|
|
@@ -344,9 +346,9 @@ function createAuthClient(options = {}) {
|
|
|
344
346
|
}
|
|
345
347
|
},
|
|
346
348
|
signUp: {
|
|
347
|
-
email: (f) => post("/sign-up/email", { email: f.email, password: f.password, name: f.name }, firstProblem(checkName(f.name), checkEmail(f.email), checkNewPassword(f.password)))
|
|
349
|
+
email: (f) => post("/sign-up/email", { email: f.email, password: f.password, name: f.name }, firstProblem(checkName(f.name), checkEmail(f.email), checkNewPassword(f.password)), f.captchaToken)
|
|
348
350
|
},
|
|
349
|
-
forgotPassword: (f) => post("/forgot-password", { email: f.email }, checkEmail(f.email)),
|
|
351
|
+
forgotPassword: (f) => post("/forgot-password", { email: f.email }, checkEmail(f.email), f.captchaToken),
|
|
350
352
|
resetPassword: (f) => post("/reset-password", { token: f.token, password: f.password }, checkNewPassword(f.password)),
|
|
351
353
|
verifyEmail: (f) => post("/verify-email", { token: f.token }, null),
|
|
352
354
|
signOut: () => post("/sign-out", {}, null),
|
|
@@ -364,7 +366,14 @@ function createAuthClient(options = {}) {
|
|
|
364
366
|
setup: (f) => post("/me/mfa/setup", { password: f.password }, null),
|
|
365
367
|
verify: (f) => post("/me/mfa/verify", { code: f.code }, checkCode(f.code)),
|
|
366
368
|
disable: (f) => post("/me/mfa/disable", { password: f.password }, null),
|
|
367
|
-
recoveryCodes: (f) => post("/me/mfa/recovery-codes", { password: f.password }, null)
|
|
369
|
+
recoveryCodes: (f) => post("/me/mfa/recovery-codes", { password: f.password }, null),
|
|
370
|
+
async factors() {
|
|
371
|
+
const body = await read("/me/mfa/factors");
|
|
372
|
+
return body && typeof body.totp === "boolean" ? body : null;
|
|
373
|
+
},
|
|
374
|
+
addPhone: (f) => post("/me/mfa/phone", { phone: f.phone, channel: f.channel, password: f.password }, null),
|
|
375
|
+
verifyPhone: (f) => post("/me/mfa/phone/verify", { code: f.code }, checkCode(f.code)),
|
|
376
|
+
removePhone: (f) => post("/me/mfa/phone/remove", { password: f.password }, null)
|
|
368
377
|
},
|
|
369
378
|
changeEmail: (f) => post("/me/email", { email: f.email, password: f.password }, checkEmail(f.email)),
|
|
370
379
|
async identities() {
|
|
@@ -381,6 +390,11 @@ function createAuthClient(options = {}) {
|
|
|
381
390
|
return body && Array.isArray(body.sessions) ? body.sessions : [];
|
|
382
391
|
},
|
|
383
392
|
signOutEverywhere: () => post("/me/sessions/end", {}, null),
|
|
393
|
+
async agents() {
|
|
394
|
+
const body = await read("/me/agents");
|
|
395
|
+
return body && Array.isArray(body.agents) ? body.agents : [];
|
|
396
|
+
},
|
|
397
|
+
disconnectAgent: (id) => post("/me/agents/disconnect", { id }, null),
|
|
384
398
|
async passkeys() {
|
|
385
399
|
const body = await read("/me/passkeys");
|
|
386
400
|
return body && Array.isArray(body.passkeys) ? body.passkeys : [];
|
|
@@ -482,6 +496,18 @@ var TEXTS = {
|
|
|
482
496
|
linkSent: "If that address has an account, a link to choose a new password is on its way.",
|
|
483
497
|
codeHelp: "Enter the code from your authenticator app, or a recovery code.",
|
|
484
498
|
verify: "Verify",
|
|
499
|
+
codeHelpPhone: "We send a code to your phone. Ask for it, then type it here, or use a recovery code.",
|
|
500
|
+
sendCodeSms: "Send a code by SMS to {to}",
|
|
501
|
+
sendCodeWhatsapp: "Send a code by WhatsApp to {to}",
|
|
502
|
+
codeSent: "We sent a code to {to}. Type it here.",
|
|
503
|
+
phone: "Phone, with its country code",
|
|
504
|
+
phoneHelp: "Get your codes by SMS or WhatsApp instead of an app.",
|
|
505
|
+
phoneOn: "Codes go to {to}.",
|
|
506
|
+
phoneOff: "No phone yet.",
|
|
507
|
+
addPhone: "Add phone",
|
|
508
|
+
removePhone: "Remove phone",
|
|
509
|
+
bySms: "SMS",
|
|
510
|
+
byWhatsapp: "WhatsApp",
|
|
485
511
|
checkEmail: "Almost there: open the link we sent to confirm your address, then sign in.",
|
|
486
512
|
backToSignIn: "Back to sign in",
|
|
487
513
|
emailMeALink: "Email me a sign-in link",
|
|
@@ -520,6 +546,10 @@ var TEXTS = {
|
|
|
520
546
|
passkeyUnnamed: "Passkey",
|
|
521
547
|
sessions: "Where you are signed in",
|
|
522
548
|
signOutEverywhere: "Sign out everywhere",
|
|
549
|
+
agents: "AI agents",
|
|
550
|
+
agentsHelp: "Agents you allowed to act for you. Disconnect one and it loses its access within minutes.",
|
|
551
|
+
disconnect: "Disconnect",
|
|
552
|
+
anAgent: "An AI agent",
|
|
523
553
|
unknownDevice: "Unknown device",
|
|
524
554
|
emailAddress: "Email address",
|
|
525
555
|
newEmail: "New email address",
|
|
@@ -549,6 +579,13 @@ var TEXTS = {
|
|
|
549
579
|
step_up_required: "Enter your current password.",
|
|
550
580
|
weak_password: "Choose a stronger password.",
|
|
551
581
|
unauthenticated: "Sign in first.",
|
|
582
|
+
phone_refused: "The code could not be sent to that number.",
|
|
583
|
+
too_many_codes: "Enough codes were sent. Use the last one, or sign in again.",
|
|
584
|
+
phone_unavailable: "Codes cannot be sent that way here.",
|
|
585
|
+
phone_already_set: "Remove the phone you have first.",
|
|
586
|
+
phone_codes_unavailable: "Codes cannot be sent right now. Try again in a moment.",
|
|
587
|
+
captcha_required: "Complete the check that you are not a bot, then try again.",
|
|
588
|
+
captcha_failed: "The check that you are not a bot did not pass. Try it again.",
|
|
552
589
|
passkey_failed: "The passkey could not be used. Try again, or sign in another way.",
|
|
553
590
|
passkey_exists: "This device already has a passkey for your account.",
|
|
554
591
|
passkey_unavailable: "Passkeys cannot be used here."
|
|
@@ -571,6 +608,18 @@ var TEXTS = {
|
|
|
571
608
|
linkSent: "Si ese correo tiene una cuenta, ya va en camino un enlace para elegir una contrase\xF1a nueva.",
|
|
572
609
|
codeHelp: "Escribe el c\xF3digo de tu aplicaci\xF3n de autenticaci\xF3n, o un c\xF3digo de recuperaci\xF3n.",
|
|
573
610
|
verify: "Verificar",
|
|
611
|
+
codeHelpPhone: "Te enviamos un c\xF3digo a tu tel\xE9fono. P\xEDdelo y escr\xEDbelo aqu\xED, o usa un c\xF3digo de recuperaci\xF3n.",
|
|
612
|
+
sendCodeSms: "Enviar un c\xF3digo por SMS a {to}",
|
|
613
|
+
sendCodeWhatsapp: "Enviar un c\xF3digo por WhatsApp a {to}",
|
|
614
|
+
codeSent: "Te enviamos un c\xF3digo a {to}. Escr\xEDbelo aqu\xED.",
|
|
615
|
+
phone: "Tel\xE9fono, con su c\xF3digo de pa\xEDs",
|
|
616
|
+
phoneHelp: "Recibe tus c\xF3digos por SMS o WhatsApp en vez de una app.",
|
|
617
|
+
phoneOn: "Los c\xF3digos van a {to}.",
|
|
618
|
+
phoneOff: "Todav\xEDa no tienes un tel\xE9fono.",
|
|
619
|
+
addPhone: "Agregar tel\xE9fono",
|
|
620
|
+
removePhone: "Quitar tel\xE9fono",
|
|
621
|
+
bySms: "SMS",
|
|
622
|
+
byWhatsapp: "WhatsApp",
|
|
574
623
|
checkEmail: "Ya casi: abre el enlace que te enviamos para confirmar tu correo y luego entra.",
|
|
575
624
|
backToSignIn: "Volver a entrar",
|
|
576
625
|
emailMeALink: "Env\xEDame un enlace para entrar",
|
|
@@ -609,6 +658,10 @@ var TEXTS = {
|
|
|
609
658
|
passkeyUnnamed: "Llave de acceso",
|
|
610
659
|
sessions: "D\xF3nde tienes la sesi\xF3n abierta",
|
|
611
660
|
signOutEverywhere: "Cerrar sesi\xF3n en todas partes",
|
|
661
|
+
agents: "Agentes de IA",
|
|
662
|
+
agentsHelp: "Los agentes a los que permitiste actuar por ti. Si desconectas uno, pierde el acceso en pocos minutos.",
|
|
663
|
+
disconnect: "Desconectar",
|
|
664
|
+
anAgent: "Un agente de IA",
|
|
612
665
|
unknownDevice: "Dispositivo desconocido",
|
|
613
666
|
emailAddress: "Correo",
|
|
614
667
|
newEmail: "Correo nuevo",
|
|
@@ -638,6 +691,13 @@ var TEXTS = {
|
|
|
638
691
|
step_up_required: "Escribe tu contrase\xF1a actual.",
|
|
639
692
|
weak_password: "Elige una contrase\xF1a m\xE1s fuerte.",
|
|
640
693
|
unauthenticated: "Inicia sesi\xF3n primero.",
|
|
694
|
+
phone_refused: "No se pudo enviar el c\xF3digo a ese n\xFAmero.",
|
|
695
|
+
too_many_codes: "Ya se enviaron suficientes c\xF3digos. Usa el \xFAltimo o vuelve a entrar.",
|
|
696
|
+
phone_unavailable: "Aqu\xED no se pueden enviar c\xF3digos de esa forma.",
|
|
697
|
+
phone_already_set: "Primero quita el tel\xE9fono que tienes.",
|
|
698
|
+
phone_codes_unavailable: "Ahora no se pueden enviar c\xF3digos. Int\xE9ntalo en un momento.",
|
|
699
|
+
captcha_required: "Completa la verificaci\xF3n de que no eres un robot y vuelve a intentarlo.",
|
|
700
|
+
captcha_failed: "La verificaci\xF3n de que no eres un robot no pas\xF3. Int\xE9ntalo de nuevo.",
|
|
641
701
|
passkey_failed: "No se pudo usar la llave de acceso. Intenta de nuevo o entra de otra forma.",
|
|
642
702
|
passkey_exists: "Este dispositivo ya tiene una llave de acceso de tu cuenta.",
|
|
643
703
|
passkey_unavailable: "Aqu\xED no se pueden usar llaves de acceso."
|
|
@@ -682,6 +742,63 @@ function useRoot(props) {
|
|
|
682
742
|
if (props.unstyled) return {};
|
|
683
743
|
return { "data-cauthy": "", style: vars };
|
|
684
744
|
}
|
|
745
|
+
var turnstileScript = null;
|
|
746
|
+
function loadTurnstile() {
|
|
747
|
+
const ready = () => window.turnstile;
|
|
748
|
+
if (ready()) return Promise.resolve(ready());
|
|
749
|
+
turnstileScript ??= new Promise((resolve, reject) => {
|
|
750
|
+
const script = document.createElement("script");
|
|
751
|
+
script.src = "https://challenges.cloudflare.com/turnstile/v0/api.js?render=explicit";
|
|
752
|
+
script.async = true;
|
|
753
|
+
script.onload = () => ready() ? resolve(ready()) : reject(new Error("turnstile did not load"));
|
|
754
|
+
script.onerror = () => {
|
|
755
|
+
turnstileScript = null;
|
|
756
|
+
reject(new Error("turnstile did not load"));
|
|
757
|
+
};
|
|
758
|
+
document.head.appendChild(script);
|
|
759
|
+
});
|
|
760
|
+
return turnstileScript;
|
|
761
|
+
}
|
|
762
|
+
function useChallenge(siteKey, locale) {
|
|
763
|
+
const box = useRef(null);
|
|
764
|
+
const widget = useRef(null);
|
|
765
|
+
const [token, setToken] = useState("");
|
|
766
|
+
useEffect(() => {
|
|
767
|
+
if (!siteKey || !box.current) return;
|
|
768
|
+
let gone = false;
|
|
769
|
+
void loadTurnstile().then((api) => {
|
|
770
|
+
if (gone || !box.current) return;
|
|
771
|
+
const id = api.render(box.current, {
|
|
772
|
+
sitekey: siteKey,
|
|
773
|
+
language: locale ?? "auto",
|
|
774
|
+
size: "flexible",
|
|
775
|
+
callback: (t) => setToken(t),
|
|
776
|
+
"expired-callback": () => setToken(""),
|
|
777
|
+
"error-callback": () => setToken("")
|
|
778
|
+
});
|
|
779
|
+
widget.current = { api, id };
|
|
780
|
+
}).catch(() => {
|
|
781
|
+
});
|
|
782
|
+
return () => {
|
|
783
|
+
gone = true;
|
|
784
|
+
if (widget.current) widget.current.api.remove(widget.current.id);
|
|
785
|
+
widget.current = null;
|
|
786
|
+
};
|
|
787
|
+
}, [siteKey, locale]);
|
|
788
|
+
const renew = () => {
|
|
789
|
+
setToken("");
|
|
790
|
+
if (widget.current) widget.current.api.reset(widget.current.id);
|
|
791
|
+
};
|
|
792
|
+
const node = siteKey ? /* @__PURE__ */ jsx("div", { ref: box, className: "cauthy-challenge" }) : null;
|
|
793
|
+
return { node, token: token || void 0, renew };
|
|
794
|
+
}
|
|
795
|
+
function useMethods(auth) {
|
|
796
|
+
const [methods, setMethods] = useState(null);
|
|
797
|
+
useEffect(() => {
|
|
798
|
+
void auth.methods().then(setMethods);
|
|
799
|
+
}, []);
|
|
800
|
+
return methods;
|
|
801
|
+
}
|
|
685
802
|
function useForm(props) {
|
|
686
803
|
const t = { ...TEXTS[props.locale ?? "en"], ...props.texts, errors: { ...TEXTS[props.locale ?? "en"].errors, ...props.texts?.errors } };
|
|
687
804
|
const cx = (slot) => props.classNames?.[slot] ?? `cauthy-${slot}`;
|
|
@@ -754,6 +871,23 @@ function SignIn(props) {
|
|
|
754
871
|
const [password, setPassword] = useState("");
|
|
755
872
|
const [code, setCode] = useState("");
|
|
756
873
|
const [step, setStep] = useState("password");
|
|
874
|
+
const challenge = useChallenge(methods?.captchaSiteKey, props.locale);
|
|
875
|
+
const [phoneWay, setPhoneWay] = useState(null);
|
|
876
|
+
const [phoneSent, setPhoneSent] = useState("");
|
|
877
|
+
useEffect(() => {
|
|
878
|
+
if (step !== "code") return;
|
|
879
|
+
void auth.signIn.mfaMethods().then((r) => {
|
|
880
|
+
if (r.ok) setPhoneWay({ totp: r.totp, phone: r.phone });
|
|
881
|
+
});
|
|
882
|
+
}, [step]);
|
|
883
|
+
const sendCode = async () => {
|
|
884
|
+
f.setBusy(true);
|
|
885
|
+
f.setError(null);
|
|
886
|
+
const r = await auth.signIn.mfaSendCode();
|
|
887
|
+
f.setBusy(false);
|
|
888
|
+
if (!r.ok) return f.setError(r.error);
|
|
889
|
+
setPhoneSent(r.sentTo);
|
|
890
|
+
};
|
|
757
891
|
const autofill = useRef(null);
|
|
758
892
|
const [offer, setOffer] = useState(0);
|
|
759
893
|
useEffect(() => {
|
|
@@ -788,8 +922,9 @@ function SignIn(props) {
|
|
|
788
922
|
autofill.current?.abort();
|
|
789
923
|
f.setBusy(true);
|
|
790
924
|
f.setError(null);
|
|
791
|
-
const r = step === "code" ? await auth.signIn.mfa({ code }) : step === "sent" ? await auth.signIn.emailCode({ code }) : await auth.signIn.email({ email, password });
|
|
925
|
+
const r = step === "code" ? await auth.signIn.mfa({ code }) : step === "sent" ? await auth.signIn.emailCode({ code }) : await auth.signIn.email({ email, password, captchaToken: challenge.token });
|
|
792
926
|
f.setBusy(false);
|
|
927
|
+
if (step === "password") challenge.renew();
|
|
793
928
|
if (!r.ok) {
|
|
794
929
|
setOffer((n) => n + 1);
|
|
795
930
|
return f.setError(r.error);
|
|
@@ -812,8 +947,9 @@ function SignIn(props) {
|
|
|
812
947
|
autofill.current?.abort();
|
|
813
948
|
f.setBusy(true);
|
|
814
949
|
f.setError(null);
|
|
815
|
-
const r = await auth.signIn.magicLink({ email });
|
|
950
|
+
const r = await auth.signIn.magicLink({ email, captchaToken: challenge.token });
|
|
816
951
|
f.setBusy(false);
|
|
952
|
+
challenge.renew();
|
|
817
953
|
if (!r.ok) return f.setError(r.error);
|
|
818
954
|
setStep("sent");
|
|
819
955
|
};
|
|
@@ -829,9 +965,11 @@ function SignIn(props) {
|
|
|
829
965
|
] });
|
|
830
966
|
}
|
|
831
967
|
if (step === "code") {
|
|
968
|
+
const to = phoneWay?.phone?.number ?? "";
|
|
832
969
|
return /* @__PURE__ */ jsxs("form", { className: f.cx("form"), ...f.root, onSubmit: submit, noValidate: true, children: [
|
|
833
|
-
/* @__PURE__ */ jsx("p", { className: f.cx("notice"), children: f.t.codeHelp }),
|
|
970
|
+
/* @__PURE__ */ jsx("p", { className: f.cx("notice"), children: phoneSent ? f.t.codeSent.replace("{to}", phoneSent) : phoneWay?.phone && !phoneWay.totp ? f.t.codeHelpPhone : f.t.codeHelp }),
|
|
834
971
|
general && /* @__PURE__ */ jsx("p", { role: "alert", className: f.cx("error"), children: f.say(general) }),
|
|
972
|
+
phoneWay?.phone && /* @__PURE__ */ jsx(f.Button, { type: "button", disabled: f.busy, className: f.cx("providerButton"), onClick: () => void sendCode(), children: (phoneWay.phone.channel === "whatsapp" ? f.t.sendCodeWhatsapp : f.t.sendCodeSms).replace("{to}", to) }),
|
|
835
973
|
/* @__PURE__ */ jsx(Field, { f, name: "code", type: "text", label: f.t.code, value: code, autoComplete: "one-time-code", required: true, onChange: setCode }),
|
|
836
974
|
/* @__PURE__ */ jsx(f.Button, { type: "submit", disabled: f.busy, className: f.cx("button"), children: f.t.verify })
|
|
837
975
|
] });
|
|
@@ -860,6 +998,7 @@ function SignIn(props) {
|
|
|
860
998
|
aside: props.links?.forgotPassword ? /* @__PURE__ */ jsx("a", { className: f.cx("link"), href: props.links.forgotPassword, children: f.t.forgotPassword }) : void 0
|
|
861
999
|
}
|
|
862
1000
|
),
|
|
1001
|
+
challenge.node,
|
|
863
1002
|
/* @__PURE__ */ jsx(f.Button, { type: "submit", disabled: f.busy, className: f.cx("button"), children: f.t.signIn }),
|
|
864
1003
|
methods?.magicLink && /* @__PURE__ */ jsx(f.Button, { type: "button", disabled: f.busy, className: f.cx("providerButton"), onClick: () => void sendLink(), children: f.t.emailMeALink }),
|
|
865
1004
|
methods?.signUp !== false && props.links?.signUp && /* @__PURE__ */ jsxs("p", { className: f.cx("footer"), children: [
|
|
@@ -891,12 +1030,14 @@ function SignUp(props) {
|
|
|
891
1030
|
const [email, setEmail] = useState("");
|
|
892
1031
|
const [password, setPassword] = useState("");
|
|
893
1032
|
const [sent, setSent] = useState(false);
|
|
1033
|
+
const challenge = useChallenge(useMethods(auth)?.captchaSiteKey, props.locale);
|
|
894
1034
|
const submit = async (e) => {
|
|
895
1035
|
e.preventDefault();
|
|
896
1036
|
f.setBusy(true);
|
|
897
1037
|
f.setError(null);
|
|
898
|
-
const r = await auth.signUp.email({ name, email, password });
|
|
1038
|
+
const r = await auth.signUp.email({ name, email, password, captchaToken: challenge.token });
|
|
899
1039
|
f.setBusy(false);
|
|
1040
|
+
challenge.renew();
|
|
900
1041
|
if (!r.ok) return f.setError(r.error);
|
|
901
1042
|
if (r.status === "verification_required") return setSent(true);
|
|
902
1043
|
f.done();
|
|
@@ -913,6 +1054,7 @@ function SignUp(props) {
|
|
|
913
1054
|
/* @__PURE__ */ jsx(Field, { f, name: "name", type: "text", label: f.t.name, value: name, autoComplete: "name", onChange: setName }),
|
|
914
1055
|
/* @__PURE__ */ jsx(Field, { f, name: "email", type: "email", label: f.t.email, value: email, autoComplete: "username", required: true, onChange: setEmail }),
|
|
915
1056
|
/* @__PURE__ */ jsx(Field, { f, name: "password", type: "password", label: f.t.password, value: password, autoComplete: "new-password", required: true, onChange: setPassword }),
|
|
1057
|
+
challenge.node,
|
|
916
1058
|
/* @__PURE__ */ jsx(f.Button, { type: "submit", disabled: f.busy, className: f.cx("button"), children: f.t.signUp }),
|
|
917
1059
|
props.links?.signIn && /* @__PURE__ */ jsxs("p", { className: f.cx("footer"), children: [
|
|
918
1060
|
f.t.haveAccount,
|
|
@@ -926,12 +1068,14 @@ function ForgotPassword(props) {
|
|
|
926
1068
|
const f = useForm(props);
|
|
927
1069
|
const [email, setEmail] = useState("");
|
|
928
1070
|
const [sent, setSent] = useState(false);
|
|
1071
|
+
const challenge = useChallenge(useMethods(auth)?.captchaSiteKey, props.locale);
|
|
929
1072
|
const submit = async (e) => {
|
|
930
1073
|
e.preventDefault();
|
|
931
1074
|
f.setBusy(true);
|
|
932
1075
|
f.setError(null);
|
|
933
|
-
const r = await auth.forgotPassword({ email });
|
|
1076
|
+
const r = await auth.forgotPassword({ email, captchaToken: challenge.token });
|
|
934
1077
|
f.setBusy(false);
|
|
1078
|
+
challenge.renew();
|
|
935
1079
|
if (!r.ok) return f.setError(r.error);
|
|
936
1080
|
setSent(true);
|
|
937
1081
|
};
|
|
@@ -945,6 +1089,7 @@ function ForgotPassword(props) {
|
|
|
945
1089
|
return /* @__PURE__ */ jsxs("form", { className: f.cx("form"), ...f.root, onSubmit: submit, noValidate: true, children: [
|
|
946
1090
|
general && /* @__PURE__ */ jsx("p", { role: "alert", className: f.cx("error"), children: f.say(general) }),
|
|
947
1091
|
/* @__PURE__ */ jsx(Field, { f, name: "email", type: "email", label: f.t.email, value: email, autoComplete: "username", required: true, onChange: setEmail }),
|
|
1092
|
+
challenge.node,
|
|
948
1093
|
/* @__PURE__ */ jsx(f.Button, { type: "submit", disabled: f.busy, className: f.cx("button"), children: f.t.sendLink }),
|
|
949
1094
|
props.links?.signIn && /* @__PURE__ */ jsx("p", { className: f.cx("footer"), children: /* @__PURE__ */ jsx("a", { className: f.cx("link"), href: props.links.signIn, children: f.t.backToSignIn }) })
|
|
950
1095
|
] });
|
|
@@ -1044,7 +1189,9 @@ function Account(props) {
|
|
|
1044
1189
|
account.hasPassword && /* @__PURE__ */ jsx(PasswordPart, { props }),
|
|
1045
1190
|
/* @__PURE__ */ jsx(LinkedPart, { props }),
|
|
1046
1191
|
/* @__PURE__ */ jsx(SecondStepPart, { props, account, onChanged: (mfaEnabled) => setAccount({ ...account, mfaEnabled }) }),
|
|
1192
|
+
/* @__PURE__ */ jsx(PhonePart, { props, account, onChanged: (mfaEnabled) => setAccount({ ...account, mfaEnabled }) }),
|
|
1047
1193
|
/* @__PURE__ */ jsx(PasskeysPart, { props, account, accountUrl: accountUrl(), when }),
|
|
1194
|
+
/* @__PURE__ */ jsx(AgentsPart, { props, when }),
|
|
1048
1195
|
/* @__PURE__ */ jsxs("section", { className: cx("section"), children: [
|
|
1049
1196
|
/* @__PURE__ */ jsx("h2", { className: cx("heading"), children: t.sessions }),
|
|
1050
1197
|
/* @__PURE__ */ jsx("ul", { className: cx("sessions"), children: sessions.map((s, i) => /* @__PURE__ */ jsxs("li", { className: cx("session"), children: [
|
|
@@ -1273,6 +1420,106 @@ function SecondStepPart({ props, account, onChanged }) {
|
|
|
1273
1420
|
account.mfaEnabled && /* @__PURE__ */ jsx(f.Button, { type: "button", disabled: f.busy, className: f.cx("providerButton"), onClick: () => void renew(), children: f.t.newRecoveryCodes })
|
|
1274
1421
|
] });
|
|
1275
1422
|
}
|
|
1423
|
+
function PhonePart({ props, account, onChanged }) {
|
|
1424
|
+
const auth = useAuth();
|
|
1425
|
+
const { f, run, general } = useSection(props);
|
|
1426
|
+
const [ways, setWays] = useState(null);
|
|
1427
|
+
const [number, setNumber] = useState("");
|
|
1428
|
+
const [channel, setChannel] = useState("sms");
|
|
1429
|
+
const [password, setPassword] = useState("");
|
|
1430
|
+
const [code, setCode] = useState("");
|
|
1431
|
+
const [sentTo, setSentTo] = useState("");
|
|
1432
|
+
const [codes, setCodes] = useState(null);
|
|
1433
|
+
const load = () => auth.account.mfa.factors().then((w) => {
|
|
1434
|
+
setWays(w);
|
|
1435
|
+
if (w && w.phoneChannels.length > 0 && !w.phoneChannels.includes(channel)) setChannel(w.phoneChannels[0]);
|
|
1436
|
+
});
|
|
1437
|
+
useEffect(() => {
|
|
1438
|
+
void load();
|
|
1439
|
+
}, []);
|
|
1440
|
+
if (!ways || ways.phoneChannels.length === 0) return null;
|
|
1441
|
+
const add = async (e) => {
|
|
1442
|
+
e.preventDefault();
|
|
1443
|
+
const r = await run(() => auth.account.mfa.addPhone({ phone: number, channel, password }));
|
|
1444
|
+
if (r.ok) setSentTo(r.sentTo);
|
|
1445
|
+
};
|
|
1446
|
+
const confirm = async (e) => {
|
|
1447
|
+
e.preventDefault();
|
|
1448
|
+
const r = await run(() => auth.account.mfa.verifyPhone({ code }));
|
|
1449
|
+
if (!r.ok) return;
|
|
1450
|
+
setSentTo("");
|
|
1451
|
+
setCode("");
|
|
1452
|
+
setPassword("");
|
|
1453
|
+
if (r.recoveryCodes.length > 0) setCodes(r.recoveryCodes);
|
|
1454
|
+
onChanged(true);
|
|
1455
|
+
await load();
|
|
1456
|
+
};
|
|
1457
|
+
const remove = async (e) => {
|
|
1458
|
+
e.preventDefault();
|
|
1459
|
+
const r = await run(() => auth.account.mfa.removePhone({ password }));
|
|
1460
|
+
if (!r.ok) return;
|
|
1461
|
+
setPassword("");
|
|
1462
|
+
await load();
|
|
1463
|
+
const left = await auth.account.mfa.factors();
|
|
1464
|
+
onChanged(Boolean(left?.totp));
|
|
1465
|
+
};
|
|
1466
|
+
const passwordField = account.hasPassword && /* @__PURE__ */ jsx(Field, { f, name: "password", type: "password", label: f.t.currentPassword, value: password, autoComplete: "current-password", required: true, onChange: setPassword });
|
|
1467
|
+
if (sentTo) {
|
|
1468
|
+
return /* @__PURE__ */ jsxs("form", { className: f.cx("section"), onSubmit: confirm, noValidate: true, children: [
|
|
1469
|
+
/* @__PURE__ */ jsx("h2", { className: f.cx("heading"), children: f.t.phone }),
|
|
1470
|
+
/* @__PURE__ */ jsx("p", { className: f.cx("notice"), children: f.t.codeSent.replace("{to}", sentTo) }),
|
|
1471
|
+
general && /* @__PURE__ */ jsx("p", { role: "alert", className: f.cx("error"), children: f.say(general) }),
|
|
1472
|
+
/* @__PURE__ */ jsx(Field, { f, name: "code", type: "text", label: f.t.code, value: code, autoComplete: "one-time-code", required: true, onChange: setCode }),
|
|
1473
|
+
/* @__PURE__ */ jsx(f.Button, { type: "submit", disabled: f.busy, className: f.cx("button"), children: f.t.verify })
|
|
1474
|
+
] });
|
|
1475
|
+
}
|
|
1476
|
+
return /* @__PURE__ */ jsxs("form", { className: f.cx("section"), onSubmit: ways.phone ? remove : add, noValidate: true, children: [
|
|
1477
|
+
/* @__PURE__ */ jsx("h2", { className: f.cx("heading"), children: f.t.phone }),
|
|
1478
|
+
/* @__PURE__ */ jsx("p", { className: f.cx("notice"), children: ways.phone ? f.t.phoneOn.replace("{to}", ways.phone.number) : `${f.t.phoneOff} ${f.t.phoneHelp}` }),
|
|
1479
|
+
codes && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1480
|
+
/* @__PURE__ */ jsx("p", { className: f.cx("notice"), children: f.t.keepCodes }),
|
|
1481
|
+
/* @__PURE__ */ jsx("ul", { className: f.cx("codes"), children: codes.map((c) => /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsx("code", { children: c }) }, c)) })
|
|
1482
|
+
] }),
|
|
1483
|
+
general && /* @__PURE__ */ jsx("p", { role: "alert", className: f.cx("error"), children: f.say(general) }),
|
|
1484
|
+
!ways.phone && /* @__PURE__ */ jsx(Field, { f, name: "phone", type: "tel", label: f.t.phone, value: number, autoComplete: "tel", required: true, onChange: setNumber }),
|
|
1485
|
+
!ways.phone && ways.phoneChannels.length > 1 && /* @__PURE__ */ jsx("div", { className: f.cx("field"), role: "radiogroup", children: ways.phoneChannels.map((c) => /* @__PURE__ */ jsxs("label", { className: f.cx("label"), children: [
|
|
1486
|
+
/* @__PURE__ */ jsx("input", { type: "radio", name: "channel", value: c, checked: channel === c, onChange: () => setChannel(c) }),
|
|
1487
|
+
" ",
|
|
1488
|
+
c === "whatsapp" ? f.t.byWhatsapp : f.t.bySms
|
|
1489
|
+
] }, c)) }),
|
|
1490
|
+
passwordField,
|
|
1491
|
+
/* @__PURE__ */ jsx(f.Button, { type: "submit", disabled: f.busy, className: f.cx("button"), children: ways.phone ? f.t.removePhone : f.t.addPhone })
|
|
1492
|
+
] });
|
|
1493
|
+
}
|
|
1494
|
+
function AgentsPart({ props, when }) {
|
|
1495
|
+
const auth = useAuth();
|
|
1496
|
+
const { f, run, general } = useSection(props);
|
|
1497
|
+
const [list, setList] = useState([]);
|
|
1498
|
+
useEffect(() => {
|
|
1499
|
+
void auth.account.agents().then(setList);
|
|
1500
|
+
}, []);
|
|
1501
|
+
if (list.length === 0) return null;
|
|
1502
|
+
const disconnect = async (id) => {
|
|
1503
|
+
const r = await run(() => auth.account.disconnectAgent(id));
|
|
1504
|
+
if (r.ok) setList(list.filter((a) => a.id !== id));
|
|
1505
|
+
};
|
|
1506
|
+
return /* @__PURE__ */ jsxs("section", { className: f.cx("section"), children: [
|
|
1507
|
+
/* @__PURE__ */ jsx("h2", { className: f.cx("heading"), children: f.t.agents }),
|
|
1508
|
+
/* @__PURE__ */ jsx("p", { className: f.cx("notice"), children: f.t.agentsHelp }),
|
|
1509
|
+
general && /* @__PURE__ */ jsx("p", { role: "alert", className: f.cx("error"), children: f.say(general) }),
|
|
1510
|
+
/* @__PURE__ */ jsx("ul", { className: f.cx("sessions"), children: list.map((a) => /* @__PURE__ */ jsxs("li", { className: f.cx("session"), children: [
|
|
1511
|
+
a.name || f.t.anAgent,
|
|
1512
|
+
a.redirectHost ? ` (${a.redirectHost})` : "",
|
|
1513
|
+
" \u2192 ",
|
|
1514
|
+
a.resource.name,
|
|
1515
|
+
a.scopes.length > 0 ? ` \xB7 ${a.scopes.map((s) => s.description || s.name).join(", ")}` : "",
|
|
1516
|
+
" \xB7 ",
|
|
1517
|
+
when(a.lastUsedAt),
|
|
1518
|
+
" ",
|
|
1519
|
+
/* @__PURE__ */ jsx(f.Button, { type: "button", disabled: f.busy, className: f.cx("providerButton"), onClick: () => void disconnect(a.id), children: f.t.disconnect })
|
|
1520
|
+
] }, a.id)) })
|
|
1521
|
+
] });
|
|
1522
|
+
}
|
|
1276
1523
|
function SignOutEverywhere({ props }) {
|
|
1277
1524
|
const auth = useAuth();
|
|
1278
1525
|
const { f, run, general } = useSection(props);
|