cauthy-auth 0.10.0 → 0.11.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/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,73 @@ __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
+ function bytes(text2) {
34
+ const b64 = text2.replace(/-/g, "+").replace(/_/g, "/").padEnd(Math.ceil(text2.length / 4) * 4, "=");
35
+ const bin = atob(b64);
36
+ const out = new Uint8Array(bin.length);
37
+ for (let i = 0; i < bin.length; i++) out[i] = bin.charCodeAt(i);
38
+ return out.buffer;
39
+ }
40
+ function text(buf) {
41
+ if (!buf) return null;
42
+ const view = new Uint8Array(buf);
43
+ let bin = "";
44
+ for (let i = 0; i < view.byteLength; i++) bin += String.fromCharCode(view[i]);
45
+ return btoa(bin).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
46
+ }
47
+ var withId = (list) => (list ?? []).map((c) => ({ ...c, id: bytes(c.id) }));
48
+ async function getPasskey(options) {
49
+ const publicKey = { ...options, challenge: bytes(options.challenge), allowCredentials: withId(options.allowCredentials) };
50
+ const cred = await navigator.credentials.get({ publicKey });
51
+ if (cred === null) throw new DOMException("No passkey was chosen.", "NotAllowedError");
52
+ const r = cred.response;
53
+ return {
54
+ id: cred.id,
55
+ rawId: text(cred.rawId),
56
+ type: cred.type,
57
+ response: {
58
+ authenticatorData: text(r.authenticatorData),
59
+ clientDataJSON: text(r.clientDataJSON),
60
+ signature: text(r.signature),
61
+ userHandle: text(r.userHandle)
62
+ },
63
+ clientExtensionResults: cred.getClientExtensionResults?.() ?? {}
64
+ };
65
+ }
66
+ async function makePasskey(options) {
67
+ const publicKey = {
68
+ ...options,
69
+ challenge: bytes(options.challenge),
70
+ user: { ...options.user, id: bytes(options.user.id) },
71
+ excludeCredentials: withId(options.excludeCredentials)
72
+ };
73
+ const cred = await navigator.credentials.create({ publicKey });
74
+ if (cred === null) throw new DOMException("No passkey was made.", "NotAllowedError");
75
+ const r = cred.response;
76
+ return {
77
+ id: cred.id,
78
+ rawId: text(cred.rawId),
79
+ type: cred.type,
80
+ response: {
81
+ clientDataJSON: text(r.clientDataJSON),
82
+ attestationObject: text(r.attestationObject),
83
+ transports: typeof r.getTransports === "function" ? r.getTransports() : []
84
+ },
85
+ clientExtensionResults: cred.getClientExtensionResults?.() ?? {}
86
+ };
87
+ }
88
+ function ceremonyError(e) {
89
+ const name = e instanceof Error || e instanceof DOMException ? e.name : "";
90
+ if (name === "NotAllowedError" || name === "AbortError") return { code: "cancelled", message: "The passkey was not used. Try again when you are ready." };
91
+ if (name === "InvalidStateError") return { code: "passkey_exists", message: "This device already has a passkey for this account." };
92
+ if (name === "SecurityError") return { code: "passkey_unavailable", message: "Passkeys cannot be used on this page." };
93
+ return { code: "passkey_failed", message: "The passkey could not be used. Try again, or sign in another way." };
94
+ }
95
+
29
96
  // src/theme.ts
