cauthy-auth 0.11.0 → 0.13.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 +3 -2
- package/dist/cli.js +299 -20
- package/dist/client.d.mts +94 -1
- package/dist/client.d.ts +94 -1
- package/dist/client.js +107 -9
- package/dist/client.js.map +1 -1
- package/dist/client.mjs +106 -9
- package/dist/client.mjs.map +1 -1
- package/dist/index.js +67 -1
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +67 -1
- package/dist/index.mjs.map +1 -1
- package/dist/react.d.mts +123 -1
- package/dist/react.d.ts +123 -1
- package/dist/react.js +358 -17
- package/dist/react.js.map +1 -1
- package/dist/react.mjs +356 -17
- package/dist/react.mjs.map +1 -1
- package/dist/styles.css +5 -0
- package/dist/svelte.js +105 -9
- package/dist/svelte.js.map +1 -1
- package/dist/svelte.mjs +105 -9
- package/dist/svelte.mjs.map +1 -1
- package/dist/vue.js +105 -9
- package/dist/vue.js.map +1 -1
- package/dist/vue.mjs +105 -9
- package/dist/vue.mjs.map +1 -1
- package/package.json +1 -1
package/dist/react.js
CHANGED
|
@@ -24,11 +24,13 @@ __export(react_exports, {
|
|
|
24
24
|
Account: () => Account,
|
|
25
25
|
CauthyProvider: () => CauthyProvider,
|
|
26
26
|
ForgotPassword: () => ForgotPassword,
|
|
27
|
+
GoogleOneTap: () => GoogleOneTap,
|
|
27
28
|
ResetPassword: () => ResetPassword,
|
|
28
29
|
SignIn: () => SignIn,
|
|
29
30
|
SignUp: () => SignUp,
|
|
30
31
|
VerifyEmail: () => VerifyEmail,
|
|
31
32
|
can: () => can,
|
|
33
|
+
canAutofillPasskeys: () => canAutofillPasskeys,
|
|
32
34
|
canUsePasskeys: () => canUsePasskeys,
|
|
33
35
|
createAuthClient: () => createAuthClient,
|
|
34
36
|
definePolicy: () => definePolicy,
|
|
@@ -42,6 +44,15 @@ var import_react3 = require("react");
|
|
|
42
44
|
function canUsePasskeys() {
|
|
43
45
|
return typeof window !== "undefined" && typeof window.PublicKeyCredential === "function" && typeof navigator !== "undefined" && typeof navigator.credentials?.get === "function";
|
|
44
46
|
}
|
|
47
|
+
async function canAutofillPasskeys() {
|
|
48
|
+
if (!canUsePasskeys()) return false;
|
|
49
|
+
const pkc = window.PublicKeyCredential;
|
|
50
|
+
try {
|
|
51
|
+
return typeof pkc.isConditionalMediationAvailable === "function" && await pkc.isConditionalMediationAvailable() === true;
|
|
52
|
+
} catch {
|
|
53
|
+
return false;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
45
56
|
function bytes(text2) {
|
|
46
57
|
const b64 = text2.replace(/-/g, "+").replace(/_/g, "/").padEnd(Math.ceil(text2.length / 4) * 4, "=");
|
|
47
58
|
const bin = atob(b64);
|
|
@@ -57,9 +68,9 @@ function text(buf) {
|
|
|
57
68
|
return btoa(bin).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
|
|
58
69
|
}
|
|
59
70
|
var withId = (list) => (list ?? []).map((c) => ({ ...c, id: bytes(c.id) }));
|
|
60
|
-
async function getPasskey(options) {
|
|
71
|
+
async function getPasskey(options, how = {}) {
|
|
61
72
|
const publicKey = { ...options, challenge: bytes(options.challenge), allowCredentials: withId(options.allowCredentials) };
|
|
62
|
-
const cred = await navigator.credentials.get({ publicKey });
|
|
73
|
+
const cred = await navigator.credentials.get({ publicKey, ...how });
|
|
63
74
|
if (cred === null) throw new DOMException("No passkey was chosen.", "NotAllowedError");
|
|
64
75
|
const r = cred.response;
|
|
65
76
|
return {
|
|
@@ -203,6 +214,24 @@ function firstProblem(...checks) {
|
|
|
203
214
|
}
|
|
204
215
|
|
|
205
216
|
// src/client.ts
|
|
217
|
+
var googleScript = null;
|
|
218
|
+
function loadGoogleIdentity() {
|
|
219
|
+
const ready = () => window.google?.accounts?.id;
|
|
220
|
+
if (ready()) return Promise.resolve(ready());
|
|
221
|
+
googleScript ??= new Promise((resolve, reject) => {
|
|
222
|
+
const script = document.createElement("script");
|
|
223
|
+
script.src = "https://accounts.google.com/gsi/client";
|
|
224
|
+
script.async = true;
|
|
225
|
+
script.onload = () => ready() ? resolve(ready()) : reject(new Error("google identity services did not load"));
|
|
226
|
+
script.onerror = () => {
|
|
227
|
+
googleScript = null;
|
|
228
|
+
reject(new Error("google identity services did not load"));
|
|
229
|
+
};
|
|
230
|
+
document.head.appendChild(script);
|
|
231
|
+
});
|
|
232
|
+
return googleScript;
|
|
233
|
+
}
|
|
234
|
+
var PASSKEY_SECONDS = 300;
|
|
206
235
|
var UNAVAILABLE = { code: "unavailable", message: "Sign-in is unavailable right now. Try again in a moment." };
|
|
207
236
|
function createAuthClient(options = {}) {
|
|
208
237
|
const base = (options.basePath ?? "/api/auth").replace(/\/+$/, "");
|
|
@@ -211,13 +240,13 @@ function createAuthClient(options = {}) {
|
|
|
211
240
|
const said = typeof options.locale === "function" ? options.locale() : options.locale;
|
|
212
241
|
return (said ?? (typeof document === "undefined" ? "" : document.documentElement?.lang) ?? "").trim();
|
|
213
242
|
};
|
|
214
|
-
async function post(path, form, bad) {
|
|
243
|
+
async function post(path, form, bad, captchaToken) {
|
|
215
244
|
if (bad) return { ok: false, error: { code: bad.code, message: bad.message, field: bad.field } };
|
|
216
245
|
try {
|
|
217
246
|
const res = await doFetch(`${base}${path}`, {
|
|
218
247
|
method: "POST",
|
|
219
248
|
credentials: "same-origin",
|
|
220
|
-
headers: { "content-type": "application/json", accept: "application/json", ...locale() ? { "x-cauthy-locale": locale() } : {} },
|
|
249
|
+
headers: { "content-type": "application/json", accept: "application/json", ...locale() ? { "x-cauthy-locale": locale() } : {}, ...captchaToken ? { "x-cauthy-captcha": captchaToken } : {} },
|
|
221
250
|
body: JSON.stringify(form)
|
|
222
251
|
});
|
|
223
252
|
const body = await res.json().catch(() => null);
|
|
@@ -269,9 +298,11 @@ function createAuthClient(options = {}) {
|
|
|
269
298
|
}
|
|
270
299
|
return {
|
|
271
300
|
signIn: {
|
|
272
|
-
email: (f) => post("/sign-in/email", { email: f.email, password: f.password }, firstProblem(checkEmail(f.email), checkPassword(f.password))),
|
|
301
|
+
email: (f) => post("/sign-in/email", { email: f.email, password: f.password }, firstProblem(checkEmail(f.email), checkPassword(f.password)), f.captchaToken),
|
|
273
302
|
mfa: (f) => post("/sign-in/mfa", { code: f.code }, checkCode(f.code)),
|
|
274
|
-
|
|
303
|
+
mfaMethods: () => post("/sign-in/mfa/methods", {}, null),
|
|
304
|
+
mfaSendCode: () => post("/sign-in/mfa/send", {}, null),
|
|
305
|
+
magicLink: (f) => post("/sign-in/magic-link", { email: f.email }, checkEmail(f.email), f.captchaToken),
|
|
275
306
|
magicLinkVerify: (f) => post("/sign-in/magic-link/verify", { token: f.token }, null),
|
|
276
307
|
emailCode: (f) => post("/sign-in/email-code", { code: f.code }, checkCode(f.code)),
|
|
277
308
|
passkeyUrl,
|
|
@@ -286,15 +317,75 @@ function createAuthClient(options = {}) {
|
|
|
286
317
|
window.location.assign(passkeyUrl(o));
|
|
287
318
|
return { ok: true, status: "redirected" };
|
|
288
319
|
},
|
|
320
|
+
async passkeyAutofill(o = {}) {
|
|
321
|
+
const cancelled = { ok: false, error: { code: "cancelled", message: "No passkey was chosen." } };
|
|
322
|
+
const m = await methods();
|
|
323
|
+
if (!m.passkeyHere || !await canAutofillPasskeys()) return cancelled;
|
|
324
|
+
for (; ; ) {
|
|
325
|
+
if (o.signal?.aborted) return cancelled;
|
|
326
|
+
const begun = await post("/sign-in/passkey/begin", {}, null);
|
|
327
|
+
if (!begun.ok) return begun;
|
|
328
|
+
const renew = new AbortController();
|
|
329
|
+
const stop = () => renew.abort();
|
|
330
|
+
o.signal?.addEventListener("abort", stop, { once: true });
|
|
331
|
+
const timer = setTimeout(stop, (PASSKEY_SECONDS - 30) * 1e3);
|
|
332
|
+
let credential;
|
|
333
|
+
try {
|
|
334
|
+
credential = await getPasskey(begun.options, { mediation: "conditional", signal: renew.signal });
|
|
335
|
+
} catch (e) {
|
|
336
|
+
if (o.signal?.aborted) return cancelled;
|
|
337
|
+
if (renew.signal.aborted) continue;
|
|
338
|
+
return { ok: false, error: ceremonyError(e) };
|
|
339
|
+
} finally {
|
|
340
|
+
clearTimeout(timer);
|
|
341
|
+
o.signal?.removeEventListener("abort", stop);
|
|
342
|
+
}
|
|
343
|
+
return post("/sign-in/passkey/finish", { credential }, null);
|
|
344
|
+
}
|
|
345
|
+
},
|
|
346
|
+
async googleOneTap(o = {}) {
|
|
347
|
+
const cancelled = { ok: false, error: { code: "cancelled", message: "No Google account was chosen." } };
|
|
348
|
+
if (typeof window === "undefined" || o.signal?.aborted) return cancelled;
|
|
349
|
+
const begun = await post("/sign-in/google-one-tap/begin", {}, null);
|
|
350
|
+
if (!begun.ok) return begun;
|
|
351
|
+
let gis;
|
|
352
|
+
try {
|
|
353
|
+
gis = await loadGoogleIdentity();
|
|
354
|
+
} catch {
|
|
355
|
+
return { ok: false, error: { code: "unavailable", message: "Google could not be reached. Try again, or sign in another way." } };
|
|
356
|
+
}
|
|
357
|
+
const credential = await new Promise((done) => {
|
|
358
|
+
o.signal?.addEventListener("abort", () => {
|
|
359
|
+
gis.cancel();
|
|
360
|
+
done(null);
|
|
361
|
+
}, { once: true });
|
|
362
|
+
gis.initialize({
|
|
363
|
+
client_id: begun.clientId,
|
|
364
|
+
nonce: begun.nonce,
|
|
365
|
+
context: o.context ?? "signin",
|
|
366
|
+
callback: (r) => done(r.credential ?? null),
|
|
367
|
+
auto_select: false,
|
|
368
|
+
cancel_on_tap_outside: true,
|
|
369
|
+
itp_support: true,
|
|
370
|
+
use_fedcm_for_prompt: true
|
|
371
|
+
});
|
|
372
|
+
gis.prompt((moment) => {
|
|
373
|
+
if (moment.isNotDisplayed?.() || moment.isSkippedMoment?.()) done(null);
|
|
374
|
+
else if (moment.isDismissedMoment?.() && moment.getDismissedReason?.() !== "credential_returned") done(null);
|
|
375
|
+
});
|
|
376
|
+
});
|
|
377
|
+
if (credential === null) return cancelled;
|
|
378
|
+
return post("/sign-in/google-one-tap", { credential }, null);
|
|
379
|
+
},
|
|
289
380
|
socialUrl,
|
|
290
381
|
social(provider, o) {
|
|
291
382
|
window.location.assign(socialUrl(provider, o));
|
|
292
383
|
}
|
|
293
384
|
},
|
|
294
385
|
signUp: {
|
|
295
|
-
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)))
|
|
386
|
+
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)
|
|
296
387
|
},
|
|
297
|
-
forgotPassword: (f) => post("/forgot-password", { email: f.email }, checkEmail(f.email)),
|
|
388
|
+
forgotPassword: (f) => post("/forgot-password", { email: f.email }, checkEmail(f.email), f.captchaToken),
|
|
298
389
|
resetPassword: (f) => post("/reset-password", { token: f.token, password: f.password }, checkNewPassword(f.password)),
|
|
299
390
|
verifyEmail: (f) => post("/verify-email", { token: f.token }, null),
|
|
300
391
|
signOut: () => post("/sign-out", {}, null),
|
|
@@ -312,7 +403,14 @@ function createAuthClient(options = {}) {
|
|
|
312
403
|
setup: (f) => post("/me/mfa/setup", { password: f.password }, null),
|
|
313
404
|
verify: (f) => post("/me/mfa/verify", { code: f.code }, checkCode(f.code)),
|
|
314
405
|
disable: (f) => post("/me/mfa/disable", { password: f.password }, null),
|
|
315
|
-
recoveryCodes: (f) => post("/me/mfa/recovery-codes", { password: f.password }, null)
|
|
406
|
+
recoveryCodes: (f) => post("/me/mfa/recovery-codes", { password: f.password }, null),
|
|
407
|
+
async factors() {
|
|
408
|
+
const body = await read("/me/mfa/factors");
|
|
409
|
+
return body && typeof body.totp === "boolean" ? body : null;
|
|
410
|
+
},
|
|
411
|
+
addPhone: (f) => post("/me/mfa/phone", { phone: f.phone, channel: f.channel, password: f.password }, null),
|
|
412
|
+
verifyPhone: (f) => post("/me/mfa/phone/verify", { code: f.code }, checkCode(f.code)),
|
|
413
|
+
removePhone: (f) => post("/me/mfa/phone/remove", { password: f.password }, null)
|
|
316
414
|
},
|
|
317
415
|
changeEmail: (f) => post("/me/email", { email: f.email, password: f.password }, checkEmail(f.email)),
|
|
318
416
|
async identities() {
|
|
@@ -430,6 +528,18 @@ var TEXTS = {
|
|
|
430
528
|
linkSent: "If that address has an account, a link to choose a new password is on its way.",
|
|
431
529
|
codeHelp: "Enter the code from your authenticator app, or a recovery code.",
|
|
432
530
|
verify: "Verify",
|
|
531
|
+
codeHelpPhone: "We send a code to your phone. Ask for it, then type it here, or use a recovery code.",
|
|
532
|
+
sendCodeSms: "Send a code by SMS to {to}",
|
|
533
|
+
sendCodeWhatsapp: "Send a code by WhatsApp to {to}",
|
|
534
|
+
codeSent: "We sent a code to {to}. Type it here.",
|
|
535
|
+
phone: "Phone, with its country code",
|
|
536
|
+
phoneHelp: "Get your codes by SMS or WhatsApp instead of an app.",
|
|
537
|
+
phoneOn: "Codes go to {to}.",
|
|
538
|
+
phoneOff: "No phone yet.",
|
|
539
|
+
addPhone: "Add phone",
|
|
540
|
+
removePhone: "Remove phone",
|
|
541
|
+
bySms: "SMS",
|
|
542
|
+
byWhatsapp: "WhatsApp",
|
|
433
543
|
checkEmail: "Almost there: open the link we sent to confirm your address, then sign in.",
|
|
434
544
|
backToSignIn: "Back to sign in",
|
|
435
545
|
emailMeALink: "Email me a sign-in link",
|
|
@@ -497,6 +607,13 @@ var TEXTS = {
|
|
|
497
607
|
step_up_required: "Enter your current password.",
|
|
498
608
|
weak_password: "Choose a stronger password.",
|
|
499
609
|
unauthenticated: "Sign in first.",
|
|
610
|
+
phone_refused: "The code could not be sent to that number.",
|
|
611
|
+
too_many_codes: "Enough codes were sent. Use the last one, or sign in again.",
|
|
612
|
+
phone_unavailable: "Codes cannot be sent that way here.",
|
|
613
|
+
phone_already_set: "Remove the phone you have first.",
|
|
614
|
+
phone_codes_unavailable: "Codes cannot be sent right now. Try again in a moment.",
|
|
615
|
+
captcha_required: "Complete the check that you are not a bot, then try again.",
|
|
616
|
+
captcha_failed: "The check that you are not a bot did not pass. Try it again.",
|
|
500
617
|
passkey_failed: "The passkey could not be used. Try again, or sign in another way.",
|
|
501
618
|
passkey_exists: "This device already has a passkey for your account.",
|
|
502
619
|
passkey_unavailable: "Passkeys cannot be used here."
|
|
@@ -519,6 +636,18 @@ var TEXTS = {
|
|
|
519
636
|
linkSent: "Si ese correo tiene una cuenta, ya va en camino un enlace para elegir una contrase\xF1a nueva.",
|
|
520
637
|
codeHelp: "Escribe el c\xF3digo de tu aplicaci\xF3n de autenticaci\xF3n, o un c\xF3digo de recuperaci\xF3n.",
|
|
521
638
|
verify: "Verificar",
|
|
639
|
+
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.",
|
|
640
|
+
sendCodeSms: "Enviar un c\xF3digo por SMS a {to}",
|
|
641
|
+
sendCodeWhatsapp: "Enviar un c\xF3digo por WhatsApp a {to}",
|
|
642
|
+
codeSent: "Te enviamos un c\xF3digo a {to}. Escr\xEDbelo aqu\xED.",
|
|
643
|
+
phone: "Tel\xE9fono, con su c\xF3digo de pa\xEDs",
|
|
644
|
+
phoneHelp: "Recibe tus c\xF3digos por SMS o WhatsApp en vez de una app.",
|
|
645
|
+
phoneOn: "Los c\xF3digos van a {to}.",
|
|
646
|
+
phoneOff: "Todav\xEDa no tienes un tel\xE9fono.",
|
|
647
|
+
addPhone: "Agregar tel\xE9fono",
|
|
648
|
+
removePhone: "Quitar tel\xE9fono",
|
|
649
|
+
bySms: "SMS",
|
|
650
|
+
byWhatsapp: "WhatsApp",
|
|
522
651
|
checkEmail: "Ya casi: abre el enlace que te enviamos para confirmar tu correo y luego entra.",
|
|
523
652
|
backToSignIn: "Volver a entrar",
|
|
524
653
|
emailMeALink: "Env\xEDame un enlace para entrar",
|
|
@@ -586,6 +715,13 @@ var TEXTS = {
|
|
|
586
715
|
step_up_required: "Escribe tu contrase\xF1a actual.",
|
|
587
716
|
weak_password: "Elige una contrase\xF1a m\xE1s fuerte.",
|
|
588
717
|
unauthenticated: "Inicia sesi\xF3n primero.",
|
|
718
|
+
phone_refused: "No se pudo enviar el c\xF3digo a ese n\xFAmero.",
|
|
719
|
+
too_many_codes: "Ya se enviaron suficientes c\xF3digos. Usa el \xFAltimo o vuelve a entrar.",
|
|
720
|
+
phone_unavailable: "Aqu\xED no se pueden enviar c\xF3digos de esa forma.",
|
|
721
|
+
phone_already_set: "Primero quita el tel\xE9fono que tienes.",
|
|
722
|
+
phone_codes_unavailable: "Ahora no se pueden enviar c\xF3digos. Int\xE9ntalo en un momento.",
|
|
723
|
+
captcha_required: "Completa la verificaci\xF3n de que no eres un robot y vuelve a intentarlo.",
|
|
724
|
+
captcha_failed: "La verificaci\xF3n de que no eres un robot no pas\xF3. Int\xE9ntalo de nuevo.",
|
|
589
725
|
passkey_failed: "No se pudo usar la llave de acceso. Intenta de nuevo o entra de otra forma.",
|
|
590
726
|
passkey_exists: "Este dispositivo ya tiene una llave de acceso de tu cuenta.",
|
|
591
727
|
passkey_unavailable: "Aqu\xED no se pueden usar llaves de acceso."
|
|
@@ -630,6 +766,63 @@ function useRoot(props) {
|
|
|
630
766
|
if (props.unstyled) return {};
|
|
631
767
|
return { "data-cauthy": "", style: vars };
|
|
632
768
|
}
|
|
769
|
+
var turnstileScript = null;
|
|
770
|
+
function loadTurnstile() {
|
|
771
|
+
const ready = () => window.turnstile;
|
|
772
|
+
if (ready()) return Promise.resolve(ready());
|
|
773
|
+
turnstileScript ??= new Promise((resolve, reject) => {
|
|
774
|
+
const script = document.createElement("script");
|
|
775
|
+
script.src = "https://challenges.cloudflare.com/turnstile/v0/api.js?render=explicit";
|
|
776
|
+
script.async = true;
|
|
777
|
+
script.onload = () => ready() ? resolve(ready()) : reject(new Error("turnstile did not load"));
|
|
778
|
+
script.onerror = () => {
|
|
779
|
+
turnstileScript = null;
|
|
780
|
+
reject(new Error("turnstile did not load"));
|
|
781
|
+
};
|
|
782
|
+
document.head.appendChild(script);
|
|
783
|
+
});
|
|
784
|
+
return turnstileScript;
|
|
785
|
+
}
|
|
786
|
+
function useChallenge(siteKey, locale) {
|
|
787
|
+
const box = (0, import_react.useRef)(null);
|
|
788
|
+
const widget = (0, import_react.useRef)(null);
|
|
789
|
+
const [token, setToken] = (0, import_react.useState)("");
|
|
790
|
+
(0, import_react.useEffect)(() => {
|
|
791
|
+
if (!siteKey || !box.current) return;
|
|
792
|
+
let gone = false;
|
|
793
|
+
void loadTurnstile().then((api) => {
|
|
794
|
+
if (gone || !box.current) return;
|
|
795
|
+
const id = api.render(box.current, {
|
|
796
|
+
sitekey: siteKey,
|
|
797
|
+
language: locale ?? "auto",
|
|
798
|
+
size: "flexible",
|
|
799
|
+
callback: (t) => setToken(t),
|
|
800
|
+
"expired-callback": () => setToken(""),
|
|
801
|
+
"error-callback": () => setToken("")
|
|
802
|
+
});
|
|
803
|
+
widget.current = { api, id };
|
|
804
|
+
}).catch(() => {
|
|
805
|
+
});
|
|
806
|
+
return () => {
|
|
807
|
+
gone = true;
|
|
808
|
+
if (widget.current) widget.current.api.remove(widget.current.id);
|
|
809
|
+
widget.current = null;
|
|
810
|
+
};
|
|
811
|
+
}, [siteKey, locale]);
|
|
812
|
+
const renew = () => {
|
|
813
|
+
setToken("");
|
|
814
|
+
if (widget.current) widget.current.api.reset(widget.current.id);
|
|
815
|
+
};
|
|
816
|
+
const node = siteKey ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { ref: box, className: "cauthy-challenge" }) : null;
|
|
817
|
+
return { node, token: token || void 0, renew };
|
|
818
|
+
}
|
|
819
|
+
function useMethods(auth) {
|
|
820
|
+
const [methods, setMethods] = (0, import_react.useState)(null);
|
|
821
|
+
(0, import_react.useEffect)(() => {
|
|
822
|
+
void auth.methods().then(setMethods);
|
|
823
|
+
}, []);
|
|
824
|
+
return methods;
|
|
825
|
+
}
|
|
633
826
|
function useForm(props) {
|
|
634
827
|
const t = { ...TEXTS[props.locale ?? "en"], ...props.texts, errors: { ...TEXTS[props.locale ?? "en"].errors, ...props.texts?.errors } };
|
|
635
828
|
const cx = (slot) => props.classNames?.[slot] ?? `cauthy-${slot}`;
|
|
@@ -702,6 +895,25 @@ function SignIn(props) {
|
|
|
702
895
|
const [password, setPassword] = (0, import_react.useState)("");
|
|
703
896
|
const [code, setCode] = (0, import_react.useState)("");
|
|
704
897
|
const [step, setStep] = (0, import_react.useState)("password");
|
|
898
|
+
const challenge = useChallenge(methods?.captchaSiteKey, props.locale);
|
|
899
|
+
const [phoneWay, setPhoneWay] = (0, import_react.useState)(null);
|
|
900
|
+
const [phoneSent, setPhoneSent] = (0, import_react.useState)("");
|
|
901
|
+
(0, import_react.useEffect)(() => {
|
|
902
|
+
if (step !== "code") return;
|
|
903
|
+
void auth.signIn.mfaMethods().then((r) => {
|
|
904
|
+
if (r.ok) setPhoneWay({ totp: r.totp, phone: r.phone });
|
|
905
|
+
});
|
|
906
|
+
}, [step]);
|
|
907
|
+
const sendCode = async () => {
|
|
908
|
+
f.setBusy(true);
|
|
909
|
+
f.setError(null);
|
|
910
|
+
const r = await auth.signIn.mfaSendCode();
|
|
911
|
+
f.setBusy(false);
|
|
912
|
+
if (!r.ok) return f.setError(r.error);
|
|
913
|
+
setPhoneSent(r.sentTo);
|
|
914
|
+
};
|
|
915
|
+
const autofill = (0, import_react.useRef)(null);
|
|
916
|
+
const [offer, setOffer] = (0, import_react.useState)(0);
|
|
705
917
|
(0, import_react.useEffect)(() => {
|
|
706
918
|
void auth.methods().then(setMethods);
|
|
707
919
|
const back = returnedError();
|
|
@@ -716,30 +928,52 @@ function SignIn(props) {
|
|
|
716
928
|
setStep("password");
|
|
717
929
|
});
|
|
718
930
|
});
|
|
931
|
+
(0, import_react.useEffect)(() => {
|
|
932
|
+
if (step !== "password" || !methods?.passkeyHere) return;
|
|
933
|
+
const ctl = new AbortController();
|
|
934
|
+
autofill.current = ctl;
|
|
935
|
+
void auth.signIn.passkeyAutofill({ signal: ctl.signal }).then((r) => {
|
|
936
|
+
if (ctl.signal.aborted) return;
|
|
937
|
+
if (r.ok) return f.done();
|
|
938
|
+
if (r.error.code === "cancelled") return;
|
|
939
|
+
f.setError(r.error);
|
|
940
|
+
if (r.error.code === "passkey_failed" || r.error.code === "step_expired") setOffer((n) => n + 1);
|
|
941
|
+
});
|
|
942
|
+
return () => ctl.abort();
|
|
943
|
+
}, [step, methods, offer]);
|
|
719
944
|
const submit = async (e) => {
|
|
720
945
|
e.preventDefault();
|
|
946
|
+
autofill.current?.abort();
|
|
721
947
|
f.setBusy(true);
|
|
722
948
|
f.setError(null);
|
|
723
|
-
const r = step === "code" ? await auth.signIn.mfa({ code }) : step === "sent" ? await auth.signIn.emailCode({ code }) : await auth.signIn.email({ email, password });
|
|
949
|
+
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 });
|
|
724
950
|
f.setBusy(false);
|
|
725
|
-
if (
|
|
951
|
+
if (step === "password") challenge.renew();
|
|
952
|
+
if (!r.ok) {
|
|
953
|
+
setOffer((n) => n + 1);
|
|
954
|
+
return f.setError(r.error);
|
|
955
|
+
}
|
|
726
956
|
if (r.status === "mfa_required") return setStep("code");
|
|
727
957
|
f.done();
|
|
728
958
|
};
|
|
729
959
|
const usePasskey = async () => {
|
|
960
|
+
autofill.current?.abort();
|
|
730
961
|
f.setBusy(true);
|
|
731
962
|
f.setError(null);
|
|
732
963
|
const r = await auth.signIn.passkey({ returnTo: props.returnTo, locale: props.locale });
|
|
733
964
|
if (r.ok && r.status === "redirected") return;
|
|
734
965
|
f.setBusy(false);
|
|
966
|
+
if (!r.ok) setOffer((n) => n + 1);
|
|
735
967
|
if (!r.ok) return r.error.code === "cancelled" ? void 0 : f.setError(r.error);
|
|
736
968
|
f.done();
|
|
737
969
|
};
|
|
738
970
|
const sendLink = async () => {
|
|
971
|
+
autofill.current?.abort();
|
|
739
972
|
f.setBusy(true);
|
|
740
973
|
f.setError(null);
|
|
741
|
-
const r = await auth.signIn.magicLink({ email });
|
|
974
|
+
const r = await auth.signIn.magicLink({ email, captchaToken: challenge.token });
|
|
742
975
|
f.setBusy(false);
|
|
976
|
+
challenge.renew();
|
|
743
977
|
if (!r.ok) return f.setError(r.error);
|
|
744
978
|
setStep("sent");
|
|
745
979
|
};
|
|
@@ -755,9 +989,11 @@ function SignIn(props) {
|
|
|
755
989
|
] });
|
|
756
990
|
}
|
|
757
991
|
if (step === "code") {
|
|
992
|
+
const to = phoneWay?.phone?.number ?? "";
|
|
758
993
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("form", { className: f.cx("form"), ...f.root, onSubmit: submit, noValidate: true, children: [
|
|
759
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: f.cx("notice"), children: f.t.codeHelp }),
|
|
994
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: f.cx("notice"), children: phoneSent ? f.t.codeSent.replace("{to}", phoneSent) : phoneWay?.phone && !phoneWay.totp ? f.t.codeHelpPhone : f.t.codeHelp }),
|
|
760
995
|
general && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { role: "alert", className: f.cx("error"), children: f.say(general) }),
|
|
996
|
+
phoneWay?.phone && /* @__PURE__ */ (0, import_jsx_runtime.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) }),
|
|
761
997
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Field, { f, name: "code", type: "text", label: f.t.code, value: code, autoComplete: "one-time-code", required: true, onChange: setCode }),
|
|
762
998
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(f.Button, { type: "submit", disabled: f.busy, className: f.cx("button"), children: f.t.verify })
|
|
763
999
|
] });
|
|
@@ -771,7 +1007,7 @@ function SignIn(props) {
|
|
|
771
1007
|
] }, p.id)),
|
|
772
1008
|
methods?.passkey && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(f.Button, { type: "button", disabled: f.busy, className: f.cx("providerButton"), onClick: () => void usePasskey(), children: f.t.usePasskey }),
|
|
773
1009
|
((methods?.providers.length ?? 0) > 0 || methods?.passkey) && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: f.cx("separator"), children: f.t.orWithEmail }),
|
|
774
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Field, { f, name: "email", type: "email", label: f.t.email, value: email, autoComplete: "username", required: true, onChange: setEmail }),
|
|
1010
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Field, { f, name: "email", type: "email", label: f.t.email, value: email, autoComplete: methods?.passkeyHere ? "username webauthn" : "username", required: true, onChange: setEmail }),
|
|
775
1011
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
776
1012
|
Field,
|
|
777
1013
|
{
|
|
@@ -786,6 +1022,7 @@ function SignIn(props) {
|
|
|
786
1022
|
aside: props.links?.forgotPassword ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("a", { className: f.cx("link"), href: props.links.forgotPassword, children: f.t.forgotPassword }) : void 0
|
|
787
1023
|
}
|
|
788
1024
|
),
|
|
1025
|
+
challenge.node,
|
|
789
1026
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(f.Button, { type: "submit", disabled: f.busy, className: f.cx("button"), children: f.t.signIn }),
|
|
790
1027
|
methods?.magicLink && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(f.Button, { type: "button", disabled: f.busy, className: f.cx("providerButton"), onClick: () => void sendLink(), children: f.t.emailMeALink }),
|
|
791
1028
|
methods?.signUp !== false && props.links?.signUp && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("p", { className: f.cx("footer"), children: [
|
|
@@ -795,6 +1032,21 @@ function SignIn(props) {
|
|
|
795
1032
|
] })
|
|
796
1033
|
] });
|
|
797
1034
|
}
|
|
1035
|
+
function GoogleOneTap(props) {
|
|
1036
|
+
const auth = useAuth();
|
|
1037
|
+
const unauthenticated = auth.status === "unauthenticated";
|
|
1038
|
+
(0, import_react.useEffect)(() => {
|
|
1039
|
+
if (!unauthenticated) return;
|
|
1040
|
+
const ctl = new AbortController();
|
|
1041
|
+
void auth.signIn.googleOneTap({ signal: ctl.signal, context: props.context }).then((r) => {
|
|
1042
|
+
if (!r.ok || ctl.signal.aborted) return;
|
|
1043
|
+
if (props.onSuccess) props.onSuccess();
|
|
1044
|
+
else if (props.returnTo) window.location.assign(props.returnTo);
|
|
1045
|
+
});
|
|
1046
|
+
return () => ctl.abort();
|
|
1047
|
+
}, [unauthenticated]);
|
|
1048
|
+
return null;
|
|
1049
|
+
}
|
|
798
1050
|
function SignUp(props) {
|
|
799
1051
|
const auth = useAuth();
|
|
800
1052
|
const f = useForm(props);
|
|
@@ -802,12 +1054,14 @@ function SignUp(props) {
|
|
|
802
1054
|
const [email, setEmail] = (0, import_react.useState)("");
|
|
803
1055
|
const [password, setPassword] = (0, import_react.useState)("");
|
|
804
1056
|
const [sent, setSent] = (0, import_react.useState)(false);
|
|
1057
|
+
const challenge = useChallenge(useMethods(auth)?.captchaSiteKey, props.locale);
|
|
805
1058
|
const submit = async (e) => {
|
|
806
1059
|
e.preventDefault();
|
|
807
1060
|
f.setBusy(true);
|
|
808
1061
|
f.setError(null);
|
|
809
|
-
const r = await auth.signUp.email({ name, email, password });
|
|
1062
|
+
const r = await auth.signUp.email({ name, email, password, captchaToken: challenge.token });
|
|
810
1063
|
f.setBusy(false);
|
|
1064
|
+
challenge.renew();
|
|
811
1065
|
if (!r.ok) return f.setError(r.error);
|
|
812
1066
|
if (r.status === "verification_required") return setSent(true);
|
|
813
1067
|
f.done();
|
|
@@ -824,6 +1078,7 @@ function SignUp(props) {
|
|
|
824
1078
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Field, { f, name: "name", type: "text", label: f.t.name, value: name, autoComplete: "name", onChange: setName }),
|
|
825
1079
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Field, { f, name: "email", type: "email", label: f.t.email, value: email, autoComplete: "username", required: true, onChange: setEmail }),
|
|
826
1080
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Field, { f, name: "password", type: "password", label: f.t.password, value: password, autoComplete: "new-password", required: true, onChange: setPassword }),
|
|
1081
|
+
challenge.node,
|
|
827
1082
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(f.Button, { type: "submit", disabled: f.busy, className: f.cx("button"), children: f.t.signUp }),
|
|
828
1083
|
props.links?.signIn && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("p", { className: f.cx("footer"), children: [
|
|
829
1084
|
f.t.haveAccount,
|
|
@@ -837,12 +1092,14 @@ function ForgotPassword(props) {
|
|
|
837
1092
|
const f = useForm(props);
|
|
838
1093
|
const [email, setEmail] = (0, import_react.useState)("");
|
|
839
1094
|
const [sent, setSent] = (0, import_react.useState)(false);
|
|
1095
|
+
const challenge = useChallenge(useMethods(auth)?.captchaSiteKey, props.locale);
|
|
840
1096
|
const submit = async (e) => {
|
|
841
1097
|
e.preventDefault();
|
|
842
1098
|
f.setBusy(true);
|
|
843
1099
|
f.setError(null);
|
|
844
|
-
const r = await auth.forgotPassword({ email });
|
|
1100
|
+
const r = await auth.forgotPassword({ email, captchaToken: challenge.token });
|
|
845
1101
|
f.setBusy(false);
|
|
1102
|
+
challenge.renew();
|
|
846
1103
|
if (!r.ok) return f.setError(r.error);
|
|
847
1104
|
setSent(true);
|
|
848
1105
|
};
|
|
@@ -856,6 +1113,7 @@ function ForgotPassword(props) {
|
|
|
856
1113
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("form", { className: f.cx("form"), ...f.root, onSubmit: submit, noValidate: true, children: [
|
|
857
1114
|
general && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { role: "alert", className: f.cx("error"), children: f.say(general) }),
|
|
858
1115
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Field, { f, name: "email", type: "email", label: f.t.email, value: email, autoComplete: "username", required: true, onChange: setEmail }),
|
|
1116
|
+
challenge.node,
|
|
859
1117
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(f.Button, { type: "submit", disabled: f.busy, className: f.cx("button"), children: f.t.sendLink }),
|
|
860
1118
|
props.links?.signIn && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: f.cx("footer"), children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("a", { className: f.cx("link"), href: props.links.signIn, children: f.t.backToSignIn }) })
|
|
861
1119
|
] });
|
|
@@ -955,6 +1213,7 @@ function Account(props) {
|
|
|
955
1213
|
account.hasPassword && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(PasswordPart, { props }),
|
|
956
1214
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(LinkedPart, { props }),
|
|
957
1215
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(SecondStepPart, { props, account, onChanged: (mfaEnabled) => setAccount({ ...account, mfaEnabled }) }),
|
|
1216
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(PhonePart, { props, account, onChanged: (mfaEnabled) => setAccount({ ...account, mfaEnabled }) }),
|
|
958
1217
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(PasskeysPart, { props, account, accountUrl: accountUrl(), when }),
|
|
959
1218
|
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("section", { className: cx("section"), children: [
|
|
960
1219
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("h2", { className: cx("heading"), children: t.sessions }),
|
|
@@ -1184,6 +1443,77 @@ function SecondStepPart({ props, account, onChanged }) {
|
|
|
1184
1443
|
account.mfaEnabled && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(f.Button, { type: "button", disabled: f.busy, className: f.cx("providerButton"), onClick: () => void renew(), children: f.t.newRecoveryCodes })
|
|
1185
1444
|
] });
|
|
1186
1445
|
}
|
|
1446
|
+
function PhonePart({ props, account, onChanged }) {
|
|
1447
|
+
const auth = useAuth();
|
|
1448
|
+
const { f, run, general } = useSection(props);
|
|
1449
|
+
const [ways, setWays] = (0, import_react.useState)(null);
|
|
1450
|
+
const [number, setNumber] = (0, import_react.useState)("");
|
|
1451
|
+
const [channel, setChannel] = (0, import_react.useState)("sms");
|
|
1452
|
+
const [password, setPassword] = (0, import_react.useState)("");
|
|
1453
|
+
const [code, setCode] = (0, import_react.useState)("");
|
|
1454
|
+
const [sentTo, setSentTo] = (0, import_react.useState)("");
|
|
1455
|
+
const [codes, setCodes] = (0, import_react.useState)(null);
|
|
1456
|
+
const load = () => auth.account.mfa.factors().then((w) => {
|
|
1457
|
+
setWays(w);
|
|
1458
|
+
if (w && w.phoneChannels.length > 0 && !w.phoneChannels.includes(channel)) setChannel(w.phoneChannels[0]);
|
|
1459
|
+
});
|
|
1460
|
+
(0, import_react.useEffect)(() => {
|
|
1461
|
+
void load();
|
|
1462
|
+
}, []);
|
|
1463
|
+
if (!ways || ways.phoneChannels.length === 0) return null;
|
|
1464
|
+
const add = async (e) => {
|
|
1465
|
+
e.preventDefault();
|
|
1466
|
+
const r = await run(() => auth.account.mfa.addPhone({ phone: number, channel, password }));
|
|
1467
|
+
if (r.ok) setSentTo(r.sentTo);
|
|
1468
|
+
};
|
|
1469
|
+
const confirm = async (e) => {
|
|
1470
|
+
e.preventDefault();
|
|
1471
|
+
const r = await run(() => auth.account.mfa.verifyPhone({ code }));
|
|
1472
|
+
if (!r.ok) return;
|
|
1473
|
+
setSentTo("");
|
|
1474
|
+
setCode("");
|
|
1475
|
+
setPassword("");
|
|
1476
|
+
if (r.recoveryCodes.length > 0) setCodes(r.recoveryCodes);
|
|
1477
|
+
onChanged(true);
|
|
1478
|
+
await load();
|
|
1479
|
+
};
|
|
1480
|
+
const remove = async (e) => {
|
|
1481
|
+
e.preventDefault();
|
|
1482
|
+
const r = await run(() => auth.account.mfa.removePhone({ password }));
|
|
1483
|
+
if (!r.ok) return;
|
|
1484
|
+
setPassword("");
|
|
1485
|
+
await load();
|
|
1486
|
+
const left = await auth.account.mfa.factors();
|
|
1487
|
+
onChanged(Boolean(left?.totp));
|
|
1488
|
+
};
|
|
1489
|
+
const passwordField = account.hasPassword && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Field, { f, name: "password", type: "password", label: f.t.currentPassword, value: password, autoComplete: "current-password", required: true, onChange: setPassword });
|
|
1490
|
+
if (sentTo) {
|
|
1491
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("form", { className: f.cx("section"), onSubmit: confirm, noValidate: true, children: [
|
|
1492
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("h2", { className: f.cx("heading"), children: f.t.phone }),
|
|
1493
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: f.cx("notice"), children: f.t.codeSent.replace("{to}", sentTo) }),
|
|
1494
|
+
general && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { role: "alert", className: f.cx("error"), children: f.say(general) }),
|
|
1495
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Field, { f, name: "code", type: "text", label: f.t.code, value: code, autoComplete: "one-time-code", required: true, onChange: setCode }),
|
|
1496
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(f.Button, { type: "submit", disabled: f.busy, className: f.cx("button"), children: f.t.verify })
|
|
1497
|
+
] });
|
|
1498
|
+
}
|
|
1499
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("form", { className: f.cx("section"), onSubmit: ways.phone ? remove : add, noValidate: true, children: [
|
|
1500
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("h2", { className: f.cx("heading"), children: f.t.phone }),
|
|
1501
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: f.cx("notice"), children: ways.phone ? f.t.phoneOn.replace("{to}", ways.phone.number) : `${f.t.phoneOff} ${f.t.phoneHelp}` }),
|
|
1502
|
+
codes && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
|
|
1503
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: f.cx("notice"), children: f.t.keepCodes }),
|
|
1504
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("ul", { className: f.cx("codes"), children: codes.map((c) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("li", { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("code", { children: c }) }, c)) })
|
|
1505
|
+
] }),
|
|
1506
|
+
general && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { role: "alert", className: f.cx("error"), children: f.say(general) }),
|
|
1507
|
+
!ways.phone && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Field, { f, name: "phone", type: "tel", label: f.t.phone, value: number, autoComplete: "tel", required: true, onChange: setNumber }),
|
|
1508
|
+
!ways.phone && ways.phoneChannels.length > 1 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: f.cx("field"), role: "radiogroup", children: ways.phoneChannels.map((c) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", { className: f.cx("label"), children: [
|
|
1509
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("input", { type: "radio", name: "channel", value: c, checked: channel === c, onChange: () => setChannel(c) }),
|
|
1510
|
+
" ",
|
|
1511
|
+
c === "whatsapp" ? f.t.byWhatsapp : f.t.bySms
|
|
1512
|
+
] }, c)) }),
|
|
1513
|
+
passwordField,
|
|
1514
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(f.Button, { type: "submit", disabled: f.busy, className: f.cx("button"), children: ways.phone ? f.t.removePhone : f.t.addPhone })
|
|
1515
|
+
] });
|
|
1516
|
+
}
|
|
1187
1517
|
function SignOutEverywhere({ props }) {
|
|
1188
1518
|
const auth = useAuth();
|
|
1189
1519
|
const { f, run, general } = useSection(props);
|
|
@@ -1286,7 +1616,16 @@ function useAuth() {
|
|
|
1286
1616
|
};
|
|
1287
1617
|
return {
|
|
1288
1618
|
...client,
|
|
1289
|
-
signIn: {
|
|
1619
|
+
signIn: {
|
|
1620
|
+
...client.signIn,
|
|
1621
|
+
email: (f) => then(client.signIn.email(f)),
|
|
1622
|
+
mfa: (f) => then(client.signIn.mfa(f)),
|
|
1623
|
+
magicLinkVerify: (f) => then(client.signIn.magicLinkVerify(f)),
|
|
1624
|
+
emailCode: (f) => then(client.signIn.emailCode(f)),
|
|
1625
|
+
passkey: (o) => then(client.signIn.passkey(o)),
|
|
1626
|
+
passkeyAutofill: (o) => then(client.signIn.passkeyAutofill(o)),
|
|
1627
|
+
googleOneTap: (o) => then(client.signIn.googleOneTap(o))
|
|
1628
|
+
},
|
|
1290
1629
|
signUp: { email: (f) => then(client.signUp.email(f)) },
|
|
1291
1630
|
signOut: () => then(client.signOut()),
|
|
1292
1631
|
account: {
|
|
@@ -1307,11 +1646,13 @@ function useAuth() {
|
|
|
1307
1646
|
Account,
|
|
1308
1647
|
CauthyProvider,
|
|
1309
1648
|
ForgotPassword,
|
|
1649
|
+
GoogleOneTap,
|
|
1310
1650
|
ResetPassword,
|
|
1311
1651
|
SignIn,
|
|
1312
1652
|
SignUp,
|
|
1313
1653
|
VerifyEmail,
|
|
1314
1654
|
can,
|
|
1655
|
+
canAutofillPasskeys,
|
|
1315
1656
|
canUsePasskeys,
|
|
1316
1657
|
createAuthClient,
|
|
1317
1658
|
definePolicy,
|