cauthy-auth 0.12.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/dist/client.d.mts +60 -1
- package/dist/client.d.ts +60 -1
- package/dist/client.js +16 -7
- package/dist/client.js.map +1 -1
- package/dist/client.mjs +16 -7
- package/dist/client.mjs.map +1 -1
- package/dist/index.js +54 -1
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +54 -1
- package/dist/index.mjs.map +1 -1
- package/dist/react.d.mts +74 -1
- package/dist/react.d.ts +74 -1
- package/dist/react.js +216 -12
- package/dist/react.js.map +1 -1
- package/dist/react.mjs +216 -12
- package/dist/react.mjs.map +1 -1
- package/dist/styles.css +5 -0
- package/dist/svelte.js +16 -7
- package/dist/svelte.js.map +1 -1
- package/dist/svelte.mjs +16 -7
- package/dist/svelte.mjs.map +1 -1
- package/dist/vue.js +16 -7
- package/dist/vue.js.map +1 -1
- package/dist/vue.mjs +16 -7
- package/dist/vue.mjs.map +1 -1
- package/package.json +1 -1
package/dist/react.d.mts
CHANGED
|
@@ -258,6 +258,13 @@ interface AuthMethods {
|
|
|
258
258
|
* page that cannot, or a browser that refuses to here, sends the person.
|
|
259
259
|
*/
|
|
260
260
|
passkeyOnCauthy: boolean;
|
|
261
|
+
/**
|
|
262
|
+
* The workspace's Cloudflare Turnstile site key, when it asks for the
|
|
263
|
+
* challenge: signing in, creating an account, and asking for a sign-in link
|
|
264
|
+
* or a new password then take its answer (`captchaToken`). The ready-made
|
|
265
|
+
* forms show it themselves.
|
|
266
|
+
*/
|
|
267
|
+
captchaSiteKey?: string;
|
|
261
268
|
}
|
|
262
269
|
/** A passkey of the signed-in account. Moments are UTC. */
|
|
263
270
|
interface Passkey {
|
|
@@ -299,6 +306,17 @@ interface LinkedIdentity {
|
|
|
299
306
|
/** When it was linked, in UTC (ISO 8601). */
|
|
300
307
|
linkedAt: string;
|
|
301
308
|
}
|
|
309
|
+
/** How an account takes its second step. */
|
|
310
|
+
interface SecondStep {
|
|
311
|
+
totp: boolean;
|
|
312
|
+
/** The phone codes are sent to, masked (`+57 ••• ••• 4567`). */
|
|
313
|
+
phone: {
|
|
314
|
+
number: string;
|
|
315
|
+
channel: string;
|
|
316
|
+
} | null;
|
|
317
|
+
/** The ways the workspace sends codes to phones. */
|
|
318
|
+
phoneChannels: string[];
|
|
319
|
+
}
|
|
302
320
|
interface AccountClient {
|
|
303
321
|
/** The account, or null when nobody is signed in. */
|
|
304
322
|
get(): Promise<Account$1 | null>;
|
|
@@ -337,6 +355,30 @@ interface AccountClient {
|
|
|
337
355
|
}): Promise<AuthResult<'renewed', {
|
|
338
356
|
recoveryCodes: string[];
|
|
339
357
|
}>>;
|
|
358
|
+
/**
|
|
359
|
+
* How the account takes its second step: an authenticator app, a phone
|
|
360
|
+
* (masked), and the ways the workspace sends codes to phones (`sms`,
|
|
361
|
+
* `whatsapp`; none when it has no Twilio account for it).
|
|
362
|
+
*/
|
|
363
|
+
factors(): Promise<SecondStep | null>;
|
|
364
|
+
/** Adds a phone as the second step: a code is sent to it, for `verifyPhone`. */
|
|
365
|
+
addPhone(form: {
|
|
366
|
+
phone: string;
|
|
367
|
+
channel: 'sms' | 'whatsapp';
|
|
368
|
+
password: string;
|
|
369
|
+
}): Promise<AuthResult<'sent', {
|
|
370
|
+
sentTo: string;
|
|
371
|
+
channel: string;
|
|
372
|
+
}>>;
|
|
373
|
+
/** Proves the phone with the code it received. `recoveryCodes` come with a first second step, shown this once. */
|
|
374
|
+
verifyPhone(form: {
|
|
375
|
+
code: string;
|
|
376
|
+
}): Promise<AuthResult<'enabled', {
|
|
377
|
+
recoveryCodes: string[];
|
|
378
|
+
}>>;
|
|
379
|
+
removePhone(form: {
|
|
380
|
+
password: string;
|
|
381
|
+
}): Promise<AuthResult<'removed'>>;
|
|
340
382
|
};
|
|
341
383
|
/**
|
|
342
384
|
* Asks to sign in with another address. Nothing changes yet: the new address
|
|
@@ -393,6 +435,7 @@ interface AuthClient {
|
|
|
393
435
|
email(form: {
|
|
394
436
|
email: string;
|
|
395
437
|
password: string;
|
|
438
|
+
captchaToken?: string;
|
|
396
439
|
}): Promise<AuthResult<'signed_in', {
|
|
397
440
|
session: PublicSession;
|
|
398
441
|
}> | AuthResult<'mfa_required'>>;
|
|
@@ -402,9 +445,23 @@ interface AuthClient {
|
|
|
402
445
|
}): Promise<AuthResult<'signed_in', {
|
|
403
446
|
session: PublicSession;
|
|
404
447
|
}>>;
|
|
448
|
+
/** While the second step is asked for: whether the account has an authenticator app, and the phone (masked) a code can be sent to. */
|
|
449
|
+
mfaMethods(): Promise<AuthResult<'ok', {
|
|
450
|
+
totp: boolean;
|
|
451
|
+
phone: {
|
|
452
|
+
number: string;
|
|
453
|
+
channel: string;
|
|
454
|
+
} | null;
|
|
455
|
+
}>>;
|
|
456
|
+
/** Sends a code to the account's phone, by SMS or WhatsApp; `mfa` takes it. A few per sign-in. */
|
|
457
|
+
mfaSendCode(): Promise<AuthResult<'sent', {
|
|
458
|
+
sentTo: string;
|
|
459
|
+
channel: string;
|
|
460
|
+
}>>;
|
|
405
461
|
/** Mails a link that signs the person in. The answer is the same whether or not the address has an account. */
|
|
406
462
|
magicLink(form: {
|
|
407
463
|
email: string;
|
|
464
|
+
captchaToken?: string;
|
|
408
465
|
}): Promise<AuthResult<'sent'>>;
|
|
409
466
|
/** Signs in with the token the mailed link opened the page with (`ml_token`). */
|
|
410
467
|
magicLinkVerify(form: {
|
|
@@ -480,6 +537,7 @@ interface AuthClient {
|
|
|
480
537
|
email: string;
|
|
481
538
|
password: string;
|
|
482
539
|
name?: string;
|
|
540
|
+
captchaToken?: string;
|
|
483
541
|
}): Promise<AuthResult<'signed_in', {
|
|
484
542
|
session: PublicSession;
|
|
485
543
|
}> | AuthResult<'verification_required', {
|
|
@@ -489,6 +547,7 @@ interface AuthClient {
|
|
|
489
547
|
/** Mails a link to choose a new password. The answer is the same whether or not the address has an account. */
|
|
490
548
|
forgotPassword(form: {
|
|
491
549
|
email: string;
|
|
550
|
+
captchaToken?: string;
|
|
492
551
|
}): Promise<AuthResult<'sent'>>;
|
|
493
552
|
/** Sets the new password, with the token the mailed link opened the page with (`token`). */
|
|
494
553
|
resetPassword(form: {
|
|
@@ -542,6 +601,20 @@ interface AuthTexts {
|
|
|
542
601
|
sendLink: string;
|
|
543
602
|
linkSent: string;
|
|
544
603
|
codeHelp: string;
|
|
604
|
+
/** The second step when it is a phone only. */
|
|
605
|
+
codeHelpPhone: string;
|
|
606
|
+
/** "{to}" is the phone, masked. */
|
|
607
|
+
sendCodeSms: string;
|
|
608
|
+
sendCodeWhatsapp: string;
|
|
609
|
+
codeSent: string;
|
|
610
|
+
phone: string;
|
|
611
|
+
phoneHelp: string;
|
|
612
|
+
phoneOn: string;
|
|
613
|
+
phoneOff: string;
|
|
614
|
+
addPhone: string;
|
|
615
|
+
removePhone: string;
|
|
616
|
+
bySms: string;
|
|
617
|
+
byWhatsapp: string;
|
|
545
618
|
verify: string;
|
|
546
619
|
checkEmail: string;
|
|
547
620
|
backToSignIn: string;
|
|
@@ -734,4 +807,4 @@ declare function useSession(): SessionContext;
|
|
|
734
807
|
*/
|
|
735
808
|
declare function useAuth(): AuthClient & Pick<SessionContext, 'session' | 'status' | 'refresh'>;
|
|
736
809
|
|
|
737
|
-
export { Account, type AccountClient, type Account$1 as AccountDetails, type AccountProps, type AccountSession, type AuthClient, type AuthError, type AuthFormProps, type AuthMethods, type AuthResult, type AuthTexts, CauthyProvider, type CauthyProviderProps, ForgotPassword, GoogleOneTap, type GoogleOneTapProps, type JsonValue, type LinkedIdentity, type Passkey, type PermissionOf, type Policy, type PublicSession, ResetPassword, type RoleOf, type SessionContext, type SessionOrganization, type SessionUser, SignIn, SignUp, VerifyEmail, can, canAutofillPasskeys, canUsePasskeys, createAuthClient, definePolicy, useAuth, useSession };
|
|
810
|
+
export { Account, type AccountClient, type Account$1 as AccountDetails, type AccountProps, type AccountSession, type AuthClient, type AuthError, type AuthFormProps, type AuthMethods, type AuthResult, type AuthTexts, CauthyProvider, type CauthyProviderProps, ForgotPassword, GoogleOneTap, type GoogleOneTapProps, type JsonValue, type LinkedIdentity, type Passkey, type PermissionOf, type Policy, type PublicSession, ResetPassword, type RoleOf, type SecondStep, type SessionContext, type SessionOrganization, type SessionUser, SignIn, SignUp, VerifyEmail, can, canAutofillPasskeys, canUsePasskeys, createAuthClient, definePolicy, useAuth, useSession };
|
package/dist/react.d.ts
CHANGED
|
@@ -258,6 +258,13 @@ interface AuthMethods {
|
|
|
258
258
|
* page that cannot, or a browser that refuses to here, sends the person.
|
|
259
259
|
*/
|
|
260
260
|
passkeyOnCauthy: boolean;
|
|
261
|
+
/**
|
|
262
|
+
* The workspace's Cloudflare Turnstile site key, when it asks for the
|
|
263
|
+
* challenge: signing in, creating an account, and asking for a sign-in link
|
|
264
|
+
* or a new password then take its answer (`captchaToken`). The ready-made
|
|
265
|
+
* forms show it themselves.
|
|
266
|
+
*/
|
|
267
|
+
captchaSiteKey?: string;
|
|
261
268
|
}
|
|
262
269
|
/** A passkey of the signed-in account. Moments are UTC. */
|
|
263
270
|
interface Passkey {
|
|
@@ -299,6 +306,17 @@ interface LinkedIdentity {
|
|
|
299
306
|
/** When it was linked, in UTC (ISO 8601). */
|
|
300
307
|
linkedAt: string;
|
|
301
308
|
}
|
|
309
|
+
/** How an account takes its second step. */
|
|
310
|
+
interface SecondStep {
|
|
311
|
+
totp: boolean;
|
|
312
|
+
/** The phone codes are sent to, masked (`+57 ••• ••• 4567`). */
|
|
313
|
+
phone: {
|
|
314
|
+
number: string;
|
|
315
|
+
channel: string;
|
|
316
|
+
} | null;
|
|
317
|
+
/** The ways the workspace sends codes to phones. */
|
|
318
|
+
phoneChannels: string[];
|
|
319
|
+
}
|
|
302
320
|
interface AccountClient {
|
|
303
321
|
/** The account, or null when nobody is signed in. */
|
|
304
322
|
get(): Promise<Account$1 | null>;
|
|
@@ -337,6 +355,30 @@ interface AccountClient {
|
|
|
337
355
|
}): Promise<AuthResult<'renewed', {
|
|
338
356
|
recoveryCodes: string[];
|
|
339
357
|
}>>;
|
|
358
|
+
/**
|
|
359
|
+
* How the account takes its second step: an authenticator app, a phone
|
|
360
|
+
* (masked), and the ways the workspace sends codes to phones (`sms`,
|
|
361
|
+
* `whatsapp`; none when it has no Twilio account for it).
|
|
362
|
+
*/
|
|
363
|
+
factors(): Promise<SecondStep | null>;
|
|
364
|
+
/** Adds a phone as the second step: a code is sent to it, for `verifyPhone`. */
|
|
365
|
+
addPhone(form: {
|
|
366
|
+
phone: string;
|
|
367
|
+
channel: 'sms' | 'whatsapp';
|
|
368
|
+
password: string;
|
|
369
|
+
}): Promise<AuthResult<'sent', {
|
|
370
|
+
sentTo: string;
|
|
371
|
+
channel: string;
|
|
372
|
+
}>>;
|
|
373
|
+
/** Proves the phone with the code it received. `recoveryCodes` come with a first second step, shown this once. */
|
|
374
|
+
verifyPhone(form: {
|
|
375
|
+
code: string;
|
|
376
|
+
}): Promise<AuthResult<'enabled', {
|
|
377
|
+
recoveryCodes: string[];
|
|
378
|
+
}>>;
|
|
379
|
+
removePhone(form: {
|
|
380
|
+
password: string;
|
|
381
|
+
}): Promise<AuthResult<'removed'>>;
|
|
340
382
|
};
|
|
341
383
|
/**
|
|
342
384
|
* Asks to sign in with another address. Nothing changes yet: the new address
|
|
@@ -393,6 +435,7 @@ interface AuthClient {
|
|
|
393
435
|
email(form: {
|
|
394
436
|
email: string;
|
|
395
437
|
password: string;
|
|
438
|
+
captchaToken?: string;
|
|
396
439
|
}): Promise<AuthResult<'signed_in', {
|
|
397
440
|
session: PublicSession;
|
|
398
441
|
}> | AuthResult<'mfa_required'>>;
|
|
@@ -402,9 +445,23 @@ interface AuthClient {
|
|
|
402
445
|
}): Promise<AuthResult<'signed_in', {
|
|
403
446
|
session: PublicSession;
|
|
404
447
|
}>>;
|
|
448
|
+
/** While the second step is asked for: whether the account has an authenticator app, and the phone (masked) a code can be sent to. */
|
|
449
|
+
mfaMethods(): Promise<AuthResult<'ok', {
|
|
450
|
+
totp: boolean;
|
|
451
|
+
phone: {
|
|
452
|
+
number: string;
|
|
453
|
+
channel: string;
|
|
454
|
+
} | null;
|
|
455
|
+
}>>;
|
|
456
|
+
/** Sends a code to the account's phone, by SMS or WhatsApp; `mfa` takes it. A few per sign-in. */
|
|
457
|
+
mfaSendCode(): Promise<AuthResult<'sent', {
|
|
458
|
+
sentTo: string;
|
|
459
|
+
channel: string;
|
|
460
|
+
}>>;
|
|
405
461
|
/** Mails a link that signs the person in. The answer is the same whether or not the address has an account. */
|
|
406
462
|
magicLink(form: {
|
|
407
463
|
email: string;
|
|
464
|
+
captchaToken?: string;
|
|
408
465
|
}): Promise<AuthResult<'sent'>>;
|
|
409
466
|
/** Signs in with the token the mailed link opened the page with (`ml_token`). */
|
|
410
467
|
magicLinkVerify(form: {
|
|
@@ -480,6 +537,7 @@ interface AuthClient {
|
|
|
480
537
|
email: string;
|
|
481
538
|
password: string;
|
|
482
539
|
name?: string;
|
|
540
|
+
captchaToken?: string;
|
|
483
541
|
}): Promise<AuthResult<'signed_in', {
|
|
484
542
|
session: PublicSession;
|
|
485
543
|
}> | AuthResult<'verification_required', {
|
|
@@ -489,6 +547,7 @@ interface AuthClient {
|
|
|
489
547
|
/** Mails a link to choose a new password. The answer is the same whether or not the address has an account. */
|
|
490
548
|
forgotPassword(form: {
|
|
491
549
|
email: string;
|
|
550
|
+
captchaToken?: string;
|
|
492
551
|
}): Promise<AuthResult<'sent'>>;
|
|
493
552
|
/** Sets the new password, with the token the mailed link opened the page with (`token`). */
|
|
494
553
|
resetPassword(form: {
|
|
@@ -542,6 +601,20 @@ interface AuthTexts {
|
|
|
542
601
|
sendLink: string;
|
|
543
602
|
linkSent: string;
|
|
544
603
|
codeHelp: string;
|
|
604
|
+
/** The second step when it is a phone only. */
|
|
605
|
+
codeHelpPhone: string;
|
|
606
|
+
/** "{to}" is the phone, masked. */
|
|
607
|
+
sendCodeSms: string;
|
|
608
|
+
sendCodeWhatsapp: string;
|
|
609
|
+
codeSent: string;
|
|
610
|
+
phone: string;
|
|
611
|
+
phoneHelp: string;
|
|
612
|
+
phoneOn: string;
|
|
613
|
+
phoneOff: string;
|
|
614
|
+
addPhone: string;
|
|
615
|
+
removePhone: string;
|
|
616
|
+
bySms: string;
|
|
617
|
+
byWhatsapp: string;
|
|
545
618
|
verify: string;
|
|
546
619
|
checkEmail: string;
|
|
547
620
|
backToSignIn: string;
|
|
@@ -734,4 +807,4 @@ declare function useSession(): SessionContext;
|
|
|
734
807
|
*/
|
|
735
808
|
declare function useAuth(): AuthClient & Pick<SessionContext, 'session' | 'status' | 'refresh'>;
|
|
736
809
|
|
|
737
|
-
export { Account, type AccountClient, type Account$1 as AccountDetails, type AccountProps, type AccountSession, type AuthClient, type AuthError, type AuthFormProps, type AuthMethods, type AuthResult, type AuthTexts, CauthyProvider, type CauthyProviderProps, ForgotPassword, GoogleOneTap, type GoogleOneTapProps, type JsonValue, type LinkedIdentity, type Passkey, type PermissionOf, type Policy, type PublicSession, ResetPassword, type RoleOf, type SessionContext, type SessionOrganization, type SessionUser, SignIn, SignUp, VerifyEmail, can, canAutofillPasskeys, canUsePasskeys, createAuthClient, definePolicy, useAuth, useSession };
|
|
810
|
+
export { Account, type AccountClient, type Account$1 as AccountDetails, type AccountProps, type AccountSession, type AuthClient, type AuthError, type AuthFormProps, type AuthMethods, type AuthResult, type AuthTexts, CauthyProvider, type CauthyProviderProps, ForgotPassword, GoogleOneTap, type GoogleOneTapProps, type JsonValue, type LinkedIdentity, type Passkey, type PermissionOf, type Policy, type PublicSession, ResetPassword, type RoleOf, type SecondStep, type SessionContext, type SessionOrganization, type SessionUser, SignIn, SignUp, VerifyEmail, can, canAutofillPasskeys, canUsePasskeys, createAuthClient, definePolicy, useAuth, useSession };
|
package/dist/react.js
CHANGED
|
@@ -240,13 +240,13 @@ function createAuthClient(options = {}) {
|
|
|
240
240
|
const said = typeof options.locale === "function" ? options.locale() : options.locale;
|
|
241
241
|
return (said ?? (typeof document === "undefined" ? "" : document.documentElement?.lang) ?? "").trim();
|
|
242
242
|
};
|
|
243
|
-
async function post(path, form, bad) {
|
|
243
|
+
async function post(path, form, bad, captchaToken) {
|
|
244
244
|
if (bad) return { ok: false, error: { code: bad.code, message: bad.message, field: bad.field } };
|
|
245
245
|
try {
|
|
246
246
|
const res = await doFetch(`${base}${path}`, {
|
|
247
247
|
method: "POST",
|
|
248
248
|
credentials: "same-origin",
|
|
249
|
-
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 } : {} },
|
|
250
250
|
body: JSON.stringify(form)
|
|
251
251
|
});
|
|
252
252
|
const body = await res.json().catch(() => null);
|
|
@@ -298,9 +298,11 @@ function createAuthClient(options = {}) {
|
|
|
298
298
|
}
|
|
299
299
|
return {
|
|
300
300
|
signIn: {
|
|
301
|
-
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),
|
|
302
302
|
mfa: (f) => post("/sign-in/mfa", { code: f.code }, checkCode(f.code)),
|
|
303
|
-
|
|
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),
|
|
304
306
|
magicLinkVerify: (f) => post("/sign-in/magic-link/verify", { token: f.token }, null),
|
|
305
307
|
emailCode: (f) => post("/sign-in/email-code", { code: f.code }, checkCode(f.code)),
|
|
306
308
|
passkeyUrl,
|
|
@@ -381,9 +383,9 @@ function createAuthClient(options = {}) {
|
|
|
381
383
|
}
|
|
382
384
|
},
|
|
383
385
|
signUp: {
|
|
384
|
-
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)
|
|
385
387
|
},
|
|
386
|
-
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),
|
|
387
389
|
resetPassword: (f) => post("/reset-password", { token: f.token, password: f.password }, checkNewPassword(f.password)),
|
|
388
390
|
verifyEmail: (f) => post("/verify-email", { token: f.token }, null),
|
|
389
391
|
signOut: () => post("/sign-out", {}, null),
|
|
@@ -401,7 +403,14 @@ function createAuthClient(options = {}) {
|
|
|
401
403
|
setup: (f) => post("/me/mfa/setup", { password: f.password }, null),
|
|
402
404
|
verify: (f) => post("/me/mfa/verify", { code: f.code }, checkCode(f.code)),
|
|
403
405
|
disable: (f) => post("/me/mfa/disable", { password: f.password }, null),
|
|
404
|
-
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)
|
|
405
414
|
},
|
|
406
415
|
changeEmail: (f) => post("/me/email", { email: f.email, password: f.password }, checkEmail(f.email)),
|
|
407
416
|
async identities() {
|
|
@@ -519,6 +528,18 @@ var TEXTS = {
|
|
|
519
528
|
linkSent: "If that address has an account, a link to choose a new password is on its way.",
|
|
520
529
|
codeHelp: "Enter the code from your authenticator app, or a recovery code.",
|
|
521
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",
|
|
522
543
|
checkEmail: "Almost there: open the link we sent to confirm your address, then sign in.",
|
|
523
544
|
backToSignIn: "Back to sign in",
|
|
524
545
|
emailMeALink: "Email me a sign-in link",
|
|
@@ -586,6 +607,13 @@ var TEXTS = {
|
|
|
586
607
|
step_up_required: "Enter your current password.",
|
|
587
608
|
weak_password: "Choose a stronger password.",
|
|
588
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.",
|
|
589
617
|
passkey_failed: "The passkey could not be used. Try again, or sign in another way.",
|
|
590
618
|
passkey_exists: "This device already has a passkey for your account.",
|
|
591
619
|
passkey_unavailable: "Passkeys cannot be used here."
|
|
@@ -608,6 +636,18 @@ var TEXTS = {
|
|
|
608
636
|
linkSent: "Si ese correo tiene una cuenta, ya va en camino un enlace para elegir una contrase\xF1a nueva.",
|
|
609
637
|
codeHelp: "Escribe el c\xF3digo de tu aplicaci\xF3n de autenticaci\xF3n, o un c\xF3digo de recuperaci\xF3n.",
|
|
610
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",
|
|
611
651
|
checkEmail: "Ya casi: abre el enlace que te enviamos para confirmar tu correo y luego entra.",
|
|
612
652
|
backToSignIn: "Volver a entrar",
|
|
613
653
|
emailMeALink: "Env\xEDame un enlace para entrar",
|
|
@@ -675,6 +715,13 @@ var TEXTS = {
|
|
|
675
715
|
step_up_required: "Escribe tu contrase\xF1a actual.",
|
|
676
716
|
weak_password: "Elige una contrase\xF1a m\xE1s fuerte.",
|
|
677
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.",
|
|
678
725
|
passkey_failed: "No se pudo usar la llave de acceso. Intenta de nuevo o entra de otra forma.",
|
|
679
726
|
passkey_exists: "Este dispositivo ya tiene una llave de acceso de tu cuenta.",
|
|
680
727
|
passkey_unavailable: "Aqu\xED no se pueden usar llaves de acceso."
|
|
@@ -719,6 +766,63 @@ function useRoot(props) {
|
|
|
719
766
|
if (props.unstyled) return {};
|
|
720
767
|
return { "data-cauthy": "", style: vars };
|
|
721
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
|
+
}
|
|
722
826
|
function useForm(props) {
|
|
723
827
|
const t = { ...TEXTS[props.locale ?? "en"], ...props.texts, errors: { ...TEXTS[props.locale ?? "en"].errors, ...props.texts?.errors } };
|
|
724
828
|
const cx = (slot) => props.classNames?.[slot] ?? `cauthy-${slot}`;
|
|
@@ -791,6 +895,23 @@ function SignIn(props) {
|
|
|
791
895
|
const [password, setPassword] = (0, import_react.useState)("");
|
|
792
896
|
const [code, setCode] = (0, import_react.useState)("");
|
|
793
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
|
+
};
|
|
794
915
|
const autofill = (0, import_react.useRef)(null);
|
|
795
916
|
const [offer, setOffer] = (0, import_react.useState)(0);
|
|
796
917
|
(0, import_react.useEffect)(() => {
|
|
@@ -825,8 +946,9 @@ function SignIn(props) {
|
|
|
825
946
|
autofill.current?.abort();
|
|
826
947
|
f.setBusy(true);
|
|
827
948
|
f.setError(null);
|
|
828
|
-
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 });
|
|
829
950
|
f.setBusy(false);
|
|
951
|
+
if (step === "password") challenge.renew();
|
|
830
952
|
if (!r.ok) {
|
|
831
953
|
setOffer((n) => n + 1);
|
|
832
954
|
return f.setError(r.error);
|
|
@@ -849,8 +971,9 @@ function SignIn(props) {
|
|
|
849
971
|
autofill.current?.abort();
|
|
850
972
|
f.setBusy(true);
|
|
851
973
|
f.setError(null);
|
|
852
|
-
const r = await auth.signIn.magicLink({ email });
|
|
974
|
+
const r = await auth.signIn.magicLink({ email, captchaToken: challenge.token });
|
|
853
975
|
f.setBusy(false);
|
|
976
|
+
challenge.renew();
|
|
854
977
|
if (!r.ok) return f.setError(r.error);
|
|
855
978
|
setStep("sent");
|
|
856
979
|
};
|
|
@@ -866,9 +989,11 @@ function SignIn(props) {
|
|
|
866
989
|
] });
|
|
867
990
|
}
|
|
868
991
|
if (step === "code") {
|
|
992
|
+
const to = phoneWay?.phone?.number ?? "";
|
|
869
993
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("form", { className: f.cx("form"), ...f.root, onSubmit: submit, noValidate: true, children: [
|
|
870
|
-
/* @__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 }),
|
|
871
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) }),
|
|
872
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 }),
|
|
873
998
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(f.Button, { type: "submit", disabled: f.busy, className: f.cx("button"), children: f.t.verify })
|
|
874
999
|
] });
|
|
@@ -897,6 +1022,7 @@ function SignIn(props) {
|
|
|
897
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
|
|
898
1023
|
}
|
|
899
1024
|
),
|
|
1025
|
+
challenge.node,
|
|
900
1026
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(f.Button, { type: "submit", disabled: f.busy, className: f.cx("button"), children: f.t.signIn }),
|
|
901
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 }),
|
|
902
1028
|
methods?.signUp !== false && props.links?.signUp && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("p", { className: f.cx("footer"), children: [
|
|
@@ -928,12 +1054,14 @@ function SignUp(props) {
|
|
|
928
1054
|
const [email, setEmail] = (0, import_react.useState)("");
|
|
929
1055
|
const [password, setPassword] = (0, import_react.useState)("");
|
|
930
1056
|
const [sent, setSent] = (0, import_react.useState)(false);
|
|
1057
|
+
const challenge = useChallenge(useMethods(auth)?.captchaSiteKey, props.locale);
|
|
931
1058
|
const submit = async (e) => {
|
|
932
1059
|
e.preventDefault();
|
|
933
1060
|
f.setBusy(true);
|
|
934
1061
|
f.setError(null);
|
|
935
|
-
const r = await auth.signUp.email({ name, email, password });
|
|
1062
|
+
const r = await auth.signUp.email({ name, email, password, captchaToken: challenge.token });
|
|
936
1063
|
f.setBusy(false);
|
|
1064
|
+
challenge.renew();
|
|
937
1065
|
if (!r.ok) return f.setError(r.error);
|
|
938
1066
|
if (r.status === "verification_required") return setSent(true);
|
|
939
1067
|
f.done();
|
|
@@ -950,6 +1078,7 @@ function SignUp(props) {
|
|
|
950
1078
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Field, { f, name: "name", type: "text", label: f.t.name, value: name, autoComplete: "name", onChange: setName }),
|
|
951
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 }),
|
|
952
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,
|
|
953
1082
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(f.Button, { type: "submit", disabled: f.busy, className: f.cx("button"), children: f.t.signUp }),
|
|
954
1083
|
props.links?.signIn && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("p", { className: f.cx("footer"), children: [
|
|
955
1084
|
f.t.haveAccount,
|
|
@@ -963,12 +1092,14 @@ function ForgotPassword(props) {
|
|
|
963
1092
|
const f = useForm(props);
|
|
964
1093
|
const [email, setEmail] = (0, import_react.useState)("");
|
|
965
1094
|
const [sent, setSent] = (0, import_react.useState)(false);
|
|
1095
|
+
const challenge = useChallenge(useMethods(auth)?.captchaSiteKey, props.locale);
|
|
966
1096
|
const submit = async (e) => {
|
|
967
1097
|
e.preventDefault();
|
|
968
1098
|
f.setBusy(true);
|
|
969
1099
|
f.setError(null);
|
|
970
|
-
const r = await auth.forgotPassword({ email });
|
|
1100
|
+
const r = await auth.forgotPassword({ email, captchaToken: challenge.token });
|
|
971
1101
|
f.setBusy(false);
|
|
1102
|
+
challenge.renew();
|
|
972
1103
|
if (!r.ok) return f.setError(r.error);
|
|
973
1104
|
setSent(true);
|
|
974
1105
|
};
|
|
@@ -982,6 +1113,7 @@ function ForgotPassword(props) {
|
|
|
982
1113
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("form", { className: f.cx("form"), ...f.root, onSubmit: submit, noValidate: true, children: [
|
|
983
1114
|
general && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { role: "alert", className: f.cx("error"), children: f.say(general) }),
|
|
984
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,
|
|
985
1117
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(f.Button, { type: "submit", disabled: f.busy, className: f.cx("button"), children: f.t.sendLink }),
|
|
986
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 }) })
|
|
987
1119
|
] });
|
|
@@ -1081,6 +1213,7 @@ function Account(props) {
|
|
|
1081
1213
|
account.hasPassword && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(PasswordPart, { props }),
|
|
1082
1214
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(LinkedPart, { props }),
|
|
1083
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 }) }),
|
|
1084
1217
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(PasskeysPart, { props, account, accountUrl: accountUrl(), when }),
|
|
1085
1218
|
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("section", { className: cx("section"), children: [
|
|
1086
1219
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("h2", { className: cx("heading"), children: t.sessions }),
|
|
@@ -1310,6 +1443,77 @@ function SecondStepPart({ props, account, onChanged }) {
|
|
|
1310
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 })
|
|
1311
1444
|
] });
|
|
1312
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
|
+
}
|
|
1313
1517
|
function SignOutEverywhere({ props }) {
|
|
1314
1518
|
const auth = useAuth();
|
|
1315
1519
|
const { f, run, general } = useSection(props);
|