@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
@@ -0,0 +1,223 @@
1
+ import { newAuthId as e } from "./userStore.js";
2
+ import { dataStoreSessionRegistry as t, issueRegisteredSession as n } from "./sessionRegistry.js";
3
+ import { validateGuestSecrets as r } from "./guestSecrets.js";
4
+ import { parseGuestCredential as i } from "./guestCredential.js";
5
+ import { createHash as a, randomBytes as o, timingSafeEqual as s } from "node:crypto";
6
+ import { Data as c, Effect as l } from "effect";
7
+ import { noteSigningLifetime as u } from "@voltro/protocol/session";
8
+ import { and as d, eq as f } from "@voltro/database";
9
+ // src/guestService.ts
10
+ var p = class extends c.TaggedError("GuestAuthError") {}, m = "authGuests", h = "authGuestDevices", g = 86400, _ = () => new p({ code: "credential_rejected" }), v = (e) => a("sha256").update(e).digest("hex"), y = (e) => e instanceof Date ? e.getTime() : typeof e == "string" ? Date.parse(e) : NaN, b = (e, t) => ({
11
+ table: e,
12
+ predicate: t,
13
+ order: [],
14
+ take: void 0,
15
+ skip: void 0,
16
+ projection: void 0
17
+ }), x = (e, t, n) => d(f(e, t), f("tenantId", n)), S = (e, t) => typeof e != "string" || !/^[a-f0-9]{64}$/.test(e) ? !1 : s(Buffer.from(e, "hex"), Buffer.from(v(t), "hex")), C = (e) => {
18
+ let t = i(e);
19
+ if (!t) throw _();
20
+ return t;
21
+ }, w = (e) => l.tryPromise({
22
+ try: e,
23
+ catch: (e) => e instanceof p ? e : new p({ code: "store_unavailable" })
24
+ }), T = (i, a) => {
25
+ if (!a.tenantId.trim()) throw Error("Guest authentication requires a server-owned tenant");
26
+ let s = a.ttlSeconds ?? 365 * g, c = a.sessionTtlSeconds ?? 7 * g;
27
+ if (![s, c].every((e) => Number.isSafeInteger(e) && e > 0 && e <= 3650 * g)) throw Error("Guest authentication lifetimes must be positive bounded whole seconds");
28
+ u("session", c * 1e3);
29
+ let l = a.now ?? Date.now, d = (e, t, n, r) => e.query(b(t, x(n, r, a.tenantId))), f = async (e, t, n) => {
30
+ let r = await e.update(m, t, { lastSeenAt: new Date(n) });
31
+ if (!r || r.tenantId !== a.tenantId) throw _();
32
+ return r;
33
+ }, T = async (e, t, n) => {
34
+ let r = C(t), i = await f(e, r.guestId, n), a = (await d(e, h, "id", r.deviceId))[0];
35
+ if (!a || a.guestId !== r.guestId || !S(a.secretHash, r.secret) || !(y(i.expiresAt) > n) || !(y(a.expiresAt) > n) || !Number.isSafeInteger(a.generation) || Number(a.generation) < 0) throw _();
36
+ return {
37
+ guest: i,
38
+ device: a,
39
+ parsed: r
40
+ };
41
+ }, E = async (e, i, o, s) => {
42
+ let l = Math.floor((Math.min(y(i.expiresAt), y(o.expiresAt)) - s) / 1e3);
43
+ if (l < 1) throw _();
44
+ let u = {
45
+ type: "user",
46
+ kind: "guest",
47
+ id: String(i.id),
48
+ tenantId: a.tenantId,
49
+ metadata: {
50
+ guestDeviceId: o.id,
51
+ guestDeviceGeneration: o.generation
52
+ }
53
+ }, d = await n(t(e), u, r(typeof a.secret == "function" ? a.secret() : a.secret), {
54
+ ttlSeconds: Math.min(c, l),
55
+ now: () => s,
56
+ ...a.secure === void 0 ? {} : { secure: a.secure }
57
+ });
58
+ return {
59
+ ...d,
60
+ subject: {
61
+ ...u,
62
+ metadata: {
63
+ ...u.metadata,
64
+ sessionId: d.sessionId
65
+ }
66
+ }
67
+ };
68
+ }, D = async (e, t) => {
69
+ for (let n of await d(e, "sessions", "userId", t)) await e.delete("sessions", String(n.id));
70
+ };
71
+ return {
72
+ create: () => w(async () => {
73
+ let t = l(), n = e("guest"), r = e("gdev"), c = o(32).toString("base64url"), u = {
74
+ id: n,
75
+ tenantId: a.tenantId,
76
+ upgradeable: a.upgradeable ?? !0,
77
+ expiresAt: new Date(t + s * 1e3),
78
+ createdAt: new Date(t),
79
+ lastSeenAt: new Date(t)
80
+ }, d = {
81
+ id: r,
82
+ guestId: n,
83
+ tenantId: a.tenantId,
84
+ secretHash: v(c),
85
+ generation: 0,
86
+ expiresAt: u.expiresAt,
87
+ createdAt: new Date(t)
88
+ };
89
+ return i.transactional(async (e) => (await e.insert(m, u), await e.insert(h, d), {
90
+ ...await E(e, u, d, t),
91
+ deviceCredential: `${n}.${r}.${c}`
92
+ }));
93
+ }),
94
+ resume: (e) => w(async () => {
95
+ let t = l();
96
+ return i.transactional(async (n) => {
97
+ let { guest: r, device: i } = await T(n, e, t);
98
+ return E(n, r, i, t);
99
+ });
100
+ }),
101
+ prepareRotation: (e) => w(async () => {
102
+ r(typeof a.secret == "function" ? a.secret() : a.secret);
103
+ let t = l(), n = o(32).toString("base64url");
104
+ return i.transactional(async (r) => {
105
+ let { parsed: i } = await T(r, e, t);
106
+ return { deviceCredential: `${i.guestId}.${i.deviceId}.${n}` };
107
+ });
108
+ }),
109
+ rotate: (e, t) => w(async () => {
110
+ let n = l(), r = C(t);
111
+ return i.transactional(async (i) => {
112
+ let { guest: a, device: o, parsed: s } = await T(i, e, n);
113
+ if (r.guestId !== s.guestId || r.deviceId !== s.deviceId || r.secret === s.secret) throw _();
114
+ let c = Number(o.generation) + 1;
115
+ if (!Number.isSafeInteger(c)) throw _();
116
+ let l = await i.update(h, s.deviceId, {
117
+ secretHash: v(r.secret),
118
+ generation: c
119
+ });
120
+ if (!l) throw _();
121
+ return await D(i, s.guestId), {
122
+ ...await E(i, a, l, n),
123
+ deviceCredential: t
124
+ };
125
+ });
126
+ }),
127
+ revoke: (e) => w(async () => {
128
+ let t = l();
129
+ await i.transactional(async (n) => {
130
+ let { parsed: r } = await T(n, e, t);
131
+ await n.delete(h, r.deviceId), await D(n, r.guestId);
132
+ });
133
+ }),
134
+ upgrade: (e, t) => w(async () => {
135
+ r(typeof a.secret == "function" ? a.secret() : a.secret);
136
+ let n = l();
137
+ return i.transactional(async (r) => {
138
+ let { guest: i, parsed: o } = await T(r, e, n);
139
+ if (i.upgradeable !== !0 && i.upgradeable !== 1) throw _();
140
+ if ((await d(r, "users", "id", o.guestId)).length !== 0) throw new p({ code: "upgrade_conflict" });
141
+ await D(r, o.guestId);
142
+ let s = await t({
143
+ store: r,
144
+ subjectId: o.guestId,
145
+ tenantId: a.tenantId,
146
+ createdAt: new Date(y(i.createdAt))
147
+ }), c = (await d(r, "users", "id", o.guestId))[0];
148
+ if (!c || c.id !== o.guestId || c.tenantId !== a.tenantId) throw new p({ code: "upgrade_conflict" });
149
+ for (let e of await d(r, h, "guestId", o.guestId)) await r.delete(h, String(e.id));
150
+ if (!await r.delete(m, o.guestId)) throw _();
151
+ return s;
152
+ });
153
+ }),
154
+ erase: (e) => w(async () => i.transactional(async (t) => {
155
+ let n = await t.update(m, e, { lastSeenAt: new Date(l()) });
156
+ if (!n) return !1;
157
+ if (n.tenantId !== a.tenantId) throw _();
158
+ for (let n of await d(t, h, "guestId", e)) await t.delete(h, String(n.id));
159
+ return await D(t, e), t.delete(m, e);
160
+ })),
161
+ mergeInto: (e, t, n) => w(async () => {
162
+ r(typeof a.secret == "function" ? a.secret() : a.secret);
163
+ let o = l();
164
+ if (typeof t != "string" || !t.trim()) throw _();
165
+ return i.transactional(async (r) => {
166
+ let { guest: i, parsed: s } = await T(r, e, o);
167
+ if (i.upgradeable !== !0 && i.upgradeable !== 1) throw _();
168
+ if (t === s.guestId) throw new p({ code: "upgrade_conflict" });
169
+ let c = await r.update("users", t, { updatedAt: new Date(o) });
170
+ if (!c || c.id !== t || c.tenantId !== a.tenantId || c.deactivatedAt !== null && c.deactivatedAt !== void 0 || (await d(r, m, "id", t)).length !== 0 || (await d(r, "users", "id", s.guestId)).length !== 0) throw _();
171
+ await D(r, s.guestId);
172
+ let l = await n({
173
+ store: r,
174
+ subjectId: s.guestId,
175
+ targetId: t,
176
+ tenantId: a.tenantId,
177
+ createdAt: new Date(y(i.createdAt))
178
+ }), u = (await d(r, "users", "id", t))[0];
179
+ if (!u || u.id !== t || u.tenantId !== a.tenantId || (await d(r, "users", "id", s.guestId)).length !== 0) throw new p({ code: "upgrade_conflict" });
180
+ for (let e of await d(r, h, "guestId", s.guestId)) await r.delete(h, String(e.id));
181
+ if (!await r.delete(m, s.guestId)) throw _();
182
+ return l;
183
+ });
184
+ }),
185
+ export: (e) => w(async () => {
186
+ let t = (await d(i, m, "id", e))[0];
187
+ if (!t) return null;
188
+ let n = await d(i, h, "guestId", e), r = await d(i, "sessions", "userId", e);
189
+ return {
190
+ subject: {
191
+ id: e,
192
+ tenantId: a.tenantId,
193
+ kind: "guest",
194
+ upgradeable: t.upgradeable === !0 || t.upgradeable === 1,
195
+ createdAt: y(t.createdAt),
196
+ expiresAt: y(t.expiresAt)
197
+ },
198
+ devices: n.map((e) => ({
199
+ id: String(e.id),
200
+ generation: Number(e.generation),
201
+ createdAt: y(e.createdAt),
202
+ expiresAt: y(e.expiresAt)
203
+ })),
204
+ sessions: r.map((e) => ({
205
+ id: String(e.id),
206
+ expiresAt: y(e.expiresAt)
207
+ }))
208
+ };
209
+ }),
210
+ isLive: (e) => w(async () => {
211
+ if (e.type !== "user" || e.kind !== "guest" || e.tenantId !== a.tenantId) return !1;
212
+ let t = e.metadata?.sessionId, n = e.metadata?.guestDeviceId, r = e.metadata?.guestDeviceGeneration;
213
+ if (typeof t != "string" || typeof n != "string" || !Number.isSafeInteger(r)) return !1;
214
+ let o = l(), s = (await d(i, m, "id", e.id))[0];
215
+ if (!s || !(y(s.expiresAt) > o)) return !1;
216
+ let c = (await d(i, h, "id", n))[0];
217
+ if (!c || c.guestId !== e.id || c.generation !== r || !(y(c.expiresAt) > o)) return !1;
218
+ let u = (await d(i, "sessions", "id", t))[0];
219
+ return u?.userId === e.id && y(u.expiresAt) > o;
220
+ })
221
+ };
222
+ };
223
+ export { p as GuestAuthError, T as dataStoreGuestAuth };
package/dist/handlers.js CHANGED
@@ -1,41 +1,41 @@
1
1
  import { hashPassword as e, verifyPassword as t, verifyPasswordWithRehash as n } from "./password.js";
