@voltro/plugin-auth 0.86.0 → 0.88.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.
Files changed (55) hide show
  1. package/CHANGELOG.md +5484 -0
  2. package/README.md +83 -0
  3. package/THIRD-PARTY-NOTICES.md +3 -227
  4. package/dist/admission.js +1 -2
  5. package/dist/appUserStore.js +1 -2
  6. package/dist/authExtensions.js +8 -0
  7. package/dist/csrf.js +1 -2
  8. package/dist/emailVerification.js +14 -13
  9. package/dist/guest.d.ts +99 -0
  10. package/dist/guest.js +3 -0
  11. package/dist/guestClient.js +236 -0
  12. package/dist/guestCredential.js +11 -0
  13. package/dist/guestPasswordUpgrade.js +84 -0
  14. package/dist/guestPlugin.js +165 -0
  15. package/dist/guestSchema.js +19 -0
  16. package/dist/guestSecrets.js +9 -0
  17. package/dist/guestService.js +223 -0
  18. package/dist/handlers.js +114 -115
  19. package/dist/impersonation.js +80 -80
  20. package/dist/index.d.ts +265 -15
  21. package/dist/index.js +27 -19
  22. package/dist/invitations.js +24 -25
  23. package/dist/mail.js +1 -2
  24. package/dist/memberships.js +1 -2
  25. package/dist/native/guestStorage.js +40 -0
  26. package/dist/native.d.ts +110 -0
  27. package/dist/native.js +46 -45
  28. package/dist/nativeRoutes.js +62 -74
  29. package/dist/overlayPlugin.js +166 -0
  30. package/dist/passkeyHandlers.js +1 -2
  31. package/dist/password.js +1 -2
  32. package/dist/plugin.d.ts +119 -10
  33. package/dist/plugin.js +100 -80
  34. package/dist/postgresUserStore.js +20 -9
  35. package/dist/revocation.js +41 -25
  36. package/dist/schema.d.ts +41 -1
  37. package/dist/schema.js +100 -100
  38. package/dist/session.js +1 -2
  39. package/dist/sessionFamily.js +14 -0
  40. package/dist/sessionRegistry.js +94 -80
  41. package/dist/signedClaims.js +23 -0
  42. package/dist/strategy.d.ts +13 -9
  43. package/dist/strategy.js +27 -24
  44. package/dist/subjectGuard.js +1 -2
  45. package/dist/tokens.js +1 -2
  46. package/dist/totp.js +1 -2
  47. package/dist/userStore.js +146 -140
  48. package/dist/web/guestStorage.js +46 -0
  49. package/dist/web/passkeyClient.js +1 -2
  50. package/dist/web/requireAuth.js +1 -2
  51. package/dist/web/subjectContext.js +1 -2
  52. package/dist/web.d.ts +94 -0
  53. package/dist/web.js +4 -2
  54. package/dist/webauthn.js +1 -2
  55. package/package.json +9 -4
@@ -1,5 +1,5 @@
1
1
  import { Effect as e } from "effect";
