cauthy-auth 0.10.0 → 0.12.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 +88 -5
- package/dist/client.d.ts +88 -5
- package/dist/client.js +202 -11
- package/dist/client.js.map +1 -1
- package/dist/client.mjs +200 -11
- package/dist/client.mjs.map +1 -1
- package/dist/index.js +128 -5
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +128 -5
- package/dist/index.mjs.map +1 -1
- package/dist/react.d.mts +110 -5
- package/dist/react.d.ts +110 -5
- package/dist/react.js +338 -20
- package/dist/react.js.map +1 -1
- package/dist/react.mjs +335 -20
- package/dist/react.mjs.map +1 -1
- package/dist/svelte.d.mts +1 -1
- package/dist/svelte.d.ts +1 -1
- package/dist/svelte.js +198 -11
- package/dist/svelte.js.map +1 -1
- package/dist/svelte.mjs +198 -11
- package/dist/svelte.mjs.map +1 -1
- package/dist/vue.d.mts +1 -1
- package/dist/vue.d.ts +1 -1
- package/dist/vue.js +198 -11
- package/dist/vue.js.map +1 -1
- package/dist/vue.mjs +198 -11
- package/dist/vue.mjs.map +1 -1
- package/package.json +1 -1
package/dist/svelte.d.mts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { SessionStore, SessionState, SessionStoreOptions } from './client.mjs';
|
|
2
|
-
export { Account, AccountSession, AuthClient, AuthError, AuthMethods, AuthResult } from './client.mjs';
|
|
2
|
+
export { Account, AccountSession, AuthClient, AuthError, AuthMethods, AuthResult, Passkey } from './client.mjs';
|
|
3
3
|
import { i as PublicSession } from './system-ZD_Wa8Bm.mjs';
|
|
4
4
|
export { P as PermissionOf, f as Policy, R as RoleOf, k as SessionOrganization, m as SessionUser, p as can, r as definePolicy } from './system-ZD_Wa8Bm.mjs';
|
|
5
5
|
import './theme.mjs';
|
package/dist/svelte.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { SessionStore, SessionState, SessionStoreOptions } from './client.js';
|
|
2
|
-
export { Account, AccountSession, AuthClient, AuthError, AuthMethods, AuthResult } from './client.js';
|
|
2
|
+
export { Account, AccountSession, AuthClient, AuthError, AuthMethods, AuthResult, Passkey } from './client.js';
|
|
3
3
|
import { i as PublicSession } from './system-DhRr_WxR.js';
|
|
4
4
|
export { P as PermissionOf, f as Policy, R as RoleOf, k as SessionOrganization, m as SessionUser, p as can, r as definePolicy } from './system-DhRr_WxR.js';
|
|
5
5
|
import './theme.js';
|
package/dist/svelte.js
CHANGED
|
@@ -26,6 +26,82 @@ __export(svelte_exports, {
|
|
|
26
26
|
});
|
|
27
27
|
module.exports = __toCommonJS(svelte_exports);
|
|
28
28
|
|
|
29
|
+
// src/passkey.ts
|
|
30
|
+
function canUsePasskeys() {
|
|
31
|
+
return typeof window !== "undefined" && typeof window.PublicKeyCredential === "function" && typeof navigator !== "undefined" && typeof navigator.credentials?.get === "function";
|
|
32
|
+
}
|
|
33
|
+
async function canAutofillPasskeys() {
|
|
34
|
+
if (!canUsePasskeys()) return false;
|
|
35
|
+
const pkc = window.PublicKeyCredential;
|
|
36
|
+
try {
|
|
37
|
+
return typeof pkc.isConditionalMediationAvailable === "function" && await pkc.isConditionalMediationAvailable() === true;
|
|
38
|
+
} catch {
|
|
39
|
+
return false;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
function bytes(text2) {
|
|
43
|
+
const b64 = text2.replace(/-/g, "+").replace(/_/g, "/").padEnd(Math.ceil(text2.length / 4) * 4, "=");
|
|
44
|
+
const bin = atob(b64);
|
|
45
|
+
const out = new Uint8Array(bin.length);
|
|
46
|
+
for (let i = 0; i < bin.length; i++) out[i] = bin.charCodeAt(i);
|
|
47
|
+
return out.buffer;
|
|
48
|
+
}
|
|
49
|
+
function text(buf) {
|
|
50
|
+
if (!buf) return null;
|
|
51
|
+
const view = new Uint8Array(buf);
|
|
52
|
+
let bin = "";
|
|
53
|
+
for (let i = 0; i < view.byteLength; i++) bin += String.fromCharCode(view[i]);
|
|
54
|
+
return btoa(bin).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
|
|
55
|
+
}
|
|
56
|
+
var withId = (list) => (list ?? []).map((c) => ({ ...c, id: bytes(c.id) }));
|
|
57
|
+
async function getPasskey(options, how = {}) {
|
|
58
|
+
const publicKey = { ...options, challenge: bytes(options.challenge), allowCredentials: withId(options.allowCredentials) };
|
|
59
|
+
const cred = await navigator.credentials.get({ publicKey, ...how });
|
|
60
|
+
if (cred === null) throw new DOMException("No passkey was chosen.", "NotAllowedError");
|
|
61
|
+
const r = cred.response;
|
|
62
|
+
return {
|
|
63
|
+
id: cred.id,
|
|
64
|
+
rawId: text(cred.rawId),
|
|
65
|
+
type: cred.type,
|
|
66
|
+
response: {
|
|
67
|
+
authenticatorData: text(r.authenticatorData),
|
|
68
|
+
clientDataJSON: text(r.clientDataJSON),
|
|
69
|
+
signature: text(r.signature),
|
|
70
|
+
userHandle: text(r.userHandle)
|
|
71
|
+
},
|
|
72
|
+
clientExtensionResults: cred.getClientExtensionResults?.() ?? {}
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
async function makePasskey(options) {
|
|
76
|
+
const publicKey = {
|
|
77
|
+
...options,
|
|
78
|
+
challenge: bytes(options.challenge),
|
|
79
|
+
user: { ...options.user, id: bytes(options.user.id) },
|
|
80
|
+
excludeCredentials: withId(options.excludeCredentials)
|
|
81
|
+
};
|
|
82
|
+
const cred = await navigator.credentials.create({ publicKey });
|
|
83
|
+
if (cred === null) throw new DOMException("No passkey was made.", "NotAllowedError");
|
|
84
|
+
const r = cred.response;
|
|
85
|
+
return {
|
|
86
|
+
id: cred.id,
|
|
87
|
+
rawId: text(cred.rawId),
|
|
88
|
+
type: cred.type,
|
|
89
|
+
response: {
|
|
90
|
+
clientDataJSON: text(r.clientDataJSON),
|
|
91
|
+
attestationObject: text(r.attestationObject),
|
|
92
|
+
transports: typeof r.getTransports === "function" ? r.getTransports() : []
|
|
93
|
+
},
|
|
94
|
+
clientExtensionResults: cred.getClientExtensionResults?.() ?? {}
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
function ceremonyError(e) {
|
|
98
|
+
const name = e instanceof Error || e instanceof DOMException ? e.name : "";
|
|
99
|
+
if (name === "NotAllowedError" || name === "AbortError") return { code: "cancelled", message: "The passkey was not used. Try again when you are ready." };
|
|
100
|
+
if (name === "InvalidStateError") return { code: "passkey_exists", message: "This device already has a passkey for this account." };
|
|
101
|
+
if (name === "SecurityError") return { code: "passkey_unavailable", message: "Passkeys cannot be used on this page." };
|
|
102
|
+
return { code: "passkey_failed", message: "The passkey could not be used. Try again, or sign in another way." };
|
|
103
|
+
}
|
|
104
|
+
|
|
29
105
|
// src/theme.ts
|
|
30
106
|
function fromWire(wire) {
|
|
31
107
|
const names = {
|
|
@@ -100,6 +176,24 @@ function firstProblem(...checks) {
|
|
|
100
176
|
}
|
|
101
177
|
|
|
102
178
|
// src/client.ts
|
|
179
|
+
var googleScript = null;
|
|
180
|
+
function loadGoogleIdentity() {
|
|
181
|
+
const ready = () => window.google?.accounts?.id;
|
|
182
|
+
if (ready()) return Promise.resolve(ready());
|
|
183
|
+
googleScript ??= new Promise((resolve, reject) => {
|
|
184
|
+
const script = document.createElement("script");
|
|
185
|
+
script.src = "https://accounts.google.com/gsi/client";
|
|
186
|
+
script.async = true;
|
|
187
|
+
script.onload = () => ready() ? resolve(ready()) : reject(new Error("google identity services did not load"));
|
|
188
|
+
script.onerror = () => {
|
|
189
|
+
googleScript = null;
|
|
190
|
+
reject(new Error("google identity services did not load"));
|
|
191
|
+
};
|
|
192
|
+
document.head.appendChild(script);
|
|
193
|
+
});
|
|
194
|
+
return googleScript;
|
|
195
|
+
}
|
|
196
|
+
var PASSKEY_SECONDS = 300;
|
|
103
197
|
var UNAVAILABLE = { code: "unavailable", message: "Sign-in is unavailable right now. Try again in a moment." };
|
|
104
198
|
function createAuthClient(options = {}) {
|
|
105
199
|
const base = (options.basePath ?? "/api/auth").replace(/\/+$/, "");
|
|
@@ -142,6 +236,28 @@ function createAuthClient(options = {}) {
|
|
|
142
236
|
if (o.locale) q.set("locale", o.locale);
|
|
143
237
|
return `${base}/login?${q}`;
|
|
144
238
|
};
|
|
239
|
+
async function methods() {
|
|
240
|
+
try {
|
|
241
|
+
const res = await doFetch(`${base}/methods`, { credentials: "same-origin", headers: { accept: "application/json" } });
|
|
242
|
+
if (res.ok) {
|
|
243
|
+
const m = await res.json();
|
|
244
|
+
return { providers: [], signUp: true, magicLink: false, passkey: false, ...m, passkeyHere: m.passkeyHere === true, passkeyOnCauthy: m.passkeyOnCauthy ?? m.passkey === true };
|
|
245
|
+
}
|
|
246
|
+
} catch {
|
|
247
|
+
}
|
|
248
|
+
return { providers: [], signUp: true, magicLink: false, passkey: false, passkeyHere: false, passkeyOnCauthy: false };
|
|
249
|
+
}
|
|
250
|
+
async function ceremony(begin, finish, form, run, more = {}) {
|
|
251
|
+
const begun = await post(begin, form, null);
|
|
252
|
+
if (!begun.ok) return begun;
|
|
253
|
+
let credential;
|
|
254
|
+
try {
|
|
255
|
+
credential = await run(begun.options);
|
|
256
|
+
} catch (e) {
|
|
257
|
+
return { ok: false, error: ceremonyError(e) };
|
|
258
|
+
}
|
|
259
|
+
return post(finish, { credential, ...more }, null);
|
|
260
|
+
}
|
|
145
261
|
return {
|
|
146
262
|
signIn: {
|
|
147
263
|
email: (f) => post("/sign-in/email", { email: f.email, password: f.password }, firstProblem(checkEmail(f.email), checkPassword(f.password))),
|
|
@@ -150,8 +266,76 @@ function createAuthClient(options = {}) {
|
|
|
150
266
|
magicLinkVerify: (f) => post("/sign-in/magic-link/verify", { token: f.token }, null),
|
|
151
267
|
emailCode: (f) => post("/sign-in/email-code", { code: f.code }, checkCode(f.code)),
|
|
152
268
|
passkeyUrl,
|
|
153
|
-
passkey(o) {
|
|
269
|
+
async passkey(o) {
|
|
270
|
+
const m = await methods();
|
|
271
|
+
if (m.passkeyHere && canUsePasskeys()) {
|
|
272
|
+
const r = await ceremony("/sign-in/passkey/begin", "/sign-in/passkey/finish", {}, getPasskey);
|
|
273
|
+
if (r.ok || r.error.code !== "passkey_unavailable" || !m.passkeyOnCauthy) return r;
|
|
274
|
+
} else if (!m.passkeyOnCauthy) {
|
|
275
|
+
return { ok: false, error: { code: "passkey_unavailable", message: "Passkeys cannot be used here." } };
|
|
276
|
+
}
|
|
154
277
|
window.location.assign(passkeyUrl(o));
|
|
278
|
+
return { ok: true, status: "redirected" };
|
|
279
|
+
},
|
|
280
|
+
async passkeyAutofill(o = {}) {
|
|
281
|
+
const cancelled = { ok: false, error: { code: "cancelled", message: "No passkey was chosen." } };
|
|
282
|
+
const m = await methods();
|
|
283
|
+
if (!m.passkeyHere || !await canAutofillPasskeys()) return cancelled;
|
|
284
|
+
for (; ; ) {
|
|
285
|
+
if (o.signal?.aborted) return cancelled;
|
|
286
|
+
const begun = await post("/sign-in/passkey/begin", {}, null);
|
|
287
|
+
if (!begun.ok) return begun;
|
|
288
|
+
const renew = new AbortController();
|
|
289
|
+
const stop = () => renew.abort();
|
|
290
|
+
o.signal?.addEventListener("abort", stop, { once: true });
|
|
291
|
+
const timer = setTimeout(stop, (PASSKEY_SECONDS - 30) * 1e3);
|
|
292
|
+
let credential;
|
|
293
|
+
try {
|
|
294
|
+
credential = await getPasskey(begun.options, { mediation: "conditional", signal: renew.signal });
|
|
295
|
+
} catch (e) {
|
|
296
|
+
if (o.signal?.aborted) return cancelled;
|
|
297
|
+
if (renew.signal.aborted) continue;
|
|
298
|
+
return { ok: false, error: ceremonyError(e) };
|
|
299
|
+
} finally {
|
|
300
|
+
clearTimeout(timer);
|
|
301
|
+
o.signal?.removeEventListener("abort", stop);
|
|
302
|
+
}
|
|
303
|
+
return post("/sign-in/passkey/finish", { credential }, null);
|
|
304
|
+
}
|
|
305
|
+
},
|
|
306
|
+
async googleOneTap(o = {}) {
|
|
307
|
+
const cancelled = { ok: false, error: { code: "cancelled", message: "No Google account was chosen." } };
|
|
308
|
+
if (typeof window === "undefined" || o.signal?.aborted) return cancelled;
|
|
309
|
+
const begun = await post("/sign-in/google-one-tap/begin", {}, null);
|
|
310
|
+
if (!begun.ok) return begun;
|
|
311
|
+
let gis;
|
|
312
|
+
try {
|
|
313
|
+
gis = await loadGoogleIdentity();
|
|
314
|
+
} catch {
|
|
315
|
+
return { ok: false, error: { code: "unavailable", message: "Google could not be reached. Try again, or sign in another way." } };
|
|
316
|
+
}
|
|
317
|
+
const credential = await new Promise((done) => {
|
|
318
|
+
o.signal?.addEventListener("abort", () => {
|
|
319
|
+
gis.cancel();
|
|
320
|
+
done(null);
|
|
321
|
+
}, { once: true });
|
|
322
|
+
gis.initialize({
|
|
323
|
+
client_id: begun.clientId,
|
|
324
|
+
nonce: begun.nonce,
|
|
325
|
+
context: o.context ?? "signin",
|
|
326
|
+
callback: (r) => done(r.credential ?? null),
|
|
327
|
+
auto_select: false,
|
|
328
|
+
cancel_on_tap_outside: true,
|
|
329
|
+
itp_support: true,
|
|
330
|
+
use_fedcm_for_prompt: true
|
|
331
|
+
});
|
|
332
|
+
gis.prompt((moment) => {
|
|
333
|
+
if (moment.isNotDisplayed?.() || moment.isSkippedMoment?.()) done(null);
|
|
334
|
+
else if (moment.isDismissedMoment?.() && moment.getDismissedReason?.() !== "credential_returned") done(null);
|
|
335
|
+
});
|
|
336
|
+
});
|
|
337
|
+
if (credential === null) return cancelled;
|
|
338
|
+
return post("/sign-in/google-one-tap", { credential }, null);
|
|
155
339
|
},
|
|
156
340
|
socialUrl,
|
|
157
341
|
social(provider, o) {
|
|
@@ -195,7 +379,16 @@ function createAuthClient(options = {}) {
|
|
|
195
379
|
const body = await read("/me/sessions");
|
|
196
380
|
return body && Array.isArray(body.sessions) ? body.sessions : [];
|
|
197
381
|
},
|
|
198
|
-
signOutEverywhere: () => post("/me/sessions/end", {}, null)
|
|
382
|
+
signOutEverywhere: () => post("/me/sessions/end", {}, null),
|
|
383
|
+
async passkeys() {
|
|
384
|
+
const body = await read("/me/passkeys");
|
|
385
|
+
return body && Array.isArray(body.passkeys) ? body.passkeys : [];
|
|
386
|
+
},
|
|
387
|
+
addPasskey(f = {}) {
|
|
388
|
+
if (!canUsePasskeys()) return Promise.resolve({ ok: false, error: { code: "passkey_unavailable", message: "This browser cannot make passkeys." } });
|
|
389
|
+
return ceremony("/me/passkeys/add/begin", "/me/passkeys/add/finish", { password: f.password ?? "" }, makePasskey, { name: f.name ?? "" });
|
|
390
|
+
},
|
|
391
|
+
removePasskey: (id) => post("/me/passkeys/remove", { id }, null)
|
|
199
392
|
},
|
|
200
393
|
async getSession() {
|
|
201
394
|
try {
|
|
@@ -210,14 +403,7 @@ function createAuthClient(options = {}) {
|
|
|
210
403
|
looked ??= read("/look").then((wire) => wire && typeof wire === "object" ? fromWire(wire) : null);
|
|
211
404
|
return looked;
|
|
212
405
|
},
|
|
213
|
-
|
|
214
|
-
try {
|
|
215
|
-
const res = await doFetch(`${base}/methods`, { credentials: "same-origin", headers: { accept: "application/json" } });
|
|
216
|
-
if (res.ok) return await res.json();
|
|
217
|
-
} catch {
|
|
218
|
-
}
|
|
219
|
-
return { providers: [], signUp: true, magicLink: false, passkey: false };
|
|
220
|
-
}
|
|
406
|
+
methods
|
|
221
407
|
};
|
|
222
408
|
}
|
|
223
409
|
|
|
@@ -313,7 +499,8 @@ function createSessionStore(options = {}) {
|
|
|
313
499
|
email: (f) => then(client.signIn.email(f)),
|
|
314
500
|
mfa: (f) => then(client.signIn.mfa(f)),
|
|
315
501
|
magicLinkVerify: (f) => then(client.signIn.magicLinkVerify(f)),
|
|
316
|
-
emailCode: (f) => then(client.signIn.emailCode(f))
|
|
502
|
+
emailCode: (f) => then(client.signIn.emailCode(f)),
|
|
503
|
+
passkey: (o) => then(client.signIn.passkey(o))
|
|
317
504
|
},
|
|
318
505
|
signUp: { email: (f) => then(client.signUp.email(f)) },
|
|
319
506
|
signOut: () => then(client.signOut()),
|