2
2
  import { SESSION_COOKIE_NAME as r, clearSessionCookie as i, issueSession as a, resolveSessionSecret as o, resolveSessionSecrets as s, sessionSecretsOf as c } from "./session.js";
3
3
  import { newAuthId as l, subjectFromUser as u } from "./userStore.js";
4
- import { runSubjectGuards as d } from "./subjectGuard.js";
5
- import { generateRecoveryCodes as f, generateTotpSecret as p, otpauthUrl as m, verifyTotpCode as h } from "./totp.js";
6
- import { MFA_PENDING_TTL_S as g, hashToken as _, mintToken as v } from "./tokens.js";
7
- import { CSRF_COOKIE_NAME as y, issueCsrfToken as b } from "./csrf.js";
8
- import { decideAdmission as x } from "./admission.js";
4
+ import { CSRF_COOKIE_NAME as d, issueCsrfToken as f } from "./csrf.js";
5
+ import { decideAdmission as p } from "./admission.js";
6
+ import { generateRecoveryCodes as m, generateTotpSecret as h, otpauthUrl as g, verifyTotpCode as _ } from "./totp.js";
7
+ import { MFA_PENDING_TTL_S as v, hashToken as y, mintToken as b } from "./tokens.js";
8
+ import { runSubjectGuards as x } from "./subjectGuard.js";
9
9
  import { EMAIL_VERIFIED_METADATA_KEY as S, emailVerificationActive as C, sendEmailVerification as w, userEmailVerified as T } from "./emailVerification.js";
