@voltro/plugin-auth 0.85.0 → 0.87.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 (52) hide show
  1. package/CHANGELOG.md +5987 -0
  2. package/README.md +93 -0
  3. package/THIRD-PARTY-NOTICES.md +1122 -31
  4. package/dist/admission.js +9 -0
  5. package/dist/appUserStore.js +19 -0
  6. package/dist/authExtensions.js +9 -0
  7. package/dist/emailVerification.js +64 -0
  8. package/dist/guest.d.ts +99 -0
  9. package/dist/guest.js +3 -0
  10. package/dist/guestClient.js +237 -0
  11. package/dist/guestCredential.js +12 -0
  12. package/dist/guestPasswordUpgrade.js +85 -0
  13. package/dist/guestPlugin.js +166 -0
  14. package/dist/guestSchema.js +20 -0
  15. package/dist/guestSecrets.js +10 -0
  16. package/dist/guestService.js +224 -0
  17. package/dist/handlers.js +337 -0
  18. package/dist/impersonation.js +176 -0
  19. package/dist/index.d.ts +282 -20
  20. package/dist/index.js +30 -88
  21. package/dist/invitations.js +187 -0
  22. package/dist/mail.js +10 -0
  23. package/dist/memberships.js +8 -0
  24. package/dist/native/guestStorage.js +41 -0
  25. package/dist/native.d.ts +110 -0
  26. package/dist/native.js +45 -43
  27. package/dist/nativeRoutes.js +109 -0
  28. package/dist/overlayPlugin.js +167 -0
  29. package/dist/passkeyHandlers.js +162 -0
  30. package/dist/plugin.d.ts +123 -10
  31. package/dist/plugin.js +519 -5
  32. package/dist/postgresUserStore.js +475 -0
  33. package/dist/revocation.js +50 -0
  34. package/dist/schema.d.ts +45 -6
  35. package/dist/schema.js +99 -96
  36. package/dist/sessionFamily.js +15 -0
  37. package/dist/sessionRegistry.js +133 -0
  38. package/dist/signedClaims.js +24 -0
  39. package/dist/strategy.d.ts +17 -9
  40. package/dist/strategy.js +69 -3
  41. package/dist/subjectGuard.js +11 -0
  42. package/dist/totp.js +55 -0
  43. package/dist/userStore.js +292 -0
  44. package/dist/web/guestStorage.js +47 -0
  45. package/dist/web/passkeyClient.js +78 -0
  46. package/dist/web/requireAuth.js +9 -0
  47. package/dist/web/subjectContext.js +13 -0
  48. package/dist/web.d.ts +94 -0
  49. package/dist/web.js +6 -90
  50. package/package.json +8 -3
  51. package/dist/plugin-CbmcyEJs.js +0 -2018
  52. package/dist/strategy-XaGjSicX.js +0 -353