2
- //#region src/subjectGuard.ts
2
+ // src/subjectGuard.ts
3
3
  var t = { ok: !0 }, n = (n, r) => e.gen(function* () {
4
4
  for (let e of n) {
5
5
  let t = yield* e(r);
@@ -7,5 +7,4 @@ var t = { ok: !0 }, n = (n, r) => e.gen(function* () {
7
7
  }
8
8
  return t;
9
9
  });
10
- //#endregion
11
10
  export { n as runSubjectGuards };
package/dist/tokens.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { createHash as e, randomBytes as t } from "node:crypto";
2
- //#region src/tokens.ts
2
+ // src/tokens.ts
3
3
  var n = 900, r = 3600, i = 86400, a = 300, o = 300, s = {
4
4
  "magic-link": 900,
5
5
  "password-reset": r,
@@ -17,5 +17,4 @@ var n = 900, r = 3600, i = 86400, a = 300, o = 300, s = {
17
17
  ...u(t ?? s[e]),
18
18
  purpose: e
19
19
  });
20
- //#endregion
21
20
  export { i as EMAIL_VERIFY_TTL_S, n as MAGIC_LINK_TTL_S, a as MFA_PENDING_TTL_S, o as NATIVE_CODE_TTL_S, r as PASSWORD_RESET_TTL_S, l as hashToken, u as mintSecret, d as mintToken };
package/dist/totp.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { createHmac as e, randomBytes as t, timingSafeEqual as n } from "node:crypto";
2
2
  import { Data as r, Effect as i } from "effect";
3
- //#region src/totp.ts
3
+ // src/totp.ts
4
4
  var a = 30, o = 6, s = 20, c = "sha1", l = 1, u = class extends r.TaggedError("TotpVerifyError") {}, d = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567", f = (e) => {
5
5
  let t = 0, n = 0, r = "";
6
6
  for (let i of e) for (n = n << 8 | i, t += 8; t >= 5;) r += d[n >>> t - 5 & 31], t -= 5;
@@ -51,5 +51,4 @@ var a = 30, o = 6, s = 20, c = "sha1", l = 1, u = class extends r.TaggedError("T
51
51
  }
52
52
  return n;
53
53
  };
54
- //#endregion
55
54
  export { u as TotpVerifyError, b as generateRecoveryCodes, v as generateTotpCode, g as generateTotpSecret, _ as otpauthUrl, y as verifyTotpCode };
package/dist/userStore.js CHANGED
@@ -1,71 +1,75 @@
1
- import { Data as e, Effect as t } from "effect";
2
- import { generateId as n } from "@voltro/database";
3
- //#region src/userStore.ts
4
- var r = class extends e.TaggedError("UserAlreadyExistsError") {}, i = class extends e.TaggedError("UserNotFoundError") {}, a = (e) => n({
1
+ import { hasOwnedSessionFamily as e, indexSessionFamilies as t, sessionFamilyId as n } from "./sessionFamily.js";
2
+ import { Data as r, Effect as i } from "effect";
3
+ import { generateId as a } from "@voltro/database";
4
+ // src/userStore.ts
5
+ var o = class extends r.TaggedError("UserAlreadyExistsError") {}, s = class extends r.TaggedError("UserNotFoundError") {}, c = (e) => a({
5
6
  kind: "typeid",
6
7
  prefix: e
7
- }, e), o = (e = []) => {
8
- let n = /* @__PURE__ */ new Map(), o = /* @__PURE__ */ new Map(), s = [], c = [], l = [], u = [], d = [], f = [], p = [], m = /* @__PURE__ */ new Map();
9
- for (let t of e) n.set(t.id, t), o.set(t.email.toLowerCase(), t);
10
- let h = (e, r) => t.gen(function* () {
11
- let a = n.get(e);
12
- if (!a) return yield* t.fail(new i({ userId: e }));
13
- let s = r(a);
14
- return n.set(e, s), o.set(s.email.toLowerCase(), s), s;
8
+ }, e), l = (r = []) => {
9
+ let a = /* @__PURE__ */ new Map(), l = /* @__PURE__ */ new Map(), u = [], d = [], f = [], p = [], m = [], h = [], g = [], _ = /* @__PURE__ */ new Map();
10
+ for (let e of r) a.set(e.id, e), e.email !== null && l.set(e.email.toLowerCase(), e);
11
+ let v = (e, t) => i.gen(function* () {
12
+ let n = a.get(e);
13
+ if (!n) return yield* i.fail(new s({ userId: e }));
14
+ let r = t(n);
15
+ return a.set(e, r), r.email !== null && l.set(r.email.toLowerCase(), r), r;
15
16
  });
16
17
  return {
17
- findByEmail: (e) => t.sync(() => o.get(e.toLowerCase()) ?? null),
18
- findById: (e) => t.sync(() => n.get(e) ?? null),
19
- insert: (e) => t.gen(function* () {
20
- let i = e.email.toLowerCase();
21
- if (o.has(i)) return yield* t.fail(new r({ email: i }));
22
- let a = {
18
+ findByEmail: (e) => i.sync(() => l.get(e.toLowerCase()) ?? null),
19
+ findById: (e) => i.sync(() => a.get(e) ?? null),
20
+ insert: (e) => i.gen(function* () {
21
+ let t = e.email === null ? null : e.email.toLowerCase();
22
+ if (t !== null && l.has(t)) return yield* i.fail(new o({ email: t }));
23
+ let n = {
23
24
  ...e,
24
- email: i,
25
+ email: t,
25
26
  createdAt: /* @__PURE__ */ new Date()
26
27
  };
27
- return n.set(a.id, a), o.set(i, a), a;
28
+ return a.set(n.id, n), t !== null && l.set(t, n), n;
28
29
  }),
29
- updatePassword: (e, t) => h(e, (e) => ({
30
+ updatePassword: (e, t) => v(e, (e) => ({
30
31
  ...e,
31
32
  passwordHash: t
32
33
  })),
33
- setMfaSecret: (e, t) => h(e, (e) => ({
34
+ setMfaSecret: (e, t) => v(e, (e) => ({
34
35
  ...e,
35
36
  mfaSecret: t,
36
37
  mfaEnrolledAt: null
37
38
  })),
38
- markMfaEnrolled: (e) => h(e, (e) => ({
39
+ markMfaEnrolled: (e) => v(e, (e) => ({
39
40
  ...e,
40
41
  mfaEnrolledAt: /* @__PURE__ */ new Date()
41
42
  })),
42
- clearMfa: (e) => h(e, (e) => ({
43
+ clearMfa: (e) => v(e, (e) => ({
43
44
  ...e,
44
45
  mfaSecret: null,
45
46
  mfaEnrolledAt: null
46
47
  })),
47
- markEmailVerified: (e) => h(e, (e) => e.emailVerifiedAt ? e : {
48
+ markEmailVerified: (e) => v(e, (e) => e.emailVerifiedAt ? e : {
48
49
  ...e,
49
50
  emailVerifiedAt: /* @__PURE__ */ new Date()
50
51
  }),
51
- listMemberships: (e) => t.sync(() => s.filter((t) => t.userId === e)),
52
- addMembership: (e) => t.sync(() => {
53
- let t = s.findIndex((t) => t.userId === e.userId && t.tenantId === e.tenantId), n = {
52
+ listMemberships: (e) => i.sync(() => u.filter((t) => t.userId === e)),
53
+ addMembership: (e) => i.sync(() => {
54
+ let t = u.findIndex((t) => t.userId === e.userId && t.tenantId === e.tenantId), n = {
54
55
  ...e,
55
56
  joinedAt: /* @__PURE__ */ new Date()
56
57
  };
57
58
  if (t >= 0) {
58
- let e = s[t], r = {
59
+ let e = u[t], r = {
59
60
  ...n,
60
61
  joinedAt: e.joinedAt
61
62
  };
62
- return s[t] = r, r;
63
+ return u[t] = r, r;
63
64
  }
64
- return s.push(n), n;
65
+ return u.push(n), n;
66
+ }),
67
+ membershipRole: (e, t) => i.sync(() => u.find((n) => n.userId === e && n.tenantId === t)?.role ?? null),
68
+ listSessions: (r) => i.sync(() => {
69
+ let i = t(d);
70
+ return d.filter((t) => t.userId === r && t.rotatedAt == null && i.get(t.id) === t && e(t, i.get(n(t) ?? ""))).map((e) => structuredClone(e)).sort((e, t) => t.createdAt.getTime() - e.createdAt.getTime());
65
71
  }),
66
- membershipRole: (e, n) => t.sync(() => s.find((t) => t.userId === e && t.tenantId === n)?.role ?? null),
67
- listSessions: (e) => t.sync(() => c.filter((t) => t.userId === e && t.rotatedAt == null).sort((e, t) => t.createdAt.getTime() - e.createdAt.getTime())),
68
- insertSession: (e) => t.sync(() => {
72
+ insertSession: (e) => i.sync(() => {
69
73
  let t = /* @__PURE__ */ new Date(), n = {
70
74
  ...e,
71
75
  familyId: e.familyId ?? e.id,
@@ -73,73 +77,76 @@ var r = class extends e.TaggedError("UserAlreadyExistsError") {}, i = class exte
73
77
  createdAt: t,
74
78
  lastSeenAt: t
75
79
  };
76
- return c.push(n), n;
80
+ return d.push(structuredClone(n)), structuredClone(n);
77
81
  }),
78
- findSession: (e) => t.sync(() => c.find((t) => t.id === e && t.rotatedAt == null) ?? null),
79
- touchSession: (e, n) => t.sync(() => {
80
- let t = c.findIndex((t) => t.id === e);
81
- if (t >= 0 && c[t].rotatedAt == null) {
82
- c[t] = {
83
- ...c[t],
84
- expiresAt: n,
82
+ findSession: (r) => i.sync(() => {
83
+ let i = t(d), a = i.get(r);
84
+ return a && a.rotatedAt == null && e(a, i.get(n(a) ?? "")) ? structuredClone(a) : null;
85
+ }),
86
+ touchSession: (e, t) => i.sync(() => {
87
+ let n = d.findIndex((t) => t.id === e);
88
+ if (n >= 0 && d[n].rotatedAt == null) {
89
+ d[n] = {
90
+ ...d[n],
91
+ expiresAt: t,
85
92
  lastSeenAt: /* @__PURE__ */ new Date()
86
93
  };
87
- let r = c[t].familyId ?? e, i = c.findIndex((e) => e.id === r);
88
- i >= 0 && c[i].expiresAt < n && (c[i] = {
89
- ...c[i],
90
- expiresAt: n
94
+ let r = d[n].familyId ?? e, i = d.findIndex((e) => e.id === r);
95
+ i >= 0 && d[i].expiresAt < t && (d[i] = {
96
+ ...d[i],
97
+ expiresAt: t
91
98
  });
92
99
  }
93
100
  }),
94
- revokeSession: (e, n) => t.sync(() => {
95
- let t = c.find((t) => t.id === n && t.userId === e);
96
- if (!t) return !1;
97
- let r = t.familyId ?? t.id, i = !1;
98
- for (let t = c.length - 1; t >= 0; t--) {
99
- let n = c[t];
100
- n.userId === e && (n.familyId ?? n.id) === r && (c.splice(t, 1), i = !0);
101
+ revokeSession: (e, t) => i.sync(() => {
102
+ let n = d.find((n) => n.id === t && n.userId === e);
103
+ if (!n) return !1;
104
+ let r = n.familyId ?? n.id, i = !1;
105
+ for (let t = d.length - 1; t >= 0; t--) {
106
+ let n = d[t];
107
+ n.userId === e && (n.familyId ?? n.id) === r && (d.splice(t, 1), i = !0);
101
108
  }
102
109
  return i;
103
110
  }),
104
- revokeAllOtherSessions: (e, n) => t.sync(() => {
105
- let t = c.find((t) => t.id === n && t.userId === e), r = t ? t.familyId ?? t.id : void 0, i = 0;
106
- for (let t = c.length - 1; t >= 0; t--) {
107
- let n = c[t];
108
- n.userId === e && (n.familyId ?? n.id) !== r && (c.splice(t, 1), n.rotatedAt ?? (i += 1));
111
+ revokeAllOtherSessions: (e, t) => i.sync(() => {
112
+ let n = d.find((n) => n.id === t && n.userId === e), r = n ? n.familyId ?? n.id : void 0, i = 0;
113
+ for (let t = d.length - 1; t >= 0; t--) {
114
+ let n = d[t];
115
+ n.userId === e && (n.familyId ?? n.id) !== r && (d.splice(t, 1), n.rotatedAt ?? (i += 1));
109
116
  }
110
117
  return i;
111
118
  }),
112
- revokeAllSessions: (e) => t.sync(() => {
119
+ revokeAllSessions: (e) => i.sync(() => {
113
120
  let t = 0;
114
- for (let n = c.length - 1; n >= 0; n--) if (c[n].userId === e) {
115
- let e = c[n];
116
- c.splice(n, 1), e.rotatedAt ?? t++;
121
+ for (let n = d.length - 1; n >= 0; n--) if (d[n].userId === e) {
122
+ let e = d[n];
123
+ d.splice(n, 1), e.rotatedAt ?? t++;
117
124
  }
118
125
  return t;
119
126
  }),
120
- insertToken: (e) => t.sync(() => {
127
+ insertToken: (e) => i.sync(() => {
121
128
  let t = {
122
129
  ...e,
123
130
  consumedAt: null,
124
131
  createdAt: /* @__PURE__ */ new Date()
125
132
  };
126
- return l.push(t), t;
133
+ return f.push(t), t;
127
134
  }),
128
- consumeToken: (e, n) => t.sync(() => {
129
- let t = Date.now(), r = l.find((r) => r.tokenHash === e && r.purpose === n && r.consumedAt === null && r.expiresAt.getTime() > t);
135
+ consumeToken: (e, t) => i.sync(() => {
136
+ let n = Date.now(), r = f.find((r) => r.tokenHash === e && r.purpose === t && r.consumedAt === null && r.expiresAt.getTime() > n);
130
137
  if (!r) return null;
131
138
  let i = {
132
139
  ...r,
133
140
  consumedAt: /* @__PURE__ */ new Date()
134
- }, a = l.indexOf(r);
135
- return l[a] = i, i;
141
+ }, a = f.indexOf(r);
142
+ return f[a] = i, i;
136
143
  }),
137
- latestToken: (e, n) => t.sync(() => {
138
- let t = null;
139
- for (let r of l) r.userId === e && r.purpose === n && (t === null || r.createdAt.getTime() > t.createdAt.getTime()) && (t = r);
140
- return t;
144
+ latestToken: (e, t) => i.sync(() => {
145
+ let n = null;
146
+ for (let r of f) r.userId === e && r.purpose === t && (n === null || r.createdAt.getTime() > n.createdAt.getTime()) && (n = r);
147
+ return n;
141
148
  }),
142
- insertInvitation: (e) => t.sync(() => {
149
+ insertInvitation: (e) => i.sync(() => {
143
150
  let t = {
144
151
  ...e,
145
152
  email: e.email.toLowerCase(),
@@ -149,119 +156,119 @@ var r = class extends e.TaggedError("UserAlreadyExistsError") {}, i = class exte
149
156
  revokedByUserId: null,
150
157
  createdAt: /* @__PURE__ */ new Date()
151
158
  };
152
- return f.push(t), t;
159
+ return h.push(t), t;
153
160
  }),
154
- findInvitationByHash: (e) => t.sync(() => f.find((t) => t.tokenHash === e) ?? null),
155
- acceptInvitation: (e, n, r) => t.sync(() => {
156
- let t = f.findIndex((t) => t.tokenHash === e && t.acceptedAt === null && t.revokedAt === null && t.expiresAt.getTime() > r.getTime());
157
- if (t < 0) return null;
161
+ findInvitationByHash: (e) => i.sync(() => h.find((t) => t.tokenHash === e) ?? null),
162
+ acceptInvitation: (e, t, n) => i.sync(() => {
163
+ let r = h.findIndex((t) => t.tokenHash === e && t.acceptedAt === null && t.revokedAt === null && t.expiresAt.getTime() > n.getTime());
164
+ if (r < 0) return null;
158
165
  let i = {
159
- ...f[t],
160
- acceptedAt: r,
161
- acceptedByUserId: n
166
+ ...h[r],
167
+ acceptedAt: n,
168
+ acceptedByUserId: t
162
169
  };
163
- return f[t] = i, i;
170
+ return h[r] = i, i;
164
171
  }),
165
- revokeInvitation: (e, n, r, i) => t.sync(() => {
166
- let t = f.findIndex((t) => t.id === e && t.tenantId === n && t.acceptedAt === null && t.revokedAt === null);
167
- return t < 0 ? !1 : (f[t] = {
168
- ...f[t],
169
- revokedAt: i,
170
- revokedByUserId: r
172
+ revokeInvitation: (e, t, n, r) => i.sync(() => {
173
+ let i = h.findIndex((n) => n.id === e && n.tenantId === t && n.acceptedAt === null && n.revokedAt === null);
174
+ return i < 0 ? !1 : (h[i] = {
175
+ ...h[i],
176
+ revokedAt: r,
177
+ revokedByUserId: n
171
178
  }, !0);
172
179
  }),
173
- listInvitations: (e) => t.sync(() => f.filter((t) => t.tenantId === e).sort((e, t) => t.createdAt.getTime() - e.createdAt.getTime())),
174
- countPendingInvitations: (e, n) => t.sync(() => f.filter((t) => t.tenantId === e && t.acceptedAt === null && t.revokedAt === null && t.expiresAt.getTime() > n.getTime()).length),
175
- supersedePendingInvitations: (e, n, r, i) => t.sync(() => {
176
- let t = n.toLowerCase(), a = 0;
177
- for (let n = 0; n < f.length; n++) {
178
- let o = f[n];
179
- o.tenantId === e && o.email === t && o.acceptedAt === null && o.revokedAt === null && (f[n] = {
180
+ listInvitations: (e) => i.sync(() => h.filter((t) => t.tenantId === e).sort((e, t) => t.createdAt.getTime() - e.createdAt.getTime())),
181
+ countPendingInvitations: (e, t) => i.sync(() => h.filter((n) => n.tenantId === e && n.acceptedAt === null && n.revokedAt === null && n.expiresAt.getTime() > t.getTime()).length),
182
+ supersedePendingInvitations: (e, t, n, r) => i.sync(() => {
183
+ let i = t.toLowerCase(), a = 0;
184
+ for (let t = 0; t < h.length; t++) {
185
+ let o = h[t];
186
+ o.tenantId === e && o.email === i && o.acceptedAt === null && o.revokedAt === null && (h[t] = {
180
187
  ...o,
181
- revokedAt: i,
182
- revokedByUserId: r
188
+ revokedAt: r,
189
+ revokedByUserId: n
183
190
  }, a++);
184
191
  }
185
192
  return a;
186
193
  }),
187
- insertImpersonationGrant: (e) => t.sync(() => {
194
+ insertImpersonationGrant: (e) => i.sync(() => {
188
195
  let t = {
189
196
  ...e,
190
197
  startedAt: /* @__PURE__ */ new Date(),
191
198
  endedAt: null,
192
199
  endedReason: null
193
200
  };
194
- return p.push(t), t;
201
+ return g.push(t), t;
195
202
  }),
196
- findImpersonationGrantBySession: (e) => t.sync(() => p.find((t) => t.sessionId === e) ?? null),
197
- endImpersonationGrant: (e, n, r) => t.sync(() => {
198
- let t = p.findIndex((t) => t.sessionId === e && t.endedAt === null);
199
- return t < 0 ? !1 : (p[t] = {
200
- ...p[t],
201
- endedAt: r,
202
- endedReason: n
203
+ findImpersonationGrantBySession: (e) => i.sync(() => g.find((t) => t.sessionId === e) ?? null),
204
+ endImpersonationGrant: (e, t, n) => i.sync(() => {
205
+ let r = g.findIndex((t) => t.sessionId === e && t.endedAt === null);
206
+ return r < 0 ? !1 : (g[r] = {
207
+ ...g[r],
208
+ endedAt: n,
209
+ endedReason: t
203
210
  }, !0);
204
211
  }),
205
- replaceRecoveryCodes: (e, n) => t.sync(() => {
206
- for (let t = d.length - 1; t >= 0; t--) d[t].userId === e && d.splice(t, 1);
207
- let t = /* @__PURE__ */ new Date();
208
- for (let r of n) d.push({
209
- id: a("rc"),
212
+ replaceRecoveryCodes: (e, t) => i.sync(() => {
213
+ for (let t = m.length - 1; t >= 0; t--) m[t].userId === e && m.splice(t, 1);
214
+ let n = /* @__PURE__ */ new Date();
215
+ for (let r of t) m.push({
216
+ id: c("rc"),
210
217
  userId: e,
211
218
  codeHash: r,
212
219
  consumedAt: null,
213
- createdAt: t
220
+ createdAt: n
214
221
  });
215
222
  }),
216
- consumeRecoveryCode: (e, n) => t.sync(() => {
217
- let t = d.findIndex((t) => t.userId === e && t.codeHash === n && t.consumedAt === null);
218
- return t < 0 ? !1 : (d[t] = {
219
- ...d[t],
223
+ consumeRecoveryCode: (e, t) => i.sync(() => {
224
+ let n = m.findIndex((n) => n.userId === e && n.codeHash === t && n.consumedAt === null);
225
+ return n < 0 ? !1 : (m[n] = {
226
+ ...m[n],
220
227
  consumedAt: /* @__PURE__ */ new Date()
221
228
  }, !0);
222
229
  }),
223
- countRecoveryCodes: (e) => t.sync(() => d.filter((t) => t.userId === e && t.consumedAt === null).length),
224
- listPasskeys: (e) => t.sync(() => u.filter((t) => t.userId === e)),
225
- findPasskey: (e) => t.sync(() => u.find((t) => t.credentialId === e) ?? null),
226
- insertPasskey: (e) => t.sync(() => {
230
+ countRecoveryCodes: (e) => i.sync(() => m.filter((t) => t.userId === e && t.consumedAt === null).length),
231
+ listPasskeys: (e) => i.sync(() => p.filter((t) => t.userId === e)),
232
+ findPasskey: (e) => i.sync(() => p.find((t) => t.credentialId === e) ?? null),
233
+ insertPasskey: (e) => i.sync(() => {
227
234
  let t = /* @__PURE__ */ new Date(), n = {
228
235
  ...e,
229
236
  createdAt: t,
230
237
  lastUsedAt: null
231
238
  };
232
- return u.push(n), n;
239
+ return p.push(n), n;
233
240
  }),
234
- advancePasskeyCounter: (e, n) => t.sync(() => {
235
- let t = u.findIndex((t) => t.credentialId === e);
236
- return t < 0 || n <= u[t].counter ? !1 : (u[t] = {
237
- ...u[t],
238
- counter: n,
241
+ advancePasskeyCounter: (e, t) => i.sync(() => {
242
+ let n = p.findIndex((t) => t.credentialId === e);
243
+ return n < 0 || t <= p[n].counter ? !1 : (p[n] = {
244
+ ...p[n],
245
+ counter: t,
239
246
  lastUsedAt: /* @__PURE__ */ new Date()
240
247
  }, !0);
241
248
  }),
242
- isLockedOut: (e, n) => t.sync(() => {
243
- let t = m.get(e.toLowerCase());
244
- return t?.lockedUntil != null && n < t.lockedUntil ? {
249
+ isLockedOut: (e, t) => i.sync(() => {
250
+ let n = _.get(e.toLowerCase());
251
+ return n?.lockedUntil != null && t < n.lockedUntil ? {
245
252
  locked: !0,
246
- retryAfterSeconds: Math.ceil((t.lockedUntil - n) / 1e3)
253
+ retryAfterSeconds: Math.ceil((n.lockedUntil - t) / 1e3)
247
254
  } : {
248
255
  locked: !1,
249
256
  retryAfterSeconds: 0
250
257
  };
251
258
  }),
252
- recordLoginFailure: (e, n, r) => t.sync(() => {
253
- let t = e.toLowerCase(), i = m.get(t), a = i?.windowStartedAt == null || n - i.windowStartedAt >= r.windowMs, o = i?.lockedUntil != null && n >= i.lockedUntil, s = !i || a || o, c = s ? 1 : i.failedCount + 1, l = s ? n : i.windowStartedAt, u = c >= r.maxAttempts ? n + r.lockMs : null;
254
- m.set(t, {
259
+ recordLoginFailure: (e, t, n) => i.sync(() => {
260
+ let r = e.toLowerCase(), i = _.get(r), a = i?.windowStartedAt == null || t - i.windowStartedAt >= n.windowMs, o = i?.lockedUntil != null && t >= i.lockedUntil, s = !i || a || o, c = s ? 1 : i.failedCount + 1, l = s ? t : i.windowStartedAt, u = c >= n.maxAttempts ? t + n.lockMs : null;
261
+ _.set(r, {
255
262
  failedCount: c,
256
263
  windowStartedAt: l,
257
264
  lockedUntil: u
258
265
  });
259
266
  }),
260
- clearLoginFailures: (e) => t.sync(() => {
261
- m.delete(e.toLowerCase());
267
+ clearLoginFailures: (e) => i.sync(() => {
268
+ _.delete(e.toLowerCase());
262
269
  })
263
270
  };
264
- }, s = (e, t = {}) => {
271
+ }, u = (e, t = {}) => {
265
272
  let n = { ...t.metadata };
266
273
  return t.memberships && (n.memberships = t.memberships.map((e) => ({
267
274
  tenantId: e.tenantId,
@@ -272,14 +279,13 @@ var r = class extends e.TaggedError("UserAlreadyExistsError") {}, i = class exte
272
279
  tenantId: t.tenantId ?? e.tenantId,
273
280
  ...Object.keys(n).length > 0 ? { metadata: n } : {}
274
281
  };
275
- }, c = (e) => {
282
+ }, d = (e) => {
276
283
  if (e.type !== "user") return null;
277
284
  let t = e.metadata?.sessionId;
278
285
  return typeof t == "string" && t.length > 0 ? t : null;
279
- }, l = (e) => {
286
+ }, f = (e) => {
280
287
  if (e.type !== "user") return [];
281
288
  let t = e.metadata?.memberships;
282
289
  return Array.isArray(t) ? t.filter((e) => typeof e == "object" && !!e && typeof e.tenantId == "string" && typeof e.role == "string") : [];
283
290
  };
284
- //#endregion
285
- export { r as UserAlreadyExistsError, i as UserNotFoundError, o as memoryUserStore, a as newAuthId, c as sessionIdOfSubject, s as subjectFromUser, l as subjectMemberships };
291
+ export { o as UserAlreadyExistsError, s as UserNotFoundError, l as memoryUserStore, c as newAuthId, d as sessionIdOfSubject, u as subjectFromUser, f as subjectMemberships };
@@ -0,0 +1,46 @@
1
+ // src/web/guestStorage.ts
2
+ var e = (e) => {
3
+ if (!e.trim()) throw Error("Guest storage requires an app namespace");
4
+ let t = (t, n) => new Promise((r, i) => {
5
+ let a = !1, o = globalThis.indexedDB.open("voltro:guest:" + e, 1);
6
+ o.onupgradeneeded = () => {
7
+ o.result.createObjectStore("credentials");
8
+ }, o.onerror = () => i(/* @__PURE__ */ Error("Guest IndexedDB unavailable")), o.onblocked = () => {
9
+ a = !0, i(/* @__PURE__ */ Error("Guest IndexedDB blocked"));
10
+ }, o.onsuccess = () => {
11
+ let e = o.result;
12
+ if (a) {
13
+ e.close();
14
+ return;
15
+ }
16
+ let s, c;
17
+ try {
18
+ s = e.transaction("credentials", t), c = n(s.objectStore("credentials"));
19
+ } catch {
20
+ e.close(), i(/* @__PURE__ */ Error("Guest IndexedDB transaction unavailable"));
21
+ return;
22
+ }
23
+ s.oncomplete = () => {
24
+ e.close(), r(c.result);
25
+ }, s.onerror = s.onabort = () => {
26
+ e.close(), i(/* @__PURE__ */ Error("Guest IndexedDB transaction failed"));
27
+ };
28
+ };
29
+ });
30
+ return {
31
+ exclusive: (t) => globalThis.navigator?.locks ? globalThis.navigator.locks.request("voltro:guest:" + e, t) : Promise.reject(/* @__PURE__ */ Error("Guest cross-tab locking unavailable")),
32
+ read: async () => {
33
+ let e = await t("readonly", (e) => e.get("device"));
34
+ if (e === void 0) return null;
35
+ if (typeof e != "string" || !e) throw Error("Guest IndexedDB credential invalid");
36
+ return e;
37
+ },
38
+ write: async (e) => {
39
+ await t("readwrite", (t) => t.put(e, "device"));
40
+ },
41
+ remove: async () => {
42
+ await t("readwrite", (e) => e.delete("device"));
43
+ }
44
+ };
45
+ };
46
+ export { e as indexedDbGuestStore };
@@ -1,4 +1,4 @@
1
- //#region src/web/passkeyClient.ts
1
+ // src/web/passkeyClient.ts
2
2
  var e = (e) => {
3
3
  let t = new Uint8Array(e), n = "";
4
4
  for (let e of t) n += String.fromCharCode(e);
@@ -74,5 +74,4 @@ var e = (e) => {
74
74
  if (!p.ok) throw Error(`passkey sign-in failed: ${String(p.error)}`);
75
75
  return { subject: p.subject };
76
76
  };
77
- //#endregion
78
77
  export { n as isPasskeySupported, i as registerPasskey, a as signInWithPasskey };
@@ -1,9 +1,8 @@
1
1
  import { useOptionalSubject as e } from "./subjectContext.js";
2
2
  import { Fragment as t, jsx as n } from "react/jsx-runtime";
3
- //#region src/web/requireAuth.tsx
3
+ // src/web/requireAuth.tsx
4
4
  var r = ({ children: r, fallback: i }) => {
5
5
  let a = e();
6
6
  return a ? typeof r == "function" ? /* @__PURE__ */ n(t, { children: r(a) }) : /* @__PURE__ */ n(t, { children: r }) : /* @__PURE__ */ n(t, { children: i });
7
7
  };
8
- //#endregion
9
8
  export { r as RequireAuth };
@@ -1,6 +1,6 @@
1
1
  import { createContext as e, useContext as t } from "react";
2
2
  import { jsx as n } from "react/jsx-runtime";
3
- //#region src/web/subjectContext.tsx
3
+ // src/web/subjectContext.tsx
4
4
  var r = e(null), i = ({ subject: e, children: t }) => /* @__PURE__ */ n(r.Provider, {
5
5
  value: e,
6
6
  children: t
@@ -9,5 +9,4 @@ var r = e(null), i = ({ subject: e, children: t }) => /* @__PURE__ */ n(r.Provid
9
9
  if (!e) throw Error("useSubject() called without a signed-in user. Use useOptionalSubject() or wrap the consumer in <RequireAuth>.");
10
10
  return e;
11
11
  }, o = () => t(r);
12
- //#endregion
13
12
  export { i as SubjectProvider, o as useOptionalSubject, a as useSubject };