10
10
  import { Effect as E } from "effect";
11
11
  import { buildSetCookie as D } from "@voltro/protocol/session";
12
12
  import { writeTenantSelection as O } from "@voltro/database";
13
13
  import { selectSubjectTenant as k } from "@voltro/protocol";
14
- //#region src/handlers.ts
15
- var A = 5, j = 900, M = 900, N = (e) => ({
14
+ // src/handlers.ts
15
+ var A = 5, j = 900, M = 900, N = (e) => e.email ?? `user:${e.id}`, P = (e) => ({
16
16
  maxAttempts: e.lockout?.maxAttempts ?? A,
17
17
  windowMs: (e.lockout?.windowSeconds ?? j) * 1e3,
18
18
  lockMs: (e.lockout?.lockSeconds ?? M) * 1e3
19
- }), P = (e) => e.secrets ?? (e.secret === void 0 ? s() : c(e.secret)), F = (e) => e.secret ?? o(), I = (e, t, n = {}) => ({
19
+ }), F = (e) => e.secrets ?? (e.secret === void 0 ? s() : c(e.secret)), I = (e) => e.secret ?? o(), L = (e, t, n = {}) => ({
20
20
  status: e,
21
21
  body: JSON.stringify(t),
22
22
  contentType: "application/json; charset=utf-8",
23
23
  ...n
24
- }), L = (e, t = {}) => ({
24
+ }), R = (e, t = {}) => ({
25
25
  status: 302,
26
26
  body: "",
27
27
  contentType: "text/plain",
28
28
  location: e,
29
29
  ...t
30
- }), R = 604800, z = (e, t) => e.type === "user" ? {
30
+ }), z = 604800, B = (e, t) => e.type === "user" ? {
31
31
  ...e,
32
32
  metadata: {
33
33
  ...e.metadata ?? {},
34
34
  sessionId: t
35
35
  }
36
- } : e, B = (e, t, n, r = {}) => E.gen(function* () {
37
- r.clearLoginFailures !== !1 && (yield* t.clearLoginFailures(e.email)), n.ensureActor && (yield* n.ensureActor(e));
38
- let i = yield* t.listMemberships(e.id), o = r.sessionId ?? l("sess"), s = r.ttlSeconds ?? R;
36
+ } : e, V = (e, t, n, r = {}) => E.gen(function* () {
37
+ r.clearLoginFailures !== !1 && (yield* t.clearLoginFailures(N(e))), n.ensureActor && (yield* n.ensureActor(e));
38
+ let i = yield* t.listMemberships(e.id), o = r.sessionId ?? l("sess"), s = r.ttlSeconds ?? z;
39
39
  r.sessionRow !== "existing" && (yield* t.insertSession({
40
40
  id: o,
41
41
  userId: e.id,
@@ -46,10 +46,10 @@ var A = 5, j = 900, M = 900, N = (e) => ({
46
46
  }).pipe(E.ignore));
47
47
  let c = { ...r.metadata };
48
48
  C(n.emailVerification) && (c[S] = T(e, n.emailVerification));
49
- let d = z(u(e, {
49
+ let d = B(u(e, {
50
50
  memberships: i,
51
51
  ...Object.keys(c).length > 0 ? { metadata: c } : {}
52
- }), o), f = a(d, P(n), {
52
+ }), o), f = a(d, F(n), {
53
53
  ttlSeconds: s,
54
54
  ...n.cookieDomain ? { domain: n.cookieDomain } : {},
55
55
  secure: n.cookieSecure ?? !0
@@ -60,18 +60,18 @@ var A = 5, j = 900, M = 900, N = (e) => ({
60
60
  expiresAt: Math.floor(Date.now() / 1e3) + s,
61
61
  subject: d
62
62
  };
63
- }), V = (e, t) => E.gen(function* () {
63
+ }), H = (e, t) => E.gen(function* () {
64
64
  let n = t.subjectGuards ?? [];
65
65
  if (n.length === 0) return null;
66
- let r = yield* d(n, e);
67
- return r.ok ? null : I(403, {
66
+ let r = yield* x(n, e);
67
+ return r.ok ? null : L(403, {
68
68
  error: r.code,
69
69
  message: r.message
70
70
  });
71
- }), H = (r, i, a) => E.gen(function* () {
72
- if (!r.email || !r.password) return I(400, { error: "email and password required" });
71
+ }), U = (r, i, a) => E.gen(function* () {
72
+ if (!r.email || !r.password) return L(400, { error: "email and password required" });
73
73
  let o = yield* i.findByEmail(r.email), s = (a.now ?? Date.now)(), c = yield* i.isLockedOut(r.email, s);
74
- if (c.locked) return I(429, {
74
+ if (c.locked) return L(429, {
75
75
  error: "account_locked",
76
76
  retryAfterSeconds: c.retryAfterSeconds
77
77
  });
@@ -79,65 +79,65 @@ var A = 5, j = 900, M = 900, N = (e) => ({
79
79
  let n = yield* e("decoy-not-a-real-password").pipe(E.orElseSucceed(() => ""));
80
80
  return yield* t(r.password, n), { valid: !1 };
81
81
  });
82
- if (!o || !d.valid) return yield* i.recordLoginFailure(r.email, s, N(a)), I(401, { error: "invalid credentials" });
83
- let f = yield* V(o, a);
82
+ if (!o || !d.valid) return yield* i.recordLoginFailure(r.email, s, P(a)), L(401, { error: "invalid credentials" });
83
+ let f = yield* H(o, a);
84
84
  if (f) return f;
85
85
  if (d.rehash && (yield* i.updatePassword(o.id, d.rehash).pipe(E.ignore)), o.mfaEnrolledAt) {
86
- let e = v("mfa-pending", 300);
86
+ let e = b("mfa-pending", 300);
87
87
  return yield* i.insertToken({
88
88
  id: l("tok"),
89
89
  tokenHash: e.tokenHash,
90
90
  userId: o.id,
91
91
  purpose: "mfa-pending",
92
92
  expiresAt: e.expiresAt
93
- }).pipe(E.ignore), I(200, {
93
+ }).pipe(E.ignore), L(200, {
94
94
  ok: !0,
95
95
  mfaRequired: !0,
96
96
  pendingToken: e.token,
97
97
  expiresInSeconds: 300
98
98
  });
99
99
  }
100
- let { setCookie: p, subject: m } = yield* B(o, i, a, {
100
+ let { setCookie: p, subject: m } = yield* V(o, i, a, {
101
101
  ipAddress: r.ipAddress ?? null,
102
102
  userAgent: r.userAgent ?? null
103
103
  });
104
- return r.redirectAfter ? L(a.successRedirect ?? "/", { setCookie: p }) : I(200, {
104
+ return r.redirectAfter ? R(a.successRedirect ?? "/", { setCookie: p }) : L(200, {
105
105
  ok: !0,
106
106
  subject: m
107
107
  }, { setCookie: p });
108
- }), U = (e, t, n) => E.gen(function* () {
109
- if (!e.pendingToken) return I(400, { error: "pending token required" });
110
- if (!e.code && !e.recoveryCode) return I(400, { error: "code or recoveryCode required" });
111
- let r = yield* t.consumeToken(_(e.pendingToken), "mfa-pending");
112
- if (!r) return I(401, { error: "invalid or expired challenge" });
108
+ }), W = (e, t, n) => E.gen(function* () {
109
+ if (!e.pendingToken) return L(400, { error: "pending token required" });
110
+ if (!e.code && !e.recoveryCode) return L(400, { error: "code or recoveryCode required" });
111
+ let r = yield* t.consumeToken(y(e.pendingToken), "mfa-pending");
112
+ if (!r) return L(401, { error: "invalid or expired challenge" });
113
113
  let i = yield* t.findById(r.userId);
114
- if (!i || !i.mfaEnrolledAt || !i.mfaSecret) return I(401, { error: "mfa not enrolled" });
115
- let a = (n.now ?? Date.now)(), o = yield* t.isLockedOut(i.email, a);
116
- if (o.locked) return I(429, {
114
+ if (!i || !i.mfaEnrolledAt || !i.mfaSecret) return L(401, { error: "mfa not enrolled" });
115
+ let a = (n.now ?? Date.now)(), o = yield* t.isLockedOut(N(i), a);
116
+ if (o.locked) return L(429, {
117
117
  error: "account_locked",
118
118
  retryAfterSeconds: o.retryAfterSeconds
119
119
  });
120
- let s = yield* V(i, n);
120
+ let s = yield* H(i, n);
121
121
  if (s) return s;
122
- if (!(e.recoveryCode ? yield* t.consumeRecoveryCode(i.id, _(e.recoveryCode.trim())) : yield* h(i.mfaSecret, e.code ?? "").pipe(E.map(() => !0), E.catchTag("TotpVerifyError", () => E.succeed(!1))))) return yield* t.recordLoginFailure(i.email, a, N(n)), I(401, { error: "invalid_code" });
123
- let { setCookie: c, subject: l } = yield* B(i, t, n, {
122
+ if (!(e.recoveryCode ? yield* t.consumeRecoveryCode(i.id, y(e.recoveryCode.trim())) : yield* _(i.mfaSecret, e.code ?? "").pipe(E.map(() => !0), E.catchTag("TotpVerifyError", () => E.succeed(!1))))) return yield* t.recordLoginFailure(N(i), a, P(n)), L(401, { error: "invalid_code" });
123
+ let { setCookie: c, subject: l } = yield* V(i, t, n, {
124
124
  ipAddress: e.ipAddress ?? null,
125
125
  userAgent: e.userAgent ?? null
126
126
  });
127
- return e.redirectAfter ? L(n.successRedirect ?? "/", { setCookie: c }) : I(200, {
127
+ return e.redirectAfter ? R(n.successRedirect ?? "/", { setCookie: c }) : L(200, {
128
128
  ok: !0,
129
129
  subject: l
130
130
  }, { setCookie: c });
131
- }), W = (t, n, r) => E.gen(function* () {
132
- if (!t.email || !t.password) return I(400, { error: "email and password required" });
133
- if (t.password.length < 8) return I(400, { error: "password must be at least 8 characters" });
134
- if (yield* n.findByEmail(t.email)) return I(409, { error: "email already registered" });
135
- let i = t.tenantId ?? r.defaultTenantId, a = yield* x(r.admit, {
131
+ }), G = (t, n, r) => E.gen(function* () {
132
+ if (!t.email || !t.password) return L(400, { error: "email and password required" });
133
+ if (t.password.length < 8) return L(400, { error: "password must be at least 8 characters" });
134
+ if (yield* n.findByEmail(t.email)) return L(409, { error: "email already registered" });
135
+ let i = t.tenantId ?? r.defaultTenantId, a = yield* p(r.admit, {
136
136
  email: t.email,
137
137
  tenantId: i,
138
138
  via: "sign-up"
139
139
  });
140
- if (a) return I(403, {
140
+ if (a) return L(403, {
141
141
  error: "not_admitted",
142
142
  reason: a.reason,
143
143
  message: a.message
@@ -153,15 +153,15 @@ var A = 5, j = 900, M = 900, N = (e) => ({
153
153
  tenantId: i,
154
154
  role: "owner"
155
155
  }).pipe(E.ignore), C(r.emailVerification) && (r.emailVerification?.sendOnSignUp ?? !0) && (yield* w(s, n, r).pipe(E.ignore));
156
- let { setCookie: c, subject: u } = yield* B(s, n, r);
157
- return t.redirectAfter ? L(r.successRedirect ?? "/", { setCookie: c }) : I(200, {
156
+ let { setCookie: c, subject: u } = yield* V(s, n, r);
157
+ return t.redirectAfter ? R(r.successRedirect ?? "/", { setCookie: c }) : L(200, {
158
158
  ok: !0,
159
159
  subject: u
160
160
  }, { setCookie: c });
161
- }).pipe(E.catch((e) => E.succeed(I(500, {
161
+ }).pipe(E.catch((e) => E.succeed(L(500, {
162
162
  error: "signup_failed",
163
163
  cause: String(e)
164
- })))), G = (e) => ({
164
+ })))), K = (e) => ({
165
165
  status: 302,
166
166
  body: "",
167
167
  location: e.successRedirect ?? "/",
@@ -169,61 +169,61 @@ var A = 5, j = 900, M = 900, N = (e) => ({
169
169
  ...e.cookieDomain ? { domain: e.cookieDomain } : {},
170
170
  secure: e.cookieSecure ?? !0
171
171
  })
172
- }), K = (e, t) => E.gen(function* () {
172
+ }), q = (e, t) => E.gen(function* () {
173
173
  let n = yield* t.findById(e.userId);
174
- if (!n) return I(404, { error: "user_not_found" });
175
- let r = p();
176
- return yield* t.setMfaSecret(n.id, r).pipe(E.catch(() => E.succeed(n))), I(200, {
174
+ if (!n) return L(404, { error: "user_not_found" });
175
+ let r = h();
176
+ return yield* t.setMfaSecret(n.id, r).pipe(E.catch(() => E.succeed(n))), L(200, {
177
177
  ok: !0,
178
178
  secret: r,
179
- otpauthUrl: m({
179
+ otpauthUrl: g({
180
180
  issuer: e.issuer,
181
181
  accountName: e.accountName,
182
182
  secret: r
183
183
  })
184
184
  });
185
- }), q = (e, t) => E.gen(function* () {
185
+ }), J = (e, t) => E.gen(function* () {
186
186
  let n = yield* t.findById(e.userId);
187
- if (!n) return I(404, { error: "user_not_found" });
188
- if (!n.mfaSecret) return I(400, { error: "no_enrollment_in_progress" });
189
- if (n.mfaEnrolledAt) return I(409, { error: "already_enrolled" });
190
- if (!(yield* h(n.mfaSecret, e.code).pipe(E.map(() => !0), E.catchTag("TotpVerifyError", () => E.succeed(!1))))) return I(401, { error: "invalid_code" });
187
+ if (!n) return L(404, { error: "user_not_found" });
188
+ if (!n.mfaSecret) return L(400, { error: "no_enrollment_in_progress" });
189
+ if (n.mfaEnrolledAt) return L(409, { error: "already_enrolled" });
190
+ if (!(yield* _(n.mfaSecret, e.code).pipe(E.map(() => !0), E.catchTag("TotpVerifyError", () => E.succeed(!1))))) return L(401, { error: "invalid_code" });
191
191
  yield* t.markMfaEnrolled(n.id).pipe(E.catch(() => E.succeed(n)));
192
- let r = f();
193
- return yield* t.replaceRecoveryCodes(n.id, r.map((e) => _(e))).pipe(E.ignore), I(200, {
192
+ let r = m();
193
+ return yield* t.replaceRecoveryCodes(n.id, r.map((e) => y(e))).pipe(E.ignore), L(200, {
194
194
  ok: !0,
195
195
  enrolled: !0,
196
196
  recoveryCodes: r
197
197
  });
198
- }), J = (e, t) => E.gen(function* () {
198
+ }), Y = (e, t) => E.gen(function* () {
199
199
  let n = yield* t.findById(e.userId);
200
- if (!n) return I(404, { error: "user_not_found" });
201
- if (!n.mfaEnrolledAt) return I(400, { error: "mfa_not_enrolled" });
202
- let r = f();
203
- return yield* t.replaceRecoveryCodes(n.id, r.map((e) => _(e))).pipe(E.ignore), I(200, {
200
+ if (!n) return L(404, { error: "user_not_found" });
201
+ if (!n.mfaEnrolledAt) return L(400, { error: "mfa_not_enrolled" });
202
+ let r = m();
203
+ return yield* t.replaceRecoveryCodes(n.id, r.map((e) => y(e))).pipe(E.ignore), L(200, {
204
204
  ok: !0,
205
205
  recoveryCodes: r
206
206
  });
207
- }), Y = (e, t) => E.gen(function* () {
207
+ }), X = (e, t) => E.gen(function* () {
208
208
  let n = yield* t.findById(e.userId);
209
- return n ? (yield* t.clearMfa(n.id).pipe(E.catch(() => E.succeed(n))), yield* t.replaceRecoveryCodes(n.id, []).pipe(E.ignore), I(200, {
209
+ return n ? (yield* t.clearMfa(n.id).pipe(E.catch(() => E.succeed(n))), yield* t.replaceRecoveryCodes(n.id, []).pipe(E.ignore), L(200, {
210
210
  ok: !0,
211
211
  enrolled: !1
212
- })) : I(404, { error: "user_not_found" });
213
- }), X = (e) => {
214
- let t = b(F(e)), n = D(y, t, {
212
+ })) : L(404, { error: "user_not_found" });
213
+ }), Z = (e) => {
214
+ let t = f(I(e)), n = D(d, t, {
215
215
  httpOnly: !1,
216
216
  sameSite: "lax",
217
217
  secure: e.cookieSecure ?? !0,
218
218
  ...e.cookieDomain ? { domain: e.cookieDomain } : {},
219
219
  path: "/"
220
220
  });
221
- return I(200, { csrfToken: t }, { setCookie: n });
222
- }, Z = (e, t, n) => `${e}${t}${t.includes("?") ? "&" : "?"}token=${encodeURIComponent(n)}`, Q = (e, t, n) => E.gen(function* () {
223
- if (!e.email) return I(400, { error: "email required" });
221
+ return L(200, { csrfToken: t }, { setCookie: n });
222
+ }, Q = (e, t, n) => `${e}${t}${t.includes("?") ? "&" : "?"}token=${encodeURIComponent(n)}`, $ = (e, t, n) => E.gen(function* () {
223
+ if (!e.email) return L(400, { error: "email required" });
224
224
  let r = yield* t.findByEmail(e.email);
225
225
  if (r) {
226
- let i = v("magic-link");
226
+ let i = b("magic-link");
227
227
  yield* t.insertToken({
228
228
  id: l("tok"),
229
229
  tokenHash: i.tokenHash,
@@ -231,9 +231,9 @@ var A = 5, j = 900, M = 900, N = (e) => ({
231
231
  purpose: "magic-link",
232
232
  expiresAt: i.expiresAt
233
233
  }).pipe(E.ignore);
234
- let a = Z(n.appBaseUrl ?? "", e.callbackPath ?? "/auth/magic-link/callback", i.token);
234
+ let a = Q(n.appBaseUrl ?? "", e.callbackPath ?? "/auth/magic-link/callback", i.token);
235
235
  n.sendEmail && (yield* E.tryPromise(() => n.sendEmail({
236
- to: r.email,
236
+ to: r.email ?? e.email,
237
237
  kind: "magic-link",
238
238
  actionUrl: a,
239
239
  subject: "Your sign-in link",
@@ -241,25 +241,25 @@ var A = 5, j = 900, M = 900, N = (e) => ({
241
241
  html: `<p><a href="${a}">Sign in</a> — this link expires shortly.</p>`
242
242
  })).pipe(E.ignore));
243
243
  }
244
- return I(202, { ok: !0 });
245
- }), $ = (e, t, n) => E.gen(function* () {
246
- if (!e.token) return I(400, { error: "token required" });
247
- let r = yield* t.consumeToken(_(e.token), "magic-link");
248
- if (!r) return I(401, { error: "invalid or expired token" });
244
+ return L(202, { ok: !0 });
245
+ }), ee = (e, t, n) => E.gen(function* () {
246
+ if (!e.token) return L(400, { error: "token required" });
247
+ let r = yield* t.consumeToken(y(e.token), "magic-link");
248
+ if (!r) return L(401, { error: "invalid or expired token" });
249
249
  let i = yield* t.findById(r.userId);
250
- if (!i) return I(401, { error: "invalid or expired token" });
251
- let a = yield* t.markEmailVerified(i.id).pipe(E.orElseSucceed(() => i)), o = yield* V(a, n);
250
+ if (!i) return L(401, { error: "invalid or expired token" });
251
+ let a = yield* t.markEmailVerified(i.id).pipe(E.orElseSucceed(() => i)), o = yield* H(a, n);
252
252
  if (o) return o;
253
- let { setCookie: s, subject: c } = yield* B(a, t, n);
254
- return e.redirectAfter ? L(n.successRedirect ?? "/", { setCookie: s }) : I(200, {
253
+ let { setCookie: s, subject: c } = yield* V(a, t, n);
254
+ return e.redirectAfter ? R(n.successRedirect ?? "/", { setCookie: s }) : L(200, {
255
255
  ok: !0,
256
256
  subject: c
257
257
  }, { setCookie: s });
258
- }), ee = (e, t, n) => E.gen(function* () {
259
- if (!e.email) return I(400, { error: "email required" });
258
+ }), te = (e, t, n) => E.gen(function* () {
259
+ if (!e.email) return L(400, { error: "email required" });
260
260
  let r = yield* t.findByEmail(e.email);
261
261
  if (r) {
262
- let i = v("password-reset");
262
+ let i = b("password-reset");
263
263
  yield* t.insertToken({
264
264
  id: l("tok"),
265
265
  tokenHash: i.tokenHash,
@@ -267,9 +267,9 @@ var A = 5, j = 900, M = 900, N = (e) => ({
267
267
  purpose: "password-reset",
268
268
  expiresAt: i.expiresAt
269
269
  }).pipe(E.ignore);
270
- let a = Z(n.appBaseUrl ?? "", e.callbackPath ?? "/auth/password-reset/callback", i.token);
270
+ let a = Q(n.appBaseUrl ?? "", e.callbackPath ?? "/auth/password-reset/callback", i.token);
271
271
  n.sendEmail && (yield* E.tryPromise(() => n.sendEmail({
272
- to: r.email,
272
+ to: r.email ?? e.email,
273
273
  kind: "password-reset",
274
274
  actionUrl: a,
275
275
  subject: "Reset your password",
@@ -277,16 +277,16 @@ var A = 5, j = 900, M = 900, N = (e) => ({
277
277
  html: `<p><a href="${a}">Reset your password</a> — this link expires shortly.</p>`
278
278
  })).pipe(E.ignore));
279
279
  }
280
- return I(202, { ok: !0 });
281
- }), te = (t, n) => E.gen(function* () {
282
- if (!t.token || !t.newPassword) return I(400, { error: "token and newPassword required" });
283
- if (t.newPassword.length < 8) return I(400, { error: "password must be at least 8 characters" });
284
- let r = yield* n.consumeToken(_(t.token), "password-reset");
285
- if (!r) return I(401, { error: "invalid or expired token" });
280
+ return L(202, { ok: !0 });
281
+ }), ne = (t, n) => E.gen(function* () {
282
+ if (!t.token || !t.newPassword) return L(400, { error: "token and newPassword required" });
283
+ if (t.newPassword.length < 8) return L(400, { error: "password must be at least 8 characters" });
284
+ let r = yield* n.consumeToken(y(t.token), "password-reset");
285
+ if (!r) return L(401, { error: "invalid or expired token" });
286
286
  let i = yield* e(t.newPassword).pipe(E.orElseSucceed(() => ""));
287
- return i ? (yield* n.updatePassword(r.userId, i).pipe(E.ignore), yield* n.markEmailVerified(r.userId).pipe(E.ignore), yield* n.revokeAllSessions(r.userId).pipe(E.ignore), I(200, { ok: !0 })) : I(500, { error: "hash_failed" });
288
- }), ne = (e, t) => E.gen(function* () {
289
- return I(200, { sessions: (yield* t.listSessions(e.userId)).map((e) => ({
287
+ return i ? (yield* n.updatePassword(r.userId, i).pipe(E.ignore), yield* n.markEmailVerified(r.userId).pipe(E.ignore), yield* n.revokeAllSessions(r.userId).pipe(E.ignore), L(200, { ok: !0 })) : L(500, { error: "hash_failed" });
288
+ }), re = (e, t) => E.gen(function* () {
289
+ return L(200, { sessions: (yield* t.listSessions(e.userId)).map((e) => ({
290
290
  id: e.id,
291
291
  tenantId: e.tenantId,
292
292
  ipAddress: e.ipAddress ?? null,
@@ -295,43 +295,42 @@ var A = 5, j = 900, M = 900, N = (e) => ({
295
295
  lastSeenAt: e.lastSeenAt.toISOString(),
296
296
  expiresAt: e.expiresAt.toISOString()
297
297
  })) });
298
- }), re = (e, t) => E.gen(function* () {
299
- return (yield* t.revokeSession(e.userId, e.sessionId)) ? I(200, {
298
+ }), ie = (e, t) => E.gen(function* () {
299
+ return (yield* t.revokeSession(e.userId, e.sessionId)) ? L(200, {
300
300
  ok: !0,
301
301
  revoked: 1
302
- }) : I(404, { error: "session_not_found" });
303
- }), ie = (e, t) => E.gen(function* () {
304
- return I(200, {
302
+ }) : L(404, { error: "session_not_found" });
303
+ }), ae = (e, t) => E.gen(function* () {
304
+ return L(200, {
305
305
  ok: !0,
306
306
  revoked: yield* t.revokeAllOtherSessions(e.userId, e.keepSessionId)
307
307
  });
308
- }), ae = (e, t, n, i, o) => E.gen(function* () {
308
+ }), oe = (e, t, n, i, o) => E.gen(function* () {
309
309
  let s = yield* t.findById(e.userId);
310
- if (!s) return I(404, { error: "user_not_found" });
310
+ if (!s) return L(404, { error: "user_not_found" });
311
311
  let c = yield* t.listMemberships(s.id), l = c.find((t) => t.tenantId === e.targetTenantId)?.role ?? null;
312
- if (l === null) return I(403, { error: "not a member of the target tenant" });
312
+ if (l === null) return L(403, { error: "not a member of the target tenant" });
313
313
  let d = e.metadata === void 0 ? void 0 : Object.fromEntries(Object.entries(e.metadata).filter(([e]) => e !== "memberships")), f = k(u(s, { ...d === void 0 ? {} : { metadata: d } }), e.targetTenantId, c);
314
314
  o !== void 0 && f.type === "user" && (yield* E.promise(() => O(o, {
315
315
  type: f.type,
316
316
  id: f.id
317
317
  }, e.targetTenantId)));
318
- let p = e.sessionId ? z(f, e.sessionId) : f, m = a(p, P(n), {
319
- ttlSeconds: R,
318
+ let p = e.sessionId ? B(f, e.sessionId) : f, m = a(p, F(n), {
319
+ ttlSeconds: z,
320
320
  ...n.cookieDomain ? { domain: n.cookieDomain } : {},
321
321
  secure: n.cookieSecure ?? !0
322
322
  });
323
- return e.clientId !== void 0 && i && i(e.clientId, { cookies: { [r]: m.value } }), I(200, {
323
+ return e.clientId !== void 0 && i && i(e.clientId, { cookies: { [r]: m.value } }), L(200, {
324
324
  ok: !0,
325
325
  tenantId: e.targetTenantId,
326
326
  role: l,
327
327
  subject: p
328
328
  }, { setCookie: m.setCookie });
329
- }), oe = (e, t) => E.gen(function* () {
330
- return I(200, { memberships: (yield* t.listMemberships(e.userId)).map((e) => ({
329
+ }), se = (e, t) => E.gen(function* () {
330
+ return L(200, { memberships: (yield* t.listMemberships(e.userId)).map((e) => ({
331
331
  tenantId: e.tenantId,
332
332
  role: e.role,
333
333
  joinedAt: e.joinedAt.toISOString()
334
334
  })) });
335
335
  });
336
- //#endregion
337
- export { g as MFA_PENDING_TTL_S, P as configSessionSecrets, F as configSigningKey, V as guardSubject, X as handleCsrf, oe as handleListMemberships, ne as handleListSessions, $ as handleMagicLinkConsume, Q as handleMagicLinkRequest, K as handleMfaEnrollStart, q as handleMfaEnrollVerify, J as handleMfaRegenerateRecoveryCodes, Y as handleMfaUnenroll, U as handleMfaVerify, te as handlePasswordResetConfirm, ee as handlePasswordResetRequest, ie as handleRevokeAllOtherSessions, re as handleRevokeSession, H as handleSignIn, G as handleSignOut, W as handleSignUp, ae as handleSwitchTenant, B as issueUserSession };
336
+ export { v as MFA_PENDING_TTL_S, F as configSessionSecrets, I as configSigningKey, H as guardSubject, Z as handleCsrf, se as handleListMemberships, re as handleListSessions, ee as handleMagicLinkConsume, $ as handleMagicLinkRequest, q as handleMfaEnrollStart, J as handleMfaEnrollVerify, Y as handleMfaRegenerateRecoveryCodes, X as handleMfaUnenroll, W as handleMfaVerify, ne as handlePasswordResetConfirm, te as handlePasswordResetRequest, ae as handleRevokeAllOtherSessions, ie as handleRevokeSession, U as handleSignIn, K as handleSignOut, G as handleSignUp, oe as handleSwitchTenant, V as issueUserSession };