@@ -1,353 +0,0 @@
1
- import { sessionSecretsOf as e } from "./session.js";
2
- import { Data as t, Effect as n } from "effect";
3
- import { readSessionValue as r, resolveSessionSecret as i, verifySessionKeyed as a } from "@voltro/protocol/session";
4
- import { generateId as o } from "@voltro/database";
5
- //#region src/revocation.ts
6
- var s = 3e4, c = 1e4, l = (e, t = {}) => {
7
- let r = t.ttlMs ?? s, i = t.maxEntries ?? c, a = t.now ?? Date.now, o = /* @__PURE__ */ new Map(), l = () => {
8
- if (o.size <= i) return;
9
- let e = a();
10
- for (let [t, n] of o) n.freshUntil <= e && o.delete(t);
11
- for (let e of o.keys()) {
12
- if (o.size <= i) break;
13
- o.delete(e);
14
- }
15
- }, u = (e) => e !== null && e.expiresAt.getTime() > a();
16
- return {
17
- ttlMs: r,
18
- invalidate: (e) => {
19
- o.delete(e);
20
- },
21
- isLive: (t) => n.gen(function* () {
22
- let n = a();
23
- if (r > 0) {
24
- let e = o.get(t);
25
- if (e && e.freshUntil > n) return e.live;
26
- }
27
- let i = yield* e.findSession(t), s = u(i);
28
- return r > 0 && (o.set(t, {
29
- live: s,
30
- freshUntil: n + r
31
- }), l()), s;
32
- })
33
- };
34
- }, u = class extends t.TaggedError("UserAlreadyExistsError") {}, d = class extends t.TaggedError("UserNotFoundError") {}, f = (e) => o({
35
- kind: "typeid",
36
- prefix: e
37
- }, e), p = (e = []) => {
38
- let t = /* @__PURE__ */ new Map(), r = /* @__PURE__ */ new Map(), i = [], a = [], o = [], s = [], c = [], l = [], p = [], m = /* @__PURE__ */ new Map();
39
- for (let n of e) t.set(n.id, n), r.set(n.email.toLowerCase(), n);
40
- let h = (e, i) => n.gen(function* () {
41
- let a = t.get(e);
42
- if (!a) return yield* n.fail(new d({ userId: e }));
43
- let o = i(a);
44
- return t.set(e, o), r.set(o.email.toLowerCase(), o), o;
45
- });
46
- return {
47
- findByEmail: (e) => n.sync(() => r.get(e.toLowerCase()) ?? null),
48
- findById: (e) => n.sync(() => t.get(e) ?? null),
49
- insert: (e) => n.gen(function* () {
50
- let i = e.email.toLowerCase();
51
- if (r.has(i)) return yield* n.fail(new u({ email: i }));
52
- let a = {
53
- ...e,
54
- email: i,
55
- createdAt: /* @__PURE__ */ new Date()
56
- };
57
- return t.set(a.id, a), r.set(i, a), a;
58
- }),
59
- updatePassword: (e, t) => h(e, (e) => ({
60
- ...e,
61
- passwordHash: t
62
- })),
63
- setMfaSecret: (e, t) => h(e, (e) => ({
64
- ...e,
65
- mfaSecret: t,
66
- mfaEnrolledAt: null
67
- })),
68
- markMfaEnrolled: (e) => h(e, (e) => ({
69
- ...e,
70
- mfaEnrolledAt: /* @__PURE__ */ new Date()
71
- })),
72
- clearMfa: (e) => h(e, (e) => ({
73
- ...e,
74
- mfaSecret: null,
75
- mfaEnrolledAt: null
76
- })),
77
- markEmailVerified: (e) => h(e, (e) => e.emailVerifiedAt ? e : {
78
- ...e,
79
- emailVerifiedAt: /* @__PURE__ */ new Date()
80
- }),
81
- listMemberships: (e) => n.sync(() => i.filter((t) => t.userId === e)),
82
- addMembership: (e) => n.sync(() => {
83
- let t = i.findIndex((t) => t.userId === e.userId && t.tenantId === e.tenantId), n = {
84
- ...e,
85
- joinedAt: /* @__PURE__ */ new Date()
86
- };
87
- if (t >= 0) {
88
- let e = i[t], r = {
89
- ...n,
90
- joinedAt: e.joinedAt
91
- };
92
- return i[t] = r, r;
93
- }
94
- return i.push(n), n;
95
- }),
96
- membershipRole: (e, t) => n.sync(() => i.find((n) => n.userId === e && n.tenantId === t)?.role ?? null),
97
- listSessions: (e) => n.sync(() => a.filter((t) => t.userId === e).sort((e, t) => t.createdAt.getTime() - e.createdAt.getTime())),
98
- insertSession: (e) => n.sync(() => {
99
- let t = /* @__PURE__ */ new Date(), n = {
100
- ...e,
101
- createdAt: t,
102
- lastSeenAt: t
103
- };
104
- return a.push(n), n;
105
- }),
106
- findSession: (e) => n.sync(() => a.find((t) => t.id === e) ?? null),
107
- touchSession: (e, t) => n.sync(() => {
108
- let n = a.findIndex((t) => t.id === e);
109
- n >= 0 && (a[n] = {
110
- ...a[n],
111
- expiresAt: t,
112
- lastSeenAt: /* @__PURE__ */ new Date()
113
- });
114
- }),
115
- revokeSession: (e, t) => n.sync(() => {
116
- let n = a.findIndex((n) => n.id === t && n.userId === e);
117
- return n < 0 ? !1 : (a.splice(n, 1), !0);
118
- }),
119
- revokeAllOtherSessions: (e, t) => n.sync(() => {
120
- let n = 0;
121
- for (let r = a.length - 1; r >= 0; r--) {
122
- let i = a[r];
123
- i.userId === e && i.id !== t && (a.splice(r, 1), n++);
124
- }
125
- return n;
126
- }),
127
- revokeAllSessions: (e) => n.sync(() => {
128
- let t = 0;
129
- for (let n = a.length - 1; n >= 0; n--) a[n].userId === e && (a.splice(n, 1), t++);
130
- return t;
131
- }),
132
- insertToken: (e) => n.sync(() => {
133
- let t = {
134
- ...e,
135
- consumedAt: null,
136
- createdAt: /* @__PURE__ */ new Date()
137
- };
138
- return o.push(t), t;
139
- }),
140
- consumeToken: (e, t) => n.sync(() => {
141
- let n = Date.now(), r = o.find((r) => r.tokenHash === e && r.purpose === t && r.consumedAt === null && r.expiresAt.getTime() > n);
142
- if (!r) return null;
143
- let i = {
144
- ...r,
145
- consumedAt: /* @__PURE__ */ new Date()
146
- }, a = o.indexOf(r);
147
- return o[a] = i, i;
148
- }),
149
- latestToken: (e, t) => n.sync(() => {
150
- let n = null;
151
- for (let r of o) r.userId === e && r.purpose === t && (n === null || r.createdAt.getTime() > n.createdAt.getTime()) && (n = r);
152
- return n;
153
- }),
154
- insertInvitation: (e) => n.sync(() => {
155
- let t = {
156
- ...e,
157
- email: e.email.toLowerCase(),
158
- acceptedAt: null,
159
- acceptedByUserId: null,
160
- revokedAt: null,
161
- revokedByUserId: null,
162
- createdAt: /* @__PURE__ */ new Date()
163
- };
164
- return l.push(t), t;
165
- }),
166
- findInvitationByHash: (e) => n.sync(() => l.find((t) => t.tokenHash === e) ?? null),
167
- acceptInvitation: (e, t, r) => n.sync(() => {
168
- let n = l.findIndex((t) => t.tokenHash === e && t.acceptedAt === null && t.revokedAt === null && t.expiresAt.getTime() > r.getTime());
169
- if (n < 0) return null;
170
- let i = {
171
- ...l[n],
172
- acceptedAt: r,
173
- acceptedByUserId: t
174
- };
175
- return l[n] = i, i;
176
- }),
177
- revokeInvitation: (e, t, r, i) => n.sync(() => {
178
- let n = l.findIndex((n) => n.id === e && n.tenantId === t && n.acceptedAt === null && n.revokedAt === null);
179
- return n < 0 ? !1 : (l[n] = {
180
- ...l[n],
181
- revokedAt: i,
182
- revokedByUserId: r
183
- }, !0);
184
- }),
185
- listInvitations: (e) => n.sync(() => l.filter((t) => t.tenantId === e).sort((e, t) => t.createdAt.getTime() - e.createdAt.getTime())),
186
- countPendingInvitations: (e, t) => n.sync(() => l.filter((n) => n.tenantId === e && n.acceptedAt === null && n.revokedAt === null && n.expiresAt.getTime() > t.getTime()).length),
187
- supersedePendingInvitations: (e, t, r, i) => n.sync(() => {
188
- let n = t.toLowerCase(), a = 0;
189
- for (let t = 0; t < l.length; t++) {
190
- let o = l[t];
191
- o.tenantId === e && o.email === n && o.acceptedAt === null && o.revokedAt === null && (l[t] = {
192
- ...o,
193
- revokedAt: i,
194
- revokedByUserId: r
195
- }, a++);
196
- }
197
- return a;
198
- }),
199
- insertImpersonationGrant: (e) => n.sync(() => {
200
- let t = {
201
- ...e,
202
- startedAt: /* @__PURE__ */ new Date(),
203
- endedAt: null,
204
- endedReason: null
205
- };
206
- return p.push(t), t;
207
- }),
208
- findImpersonationGrantBySession: (e) => n.sync(() => p.find((t) => t.sessionId === e) ?? null),
209
- endImpersonationGrant: (e, t, r) => n.sync(() => {
210
- let n = p.findIndex((t) => t.sessionId === e && t.endedAt === null);
211
- return n < 0 ? !1 : (p[n] = {
212
- ...p[n],
213
- endedAt: r,
214
- endedReason: t
215
- }, !0);
216
- }),
217
- replaceRecoveryCodes: (e, t) => n.sync(() => {
218
- for (let t = c.length - 1; t >= 0; t--) c[t].userId === e && c.splice(t, 1);
219
- let n = /* @__PURE__ */ new Date();
220
- for (let r of t) c.push({
221
- id: f("rc"),
222
- userId: e,
223
- codeHash: r,
224
- consumedAt: null,
225
- createdAt: n
226
- });
227
- }),
228
- consumeRecoveryCode: (e, t) => n.sync(() => {
229
- let n = c.findIndex((n) => n.userId === e && n.codeHash === t && n.consumedAt === null);
230
- return n < 0 ? !1 : (c[n] = {
231
- ...c[n],
232
- consumedAt: /* @__PURE__ */ new Date()
233
- }, !0);
234
- }),
235
- countRecoveryCodes: (e) => n.sync(() => c.filter((t) => t.userId === e && t.consumedAt === null).length),
236
- listPasskeys: (e) => n.sync(() => s.filter((t) => t.userId === e)),
237
- findPasskey: (e) => n.sync(() => s.find((t) => t.credentialId === e) ?? null),
238
- insertPasskey: (e) => n.sync(() => {
239
- let t = /* @__PURE__ */ new Date(), n = {
240
- ...e,
241
- createdAt: t,
242
- lastUsedAt: null
243
- };
244
- return s.push(n), n;
245
- }),
246
- advancePasskeyCounter: (e, t) => n.sync(() => {
247
- let n = s.findIndex((t) => t.credentialId === e);
248
- return n < 0 || t <= s[n].counter ? !1 : (s[n] = {
249
- ...s[n],
250
- counter: t,
251
- lastUsedAt: /* @__PURE__ */ new Date()
252
- }, !0);
253
- }),
254
- isLockedOut: (e, t) => n.sync(() => {
255
- let n = m.get(e.toLowerCase());
256
- return n?.lockedUntil != null && t < n.lockedUntil ? {
257
- locked: !0,
258
- retryAfterSeconds: Math.ceil((n.lockedUntil - t) / 1e3)
259
- } : {
260
- locked: !1,
261
- retryAfterSeconds: 0
262
- };
263
- }),
264
- recordLoginFailure: (e, t, r) => n.sync(() => {
265
- let n = e.toLowerCase(), i = m.get(n), a = i?.windowStartedAt == null || t - i.windowStartedAt >= r.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 >= r.maxAttempts ? t + r.lockMs : null;
266
- m.set(n, {
267
- failedCount: c,
268
- windowStartedAt: l,
269
- lockedUntil: u
270
- });
271
- }),
272
- clearLoginFailures: (e) => n.sync(() => {
273
- m.delete(e.toLowerCase());
274
- })
275
- };
276
- }, m = (e, t = {}) => {
277
- let n = { ...t.metadata };
278
- return t.memberships && (n.memberships = t.memberships.map((e) => ({
279
- tenantId: e.tenantId,
280
- role: e.role
281
- }))), {
282
- type: "user",
283
- id: e.id,
284
- tenantId: t.tenantId ?? e.tenantId,
285
- ...Object.keys(n).length > 0 ? { metadata: n } : {}
286
- };
287
- }, h = (e) => {
288
- if (e.type !== "user") return null;
289
- let t = e.metadata?.sessionId;
290
- return typeof t == "string" && t.length > 0 ? t : null;
291
- }, g = (e) => {
292
- if (e.type !== "user") return [];
293
- let t = e.metadata?.memberships;
294
- return Array.isArray(t) ? t.filter((e) => typeof e == "object" && !!e && typeof e.tenantId == "string" && typeof e.role == "string") : [];
295
- }, _ = (e) => typeof e.isLive == "function", v = (e) => e.revocation && _(e.revocation) ? e.revocation : e.store ? l(e.store, e.revocation ?? {}) : null, y = (t = {}) => {
296
- let o = t.cookieName ?? "voltro:session", s = v(t), c = t.revocation && !_(t.revocation) ? t.revocation : {}, u = /* @__PURE__ */ new WeakMap(), d = (e) => {
297
- if (s) return s;
298
- let n = t.sessionRegistry;
299
- if (n === void 0) return null;
300
- if (typeof n != "function") return l(n, c);
301
- if (e === void 0) return null;
302
- let r = u.get(e);
303
- if (r) return r;
304
- let i = l(n(e), c);
305
- return u.set(e, i), i;
306
- };
307
- return {
308
- id: "voltro-password",
309
- resolve: (s) => {
310
- let c = r(s.headers, o);
311
- if (!c) return { kind: "skip" };
312
- let l = e(t.secret ?? i()), u = a(c, l);
313
- if (!u) return {
314
- kind: "failed",
315
- reason: "voltro session cookie failed verification"
316
- };
317
- let f = b(u.subject, "voltro-password"), p = h(f);
318
- if (!p) return t.requireSessionId === !0 ? {
319
- kind: "failed",
320
- reason: "voltro session cookie carries no session id"
321
- } : {
322
- kind: "matched",
323
- subject: f
324
- };
325
- let m = d(s.store);
326
- return m ? n.runPromise(m.isLive(p)).then((e) => e ? {
327
- kind: "matched",
328
- subject: f
329
- } : {
330
- kind: "failed",
331
- reason: "voltro session revoked"
332
- }) : t.requireSessionId === !0 && t.sessionRegistry !== void 0 ? {
333
- kind: "failed",
334
- reason: "session registry not available yet"
335
- } : {
336
- kind: "matched",
337
- subject: f
338
- };
339
- }
340
- };
341
- }, b = (e, t) => {
342
- if (e.type === "anonymous") return e;
343
- let n = e.metadata ?? {};
344
- return {
345
- ...e,
346
- metadata: {
347
- ...n,
348
- provider: t
349
- }
350
- };
351
- };
352
- //#endregion
353
- export { f as a, g as c, p as i, l, u as n, h as o, d as r, m as s, y as t };