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/client.d.ts
CHANGED
|
@@ -6,6 +6,12 @@ import 'jose';
|
|
|
6
6
|
|
|
7
7
|
/** Whether this browser can use a passkey at all. */
|
|
8
8
|
declare function canUsePasskeys(): boolean;
|
|
9
|
+
/**
|
|
10
|
+
* Whether this browser offers passkeys in the autofill of a field marked
|
|
11
|
+
* `autocomplete="username webauthn"`, so the person picks one without
|
|
12
|
+
* looking for a button.
|
|
13
|
+
*/
|
|
14
|
+
declare function canAutofillPasskeys(): Promise<boolean>;
|
|
9
15
|
|
|
10
16
|
interface SessionState {
|
|
11
17
|
/** The signed-in person and their organizations, or null. */
|
|
@@ -87,6 +93,13 @@ interface AuthMethods {
|
|
|
87
93
|
* page that cannot, or a browser that refuses to here, sends the person.
|
|
88
94
|
*/
|
|
89
95
|
passkeyOnCauthy: boolean;
|
|
96
|
+
/**
|
|
97
|
+
* The workspace's Cloudflare Turnstile site key, when it asks for the
|
|
98
|
+
* challenge: signing in, creating an account, and asking for a sign-in link
|
|
99
|
+
* or a new password then take its answer (`captchaToken`). The ready-made
|
|
100
|
+
* forms show it themselves.
|
|
101
|
+
*/
|
|
102
|
+
captchaSiteKey?: string;
|
|
90
103
|
}
|
|
91
104
|
/** A passkey of the signed-in account. Moments are UTC. */
|
|
92
105
|
interface Passkey {
|
|
@@ -128,6 +141,17 @@ interface LinkedIdentity {
|
|
|
128
141
|
/** When it was linked, in UTC (ISO 8601). */
|
|
129
142
|
linkedAt: string;
|
|
130
143
|
}
|
|
144
|
+
/** How an account takes its second step. */
|
|
145
|
+
interface SecondStep {
|
|
146
|
+
totp: boolean;
|
|
147
|
+
/** The phone codes are sent to, masked (`+57 ••• ••• 4567`). */
|
|
148
|
+
phone: {
|
|
149
|
+
number: string;
|
|
150
|
+
channel: string;
|
|
151
|
+
} | null;
|
|
152
|
+
/** The ways the workspace sends codes to phones. */
|
|
153
|
+
phoneChannels: string[];
|
|
154
|
+
}
|
|
131
155
|
interface AccountClient {
|
|
132
156
|
/** The account, or null when nobody is signed in. */
|
|
133
157
|
get(): Promise<Account | null>;
|
|
@@ -166,6 +190,30 @@ interface AccountClient {
|
|
|
166
190
|
}): Promise<AuthResult<'renewed', {
|
|
167
191
|
recoveryCodes: string[];
|
|
168
192
|
}>>;
|
|
193
|
+
/**
|
|
194
|
+
* How the account takes its second step: an authenticator app, a phone
|
|
195
|
+
* (masked), and the ways the workspace sends codes to phones (`sms`,
|
|
196
|
+
* `whatsapp`; none when it has no Twilio account for it).
|
|
197
|
+
*/
|
|
198
|
+
factors(): Promise<SecondStep | null>;
|
|
199
|
+
/** Adds a phone as the second step: a code is sent to it, for `verifyPhone`. */
|
|
200
|
+
addPhone(form: {
|
|
201
|
+
phone: string;
|
|
202
|
+
channel: 'sms' | 'whatsapp';
|
|
203
|
+
password: string;
|
|
204
|
+
}): Promise<AuthResult<'sent', {
|
|
205
|
+
sentTo: string;
|
|
206
|
+
channel: string;
|
|
207
|
+
}>>;
|
|
208
|
+
/** Proves the phone with the code it received. `recoveryCodes` come with a first second step, shown this once. */
|
|
209
|
+
verifyPhone(form: {
|
|
210
|
+
code: string;
|
|
211
|
+
}): Promise<AuthResult<'enabled', {
|
|
212
|
+
recoveryCodes: string[];
|
|
213
|
+
}>>;
|
|
214
|
+
removePhone(form: {
|
|
215
|
+
password: string;
|
|
216
|
+
}): Promise<AuthResult<'removed'>>;
|
|
169
217
|
};
|
|
170
218
|
/**
|
|
171
219
|
* Asks to sign in with another address. Nothing changes yet: the new address
|
|
@@ -222,6 +270,7 @@ interface AuthClient {
|
|
|
222
270
|
email(form: {
|
|
223
271
|
email: string;
|
|
224
272
|
password: string;
|
|
273
|
+
captchaToken?: string;
|
|
225
274
|
}): Promise<AuthResult<'signed_in', {
|
|
226
275
|
session: PublicSession;
|
|
227
276
|
}> | AuthResult<'mfa_required'>>;
|
|
@@ -231,9 +280,23 @@ interface AuthClient {
|
|
|
231
280
|
}): Promise<AuthResult<'signed_in', {
|
|
232
281
|
session: PublicSession;
|
|
233
282
|
}>>;
|
|
283
|
+
/** While the second step is asked for: whether the account has an authenticator app, and the phone (masked) a code can be sent to. */
|
|
284
|
+
mfaMethods(): Promise<AuthResult<'ok', {
|
|
285
|
+
totp: boolean;
|
|
286
|
+
phone: {
|
|
287
|
+
number: string;
|
|
288
|
+
channel: string;
|
|
289
|
+
} | null;
|
|
290
|
+
}>>;
|
|
291
|
+
/** Sends a code to the account's phone, by SMS or WhatsApp; `mfa` takes it. A few per sign-in. */
|
|
292
|
+
mfaSendCode(): Promise<AuthResult<'sent', {
|
|
293
|
+
sentTo: string;
|
|
294
|
+
channel: string;
|
|
295
|
+
}>>;
|
|
234
296
|
/** Mails a link that signs the person in. The answer is the same whether or not the address has an account. */
|
|
235
297
|
magicLink(form: {
|
|
236
298
|
email: string;
|
|
299
|
+
captchaToken?: string;
|
|
237
300
|
}): Promise<AuthResult<'sent'>>;
|
|
238
301
|
/** Signs in with the token the mailed link opened the page with (`ml_token`). */
|
|
239
302
|
magicLinkVerify(form: {
|
|
@@ -261,6 +324,34 @@ interface AuthClient {
|
|
|
261
324
|
}): Promise<AuthResult<'signed_in', {
|
|
262
325
|
session: PublicSession;
|
|
263
326
|
}> | AuthResult<'redirected'>>;
|
|
327
|
+
/**
|
|
328
|
+
* Offers the person's passkeys in the browser's autofill of the email
|
|
329
|
+
* field, which must carry `autocomplete="username webauthn"`, and signs in
|
|
330
|
+
* with the one they pick. It waits until they pick one or `signal`
|
|
331
|
+
* aborts: abort it before calling `passkey` or sending the form.
|
|
332
|
+
* `cancelled`: aborted, or this browser or page cannot (then there is
|
|
333
|
+
* nothing to offer, and the passkey button still works).
|
|
334
|
+
*/
|
|
335
|
+
passkeyAutofill(options?: {
|
|
336
|
+
signal?: AbortSignal;
|
|
337
|
+
}): Promise<AuthResult<'signed_in', {
|
|
338
|
+
session: PublicSession;
|
|
339
|
+
}>>;
|
|
340
|
+
/**
|
|
341
|
+
* Google One Tap: Google's prompt over the page offers the Google account
|
|
342
|
+
* the browser already has, and signs in with it in one tap, as the Google
|
|
343
|
+
* sign-in does (the same account). Needs the workspace's Google sign-in,
|
|
344
|
+
* and the page's address among the Google client's authorized JavaScript
|
|
345
|
+
* origins. Waits until the person picks an account or closes the prompt
|
|
346
|
+
* (`cancelled`), or `signal` aborts. `one_tap_unavailable`: the workspace
|
|
347
|
+
* has no Google sign-in.
|
|
348
|
+
*/
|
|
349
|
+
googleOneTap(options?: {
|
|
350
|
+
signal?: AbortSignal;
|
|
351
|
+
context?: 'signin' | 'signup' | 'use';
|
|
352
|
+
}): Promise<AuthResult<'signed_in', {
|
|
353
|
+
session: PublicSession;
|
|
354
|
+
}>>;
|
|
264
355
|
/** Where `passkey` sends the browser when this page cannot use the passkey itself. */
|
|
265
356
|
passkeyUrl(options?: {
|
|
266
357
|
returnTo?: string;
|
|
@@ -281,6 +372,7 @@ interface AuthClient {
|
|
|
281
372
|
email: string;
|
|
282
373
|
password: string;
|
|
283
374
|
name?: string;
|
|
375
|
+
captchaToken?: string;
|
|
284
376
|
}): Promise<AuthResult<'signed_in', {
|
|
285
377
|
session: PublicSession;
|
|
286
378
|
}> | AuthResult<'verification_required', {
|
|
@@ -290,6 +382,7 @@ interface AuthClient {
|
|
|
290
382
|
/** Mails a link to choose a new password. The answer is the same whether or not the address has an account. */
|
|
291
383
|
forgotPassword(form: {
|
|
292
384
|
email: string;
|
|
385
|
+
captchaToken?: string;
|
|
293
386
|
}): Promise<AuthResult<'sent'>>;
|
|
294
387
|
/** Sets the new password, with the token the mailed link opened the page with (`token`). */
|
|
295
388
|
resetPassword(form: {
|
|
@@ -326,4 +419,4 @@ interface AuthClientOptions {
|
|
|
326
419
|
}
|
|
327
420
|
declare function createAuthClient(options?: AuthClientOptions): AuthClient;
|
|
328
421
|
|
|
329
|
-
export { type Account, type AccountClient, type AccountSession, type AuthClient, type AuthClientOptions, type AuthError, AuthField, type AuthMethods, type AuthResult, type LinkedIdentity, type Passkey, type SessionState, type SessionStore, type SessionStoreOptions, canUsePasskeys, createAuthClient, createSessionStore };
|
|
422
|
+
export { type Account, type AccountClient, type AccountSession, type AuthClient, type AuthClientOptions, type AuthError, AuthField, type AuthMethods, type AuthResult, type LinkedIdentity, type Passkey, type SecondStep, type SessionState, type SessionStore, type SessionStoreOptions, canAutofillPasskeys, canUsePasskeys, createAuthClient, createSessionStore };
|
package/dist/client.js
CHANGED
|
@@ -21,6 +21,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
21
21
|
var client_exports = {};
|
|
22
22
|
__export(client_exports, {
|
|
23
23
|
MIN_PASSWORD: () => MIN_PASSWORD,
|
|
24
|
+
canAutofillPasskeys: () => canAutofillPasskeys,
|
|
24
25
|
canUsePasskeys: () => canUsePasskeys,
|
|
25
26
|
checkCode: () => checkCode,
|
|
26
27
|
checkEmail: () => checkEmail,
|
|
@@ -36,6 +37,15 @@ module.exports = __toCommonJS(client_exports);
|
|
|
36
37
|
function canUsePasskeys() {
|
|
37
38
|
return typeof window !== "undefined" && typeof window.PublicKeyCredential === "function" && typeof navigator !== "undefined" && typeof navigator.credentials?.get === "function";
|
|
38
39
|
}
|
|
40
|
+
async function canAutofillPasskeys() {
|
|
41
|
+
if (!canUsePasskeys()) return false;
|
|
42
|
+
const pkc = window.PublicKeyCredential;
|
|
43
|
+
try {
|
|
44
|
+
return typeof pkc.isConditionalMediationAvailable === "function" && await pkc.isConditionalMediationAvailable() === true;
|
|
45
|
+
} catch {
|
|
46
|
+
return false;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
39
49
|
function bytes(text2) {
|
|
40
50
|
const b64 = text2.replace(/-/g, "+").replace(/_/g, "/").padEnd(Math.ceil(text2.length / 4) * 4, "=");
|
|
41
51
|
const bin = atob(b64);
|
|
@@ -51,9 +61,9 @@ function text(buf) {
|
|
|
51
61
|
return btoa(bin).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
|
|
52
62
|
}
|
|
53
63
|
var withId = (list) => (list ?? []).map((c) => ({ ...c, id: bytes(c.id) }));
|
|
54
|
-
async function getPasskey(options) {
|
|
64
|
+
async function getPasskey(options, how = {}) {
|
|
55
65
|
const publicKey = { ...options, challenge: bytes(options.challenge), allowCredentials: withId(options.allowCredentials) };
|
|
56
|
-
const cred = await navigator.credentials.get({ publicKey });
|
|
66
|
+
const cred = await navigator.credentials.get({ publicKey, ...how });
|
|
57
67
|
if (cred === null) throw new DOMException("No passkey was chosen.", "NotAllowedError");
|
|
58
68
|
const r = cred.response;
|
|
59
69
|
return {
|
|
@@ -281,6 +291,24 @@ function createSessionStore(options = {}) {
|
|
|
281
291
|
}
|
|
282
292
|
|
|
283
293
|
// src/client.ts
|
|
294
|
+
var googleScript = null;
|
|
295
|
+
function loadGoogleIdentity() {
|
|
296
|
+
const ready = () => window.google?.accounts?.id;
|
|
297
|
+
if (ready()) return Promise.resolve(ready());
|
|
298
|
+
googleScript ??= new Promise((resolve, reject) => {
|
|
299
|
+
const script = document.createElement("script");
|
|
300
|
+
script.src = "https://accounts.google.com/gsi/client";
|
|
301
|
+
script.async = true;
|
|
302
|
+
script.onload = () => ready() ? resolve(ready()) : reject(new Error("google identity services did not load"));
|
|
303
|
+
script.onerror = () => {
|
|
304
|
+
googleScript = null;
|
|
305
|
+
reject(new Error("google identity services did not load"));
|
|
306
|
+
};
|
|
307
|
+
document.head.appendChild(script);
|
|
308
|
+
});
|
|
309
|
+
return googleScript;
|
|
310
|
+
}
|
|
311
|
+
var PASSKEY_SECONDS = 300;
|
|
284
312
|
var UNAVAILABLE = { code: "unavailable", message: "Sign-in is unavailable right now. Try again in a moment." };
|
|
285
313
|
function createAuthClient(options = {}) {
|
|
286
314
|
const base = (options.basePath ?? "/api/auth").replace(/\/+$/, "");
|
|
@@ -289,13 +317,13 @@ function createAuthClient(options = {}) {
|
|
|
289
317
|
const said = typeof options.locale === "function" ? options.locale() : options.locale;
|
|
290
318
|
return (said ?? (typeof document === "undefined" ? "" : document.documentElement?.lang) ?? "").trim();
|
|
291
319
|
};
|
|
292
|
-
async function post(path, form, bad) {
|
|
320
|
+
async function post(path, form, bad, captchaToken) {
|
|
293
321
|
if (bad) return { ok: false, error: { code: bad.code, message: bad.message, field: bad.field } };
|
|
294
322
|
try {
|
|
295
323
|
const res = await doFetch(`${base}${path}`, {
|
|
296
324
|
method: "POST",
|
|
297
325
|
credentials: "same-origin",
|
|
298
|
-
headers: { "content-type": "application/json", accept: "application/json", ...locale() ? { "x-cauthy-locale": locale() } : {} },
|
|
326
|
+
headers: { "content-type": "application/json", accept: "application/json", ...locale() ? { "x-cauthy-locale": locale() } : {}, ...captchaToken ? { "x-cauthy-captcha": captchaToken } : {} },
|
|
299
327
|
body: JSON.stringify(form)
|
|
300
328
|
});
|
|
301
329
|
const body = await res.json().catch(() => null);
|
|
@@ -347,9 +375,11 @@ function createAuthClient(options = {}) {
|
|
|
347
375
|
}
|
|
348
376
|
return {
|
|
349
377
|
signIn: {
|
|
350
|
-
email: (f) => post("/sign-in/email", { email: f.email, password: f.password }, firstProblem(checkEmail(f.email), checkPassword(f.password))),
|
|
378
|
+
email: (f) => post("/sign-in/email", { email: f.email, password: f.password }, firstProblem(checkEmail(f.email), checkPassword(f.password)), f.captchaToken),
|
|
351
379
|
mfa: (f) => post("/sign-in/mfa", { code: f.code }, checkCode(f.code)),
|
|
352
|
-
|
|
380
|
+
mfaMethods: () => post("/sign-in/mfa/methods", {}, null),
|
|
381
|
+
mfaSendCode: () => post("/sign-in/mfa/send", {}, null),
|
|
382
|
+
magicLink: (f) => post("/sign-in/magic-link", { email: f.email }, checkEmail(f.email), f.captchaToken),
|
|
353
383
|
magicLinkVerify: (f) => post("/sign-in/magic-link/verify", { token: f.token }, null),
|
|
354
384
|
emailCode: (f) => post("/sign-in/email-code", { code: f.code }, checkCode(f.code)),
|
|
355
385
|
passkeyUrl,
|
|
@@ -364,15 +394,75 @@ function createAuthClient(options = {}) {
|
|
|
364
394
|
window.location.assign(passkeyUrl(o));
|
|
365
395
|
return { ok: true, status: "redirected" };
|
|
366
396
|
},
|
|
397
|
+
async passkeyAutofill(o = {}) {
|
|
398
|
+
const cancelled = { ok: false, error: { code: "cancelled", message: "No passkey was chosen." } };
|
|
399
|
+
const m = await methods();
|
|
400
|
+
if (!m.passkeyHere || !await canAutofillPasskeys()) return cancelled;
|
|
401
|
+
for (; ; ) {
|
|
402
|
+
if (o.signal?.aborted) return cancelled;
|
|
403
|
+
const begun = await post("/sign-in/passkey/begin", {}, null);
|
|
404
|
+
if (!begun.ok) return begun;
|
|
405
|
+
const renew = new AbortController();
|
|
406
|
+
const stop = () => renew.abort();
|
|
407
|
+
o.signal?.addEventListener("abort", stop, { once: true });
|
|
408
|
+
const timer = setTimeout(stop, (PASSKEY_SECONDS - 30) * 1e3);
|
|
409
|
+
let credential;
|
|
410
|
+
try {
|
|
411
|
+
credential = await getPasskey(begun.options, { mediation: "conditional", signal: renew.signal });
|
|
412
|
+
} catch (e) {
|
|
413
|
+
if (o.signal?.aborted) return cancelled;
|
|
414
|
+
if (renew.signal.aborted) continue;
|
|
415
|
+
return { ok: false, error: ceremonyError(e) };
|
|
416
|
+
} finally {
|
|
417
|
+
clearTimeout(timer);
|
|
418
|
+
o.signal?.removeEventListener("abort", stop);
|
|
419
|
+
}
|
|
420
|
+
return post("/sign-in/passkey/finish", { credential }, null);
|
|
421
|
+
}
|
|
422
|
+
},
|
|
423
|
+
async googleOneTap(o = {}) {
|
|
424
|
+
const cancelled = { ok: false, error: { code: "cancelled", message: "No Google account was chosen." } };
|
|
425
|
+
if (typeof window === "undefined" || o.signal?.aborted) return cancelled;
|
|
426
|
+
const begun = await post("/sign-in/google-one-tap/begin", {}, null);
|
|
427
|
+
if (!begun.ok) return begun;
|
|
428
|
+
let gis;
|
|
429
|
+
try {
|
|
430
|
+
gis = await loadGoogleIdentity();
|
|
431
|
+
} catch {
|
|
432
|
+
return { ok: false, error: { code: "unavailable", message: "Google could not be reached. Try again, or sign in another way." } };
|
|
433
|
+
}
|
|
434
|
+
const credential = await new Promise((done) => {
|
|
435
|
+
o.signal?.addEventListener("abort", () => {
|
|
436
|
+
gis.cancel();
|
|
437
|
+
done(null);
|
|
438
|
+
}, { once: true });
|
|
439
|
+
gis.initialize({
|
|
440
|
+
client_id: begun.clientId,
|
|
441
|
+
nonce: begun.nonce,
|
|
442
|
+
context: o.context ?? "signin",
|
|
443
|
+
callback: (r) => done(r.credential ?? null),
|
|
444
|
+
auto_select: false,
|
|
445
|
+
cancel_on_tap_outside: true,
|
|
446
|
+
itp_support: true,
|
|
447
|
+
use_fedcm_for_prompt: true
|
|
448
|
+
});
|
|
449
|
+
gis.prompt((moment) => {
|
|
450
|
+
if (moment.isNotDisplayed?.() || moment.isSkippedMoment?.()) done(null);
|
|
451
|
+
else if (moment.isDismissedMoment?.() && moment.getDismissedReason?.() !== "credential_returned") done(null);
|
|
452
|
+
});
|
|
453
|
+
});
|
|
454
|
+
if (credential === null) return cancelled;
|
|
455
|
+
return post("/sign-in/google-one-tap", { credential }, null);
|
|
456
|
+
},
|
|
367
457
|
socialUrl,
|
|
368
458
|
social(provider, o) {
|
|
369
459
|
window.location.assign(socialUrl(provider, o));
|
|
370
460
|
}
|
|
371
461
|
},
|
|
372
462
|
signUp: {
|
|
373
|
-
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)))
|
|
463
|
+
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)
|
|
374
464
|
},
|
|
375
|
-
forgotPassword: (f) => post("/forgot-password", { email: f.email }, checkEmail(f.email)),
|
|
465
|
+
forgotPassword: (f) => post("/forgot-password", { email: f.email }, checkEmail(f.email), f.captchaToken),
|
|
376
466
|
resetPassword: (f) => post("/reset-password", { token: f.token, password: f.password }, checkNewPassword(f.password)),
|
|
377
467
|
verifyEmail: (f) => post("/verify-email", { token: f.token }, null),
|
|
378
468
|
signOut: () => post("/sign-out", {}, null),
|
|
@@ -390,7 +480,14 @@ function createAuthClient(options = {}) {
|
|
|
390
480
|
setup: (f) => post("/me/mfa/setup", { password: f.password }, null),
|
|
391
481
|
verify: (f) => post("/me/mfa/verify", { code: f.code }, checkCode(f.code)),
|
|
392
482
|
disable: (f) => post("/me/mfa/disable", { password: f.password }, null),
|
|
393
|
-
recoveryCodes: (f) => post("/me/mfa/recovery-codes", { password: f.password }, null)
|
|
483
|
+
recoveryCodes: (f) => post("/me/mfa/recovery-codes", { password: f.password }, null),
|
|
484
|
+
async factors() {
|
|
485
|
+
const body = await read("/me/mfa/factors");
|
|
486
|
+
return body && typeof body.totp === "boolean" ? body : null;
|
|
487
|
+
},
|
|
488
|
+
addPhone: (f) => post("/me/mfa/phone", { phone: f.phone, channel: f.channel, password: f.password }, null),
|
|
489
|
+
verifyPhone: (f) => post("/me/mfa/phone/verify", { code: f.code }, checkCode(f.code)),
|
|
490
|
+
removePhone: (f) => post("/me/mfa/phone/remove", { password: f.password }, null)
|
|
394
491
|
},
|
|
395
492
|
changeEmail: (f) => post("/me/email", { email: f.email, password: f.password }, checkEmail(f.email)),
|
|
396
493
|
async identities() {
|
|
@@ -436,6 +533,7 @@ function createAuthClient(options = {}) {
|
|
|
436
533
|
// Annotate the CommonJS export names for ESM import in node:
|
|
437
534
|
0 && (module.exports = {
|
|
438
535
|
MIN_PASSWORD,
|
|
536
|
+
canAutofillPasskeys,
|
|
439
537
|
canUsePasskeys,
|
|
440
538
|
checkCode,
|
|
441
539
|
checkEmail,
|