30
97
  function fromWire(wire) {
31
98
  const names = {
@@ -142,6 +209,28 @@ function createAuthClient(options = {}) {
142
209
  if (o.locale) q.set("locale", o.locale);
143
210
  return `${base}/login?${q}`;
144
211
  };
212
+ async function methods() {
213
+ try {
214
+ const res = await doFetch(`${base}/methods`, { credentials: "same-origin", headers: { accept: "application/json" } });
215
+ if (res.ok) {
216
+ const m = await res.json();
217
+ return { providers: [], signUp: true, magicLink: false, passkey: false, ...m, passkeyHere: m.passkeyHere === true, passkeyOnCauthy: m.passkeyOnCauthy ?? m.passkey === true };
218
+ }
219
+ } catch {
220
+ }
221
+ return { providers: [], signUp: true, magicLink: false, passkey: false, passkeyHere: false, passkeyOnCauthy: false };
222
+ }
223
+ async function ceremony(begin, finish, form, run, more = {}) {
224
+ const begun = await post(begin, form, null);
225
+ if (!begun.ok) return begun;
226
+ let credential;
227
+ try {
228
+ credential = await run(begun.options);
229
+ } catch (e) {
230
+ return { ok: false, error: ceremonyError(e) };
231
+ }
232
+ return post(finish, { credential, ...more }, null);
233
+ }
145
234
  return {
146
235
  signIn: {
147
236
  email: (f) => post("/sign-in/email", { email: f.email, password: f.password }, firstProblem(checkEmail(f.email), checkPassword(f.password))),
@@ -150,8 +239,16 @@ function createAuthClient(options = {}) {
150
239
  magicLinkVerify: (f) => post("/sign-in/magic-link/verify", { token: f.token }, null),
151
240
  emailCode: (f) => post("/sign-in/email-code", { code: f.code }, checkCode(f.code)),
152
241
  passkeyUrl,
153
- passkey(o) {
242
+ async passkey(o) {
243
+ const m = await methods();
244
+ if (m.passkeyHere && canUsePasskeys()) {
245
+ const r = await ceremony("/sign-in/passkey/begin", "/sign-in/passkey/finish", {}, getPasskey);
246
+ if (r.ok || r.error.code !== "passkey_unavailable" || !m.passkeyOnCauthy) return r;
247
+ } else if (!m.passkeyOnCauthy) {
248
+ return { ok: false, error: { code: "passkey_unavailable", message: "Passkeys cannot be used here." } };
249
+ }
154
250
  window.location.assign(passkeyUrl(o));
251
+ return { ok: true, status: "redirected" };
155
252
  },
156
253
  socialUrl,
157
254
  social(provider, o) {
@@ -195,7 +292,16 @@ function createAuthClient(options = {}) {
195
292
  const body = await read("/me/sessions");
196
293
  return body && Array.isArray(body.sessions) ? body.sessions : [];
197
294
  },
198
- signOutEverywhere: () => post("/me/sessions/end", {}, null)
295
+ signOutEverywhere: () => post("/me/sessions/end", {}, null),
296
+ async passkeys() {
297
+ const body = await read("/me/passkeys");
298
+ return body && Array.isArray(body.passkeys) ? body.passkeys : [];
299
+ },
300
+ addPasskey(f = {}) {
301
+ if (!canUsePasskeys()) return Promise.resolve({ ok: false, error: { code: "passkey_unavailable", message: "This browser cannot make passkeys." } });
302
+ return ceremony("/me/passkeys/add/begin", "/me/passkeys/add/finish", { password: f.password ?? "" }, makePasskey, { name: f.name ?? "" });
303
+ },
304
+ removePasskey: (id) => post("/me/passkeys/remove", { id }, null)
199
305
  },
200
306
  async getSession() {
201
307
  try {
@@ -210,14 +316,7 @@ function createAuthClient(options = {}) {
210
316
  looked ??= read("/look").then((wire) => wire && typeof wire === "object" ? fromWire(wire) : null);
211
317
  return looked;
212
318
  },
213
- async methods() {
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
- }
319
+ methods
221
320
  };
222
321
  }
223
322
 
@@ -313,7 +412,8 @@ function createSessionStore(options = {}) {
313
412
  email: (f) => then(client.signIn.email(f)),
314
413
  mfa: (f) => then(client.signIn.mfa(f)),
315
414
  magicLinkVerify: (f) => then(client.signIn.magicLinkVerify(f)),
316
- emailCode: (f) => then(client.signIn.emailCode(f))
415
+ emailCode: (f) => then(client.signIn.emailCode(f)),
416
+ passkey: (o) => then(client.signIn.passkey(o))
317
417
  },
318
418
  signUp: { email: (f) => then(client.signUp.email(f)) },
319
419
  signOut: () => then(client.signOut()),