@voltro/plugin-auth 0.81.0 → 0.82.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.
@@ -1,1334 +0,0 @@
1
- import { hashPassword as e, verifyPassword as t, verifyPasswordWithRehash as n } from "./password.js";
2
- import { SESSION_COOKIE_NAME as r, clearSessionCookie as i, issueSession as a, readSessionKeyed as o, resolveSessionSecret as s, resolveSessionSecrets as c, sessionSecretsOf as l } from "./session.js";
3
- import { a as u, l as d, o as f, s as p, t as m } from "./strategy-MP_KxNuA.js";
4
- import { hashToken as h, mintSecret as g, mintToken as _ } from "./tokens.js";
5
- import { CSRF_COOKIE_NAME as v, CSRF_HEADER_NAME as ee, issueCsrfToken as y, verifyCsrf as te } from "./csrf.js";
6
- import { generateChallenge as b, verifyAssertion as x, verifyRegistration as ne } from "./webauthn.js";
7
- import { createHmac as re, randomBytes as S, timingSafeEqual as ie } from "node:crypto";
8
- import { Data as ae, Effect as C } from "effect";
9
- import { buildSetCookie as oe, readCookie as se } from "@voltro/protocol/session";
10
- import { registerRetention as ce, retentionTtlMsFromEnv as le, writeTenantSelection as ue } from "@voltro/database";
11
- import { IMPERSONATION_METADATA_KEY as w, definePlugin as de, rawImpersonationMark as fe, selectSubjectTenant as pe } from "@voltro/protocol";
12
- //#region src/subjectGuard.ts
13
- var me = { ok: !0 }, he = (e, t) => C.gen(function* () {
14
- for (let n of e) {
15
- let e = yield* n(t);
16
- if (!e.ok) return e;
17
- }
18
- return me;
19
- }), T = 30, ge = 6, _e = 20, ve = "sha1", ye = 1, E = class extends ae.TaggedError("TotpVerifyError") {}, be = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567", xe = (e) => {
20
- let t = 0, n = 0, r = "";
21
- for (let i of e) for (n = n << 8 | i, t += 8; t >= 5;) r += be[n >>> t - 5 & 31], t -= 5;
22
- return t > 0 && (r += be[n << 5 - t & 31]), r;
23
- }, Se = (e) => {
24
- let t = e.toUpperCase().replace(/[\s=]+/g, "");
25
- if (!/^[A-Z2-7]+$/.test(t)) return null;
26
- let n = new Uint8Array(Math.floor(t.length * 5 / 8)), r = 0, i = 0, a = 0;
27
- for (let e of t) {
28
- let t = be.indexOf(e);
29
- if (t === -1) return null;
30
- i = i << 5 | t, r += 5, r >= 8 && (n[a++] = i >>> r - 8 & 255, r -= 8);
31
- }
32
- return n.subarray(0, a);
33
- }, Ce = (e) => {
34
- let t = Buffer.alloc(8);
35
- return t.writeBigUInt64BE(BigInt(e), 0), t;
36
- }, we = (e, t) => {
37
- let n = re(ve, Buffer.from(e));
38
- n.update(Ce(t));
39
- let r = n.digest(), i = r[r.length - 1] & 15, a = (r[i] & 127) << 24 | (r[i + 1] & 255) << 16 | (r[i + 2] & 255) << 8 | r[i + 3] & 255;
40
- return String(a % 10 ** ge).padStart(ge, "0");
41
- }, Te = () => xe(S(_e)), Ee = (e) => `otpauth://totp/${encodeURIComponent(`${e.issuer}:${e.accountName}`)}?${new URLSearchParams({
42
- secret: e.secret,
43
- issuer: e.issuer,
44
- algorithm: "SHA1",
45
- digits: String(ge),
46
- period: String(T)
47
- }).toString()}`, De = (e, t = Date.now()) => {
48
- let n = Se(e);
49
- return n ? we(n, Math.floor(t / 1e3 / T)) : "000000";
50
- }, D = (e, t, n = Date.now()) => C.gen(function* () {
51
- let r = Se(e);
52
- if (!r) return yield* C.fail(new E({ reason: "malformedSecret" }));
53
- let i = t.replace(/\s+/g, "");
54
- if (!/^[0-9]{6}$/.test(i)) return yield* C.fail(new E({ reason: "invalidCode" }));
55
- let a = Math.floor(n / 1e3 / T), o = Buffer.from(i);
56
- for (let e = -1; e <= ye; e++) {
57
- let t = we(r, a + e), n = Buffer.from(t);
58
- if (n.length === o.length && ie(n, o)) return !0;
59
- }
60
- return yield* C.fail(new E({ reason: "invalidCode" }));
61
- }), O = (e = 10) => {
62
- let t = [];
63
- for (let n = 0; n < e; n++) {
64
- let e = xe(S(16)).slice(0, 25).match(/.{1,5}/g) ?? [];
65
- t.push(e.join("-"));
66
- }
67
- return t;
68
- }, Oe = 60, ke = "email_not_verified", k = "emailVerified", Ae = (e) => e?.policy ?? "off", A = (e) => Ae(e) !== "off", j = (e, t) => {
69
- if (e.emailVerifiedAt != null) return !0;
70
- let n = t?.exemptAccountsCreatedBefore;
71
- return n !== void 0 && e.createdAt.getTime() < n.getTime();
72
- }, je = (e) => {
73
- if (e.type !== "user") return null;
74
- let t = e.metadata?.[k];
75
- return typeof t == "boolean" ? t : null;
76
- }, Me = (e) => {
77
- let t = Ae(e) === "strict";
78
- return (n) => C.succeed(!t || j(n, e) ? { ok: !0 } : {
79
- ok: !1,
80
- code: ke,
81
- message: "This account's email address has not been confirmed yet. Request a new confirmation link and follow it, then sign in again."
82
- });
83
- }, M = (e, t) => ({
84
- status: e,
85
- body: JSON.stringify(t),
86
- contentType: "application/json; charset=utf-8"
87
- }), Ne = (e, t, n) => `${e}${t}${t.includes("?") ? "&" : "?"}token=${encodeURIComponent(n)}`, N = (e, t, n) => C.gen(function* () {
88
- let r = n.emailVerification, i = (n.now ?? Date.now)(), a = (r?.resendCooldownSeconds ?? 60) * 1e3;
89
- if (a > 0) {
90
- let n = yield* t.latestToken(e.id, "email-verify");
91
- if (n && i - n.createdAt.getTime() < a) return !1;
92
- }
93
- let o = _("email-verify", r?.ttlSeconds ?? 86400);
94
- if (yield* t.insertToken({
95
- id: u("tok"),
96
- tokenHash: o.tokenHash,
97
- userId: e.id,
98
- purpose: "email-verify",
99
- expiresAt: o.expiresAt
100
- }).pipe(C.ignore), !n.sendEmail) return !1;
101
- let s = Ne(n.appBaseUrl ?? "", r?.callbackPath ?? "/auth/verify-email/callback", o.token);
102
- return yield* C.tryPromise(() => n.sendEmail({
103
- to: e.email,
104
- kind: "email-verify",
105
- actionUrl: s,
106
- subject: "Confirm your email address",
107
- text: `Confirm your email address: ${s}`,
108
- html: `<p><a href="${s}">Confirm your email address</a> — this link expires in 24 hours.</p>`
109
- })).pipe(C.ignore), !0;
110
- }), P = (e, t, n) => C.gen(function* () {
111
- if (!e.email && !e.userId) return M(400, { error: "email or userId required" });
112
- let r = e.userId ? yield* t.findById(e.userId) : yield* t.findByEmail(e.email);
113
- return r && !j(r, n.emailVerification) && (yield* N(r, t, n)), M(202, { ok: !0 });
114
- }), Pe = (e, t) => C.gen(function* () {
115
- if (!e.token) return M(400, { error: "token required" });
116
- let n = yield* t.consumeToken(h(e.token), "email-verify");
117
- if (!n) return M(401, { error: "invalid or expired token" });
118
- let r = yield* t.findById(n.userId);
119
- return r ? (yield* t.markEmailVerified(r.id).pipe(C.ignore), M(200, {
120
- ok: !0,
121
- verified: !0
122
- })) : M(401, { error: "invalid or expired token" });
123
- }), Fe = 5, Ie = 900, Le = 900, Re = (e) => ({
124
- maxAttempts: e.lockout?.maxAttempts ?? Fe,
125
- windowMs: (e.lockout?.windowSeconds ?? Ie) * 1e3,
126
- lockMs: (e.lockout?.lockSeconds ?? Le) * 1e3
127
- }), F = (e) => e.secrets ?? (e.secret === void 0 ? c() : l(e.secret)), ze = (e) => e.secret ?? s(), I = (e, t, n = {}) => ({
128
- status: e,
129
- body: JSON.stringify(t),
130
- contentType: "application/json; charset=utf-8",
131
- ...n
132
- }), L = (e, t = {}) => ({
133
- status: 302,
134
- body: "",
135
- contentType: "text/plain",
136
- location: e,
137
- ...t
138
- }), Be = 604800, Ve = (e, t) => e.type === "user" ? {
139
- ...e,
140
- metadata: {
141
- ...e.metadata ?? {},
142
- sessionId: t
143
- }
144
- } : e, R = (e, t, n, r = {}) => C.gen(function* () {
145
- r.clearLoginFailures !== !1 && (yield* t.clearLoginFailures(e.email));
146
- let i = yield* t.listMemberships(e.id), o = r.sessionId ?? u("sess"), s = r.ttlSeconds ?? Be;
147
- r.sessionRow !== "existing" && (yield* t.insertSession({
148
- id: o,
149
- userId: e.id,
150
- tenantId: e.tenantId,
151
- expiresAt: new Date(Date.now() + s * 1e3),
152
- ipAddress: r.ipAddress ?? null,
153
- userAgent: r.userAgent ?? null
154
- }).pipe(C.ignore));
155
- let c = { ...r.metadata };
156
- A(n.emailVerification) && (c[k] = j(e, n.emailVerification));
157
- let l = Ve(p(e, {
158
- memberships: i,
159
- ...Object.keys(c).length > 0 ? { metadata: c } : {}
160
- }), o);
161
- return {
162
- setCookie: a(l, F(n), {
163
- ttlSeconds: s,
164
- ...n.cookieDomain ? { domain: n.cookieDomain } : {},
165
- secure: n.cookieSecure ?? !0
166
- }).setCookie,
167
- subject: l
168
- };
169
- }), z = (e, t) => C.gen(function* () {
170
- let n = t.subjectGuards ?? [];
171
- if (n.length === 0) return null;
172
- let r = yield* he(n, e);
173
- return r.ok ? null : I(403, {
174
- error: r.code,
175
- message: r.message
176
- });
177
- }), He = (r, i, a) => C.gen(function* () {
178
- if (!r.email || !r.password) return I(400, { error: "email and password required" });
179
- let o = yield* i.findByEmail(r.email), s = (a.now ?? Date.now)(), c = yield* i.isLockedOut(r.email, s);
180
- if (c.locked) return I(429, {
181
- error: "account_locked",
182
- retryAfterSeconds: c.retryAfterSeconds
183
- });
184
- let l = o?.passwordHash, d = typeof l == "string" && l.length > 0 ? yield* n(r.password, l) : yield* C.gen(function* () {
185
- let n = yield* e("decoy-not-a-real-password").pipe(C.orElseSucceed(() => ""));
186
- return yield* t(r.password, n), { valid: !1 };
187
- });
188
- if (!o || !d.valid) return yield* i.recordLoginFailure(r.email, s, Re(a)), I(401, { error: "invalid credentials" });
189
- let f = yield* z(o, a);
190
- if (f) return f;
191
- if (d.rehash && (yield* i.updatePassword(o.id, d.rehash).pipe(C.ignore)), o.mfaEnrolledAt) {
192
- let e = _("mfa-pending", 300);
193
- return yield* i.insertToken({
194
- id: u("tok"),
195
- tokenHash: e.tokenHash,
196
- userId: o.id,
197
- purpose: "mfa-pending",
198
- expiresAt: e.expiresAt
199
- }).pipe(C.ignore), I(200, {
200
- ok: !0,
201
- mfaRequired: !0,
202
- pendingToken: e.token,
203
- expiresInSeconds: 300
204
- });
205
- }
206
- let { setCookie: p, subject: m } = yield* R(o, i, a, {
207
- ipAddress: r.ipAddress ?? null,
208
- userAgent: r.userAgent ?? null
209
- });
210
- return r.redirectAfter ? L(a.successRedirect ?? "/", { setCookie: p }) : I(200, {
211
- ok: !0,
212
- subject: m
213
- }, { setCookie: p });
214
- }), Ue = (e, t, n) => C.gen(function* () {
215
- if (!e.pendingToken) return I(400, { error: "pending token required" });
216
- if (!e.code && !e.recoveryCode) return I(400, { error: "code or recoveryCode required" });
217
- let r = yield* t.consumeToken(h(e.pendingToken), "mfa-pending");
218
- if (!r) return I(401, { error: "invalid or expired challenge" });
219
- let i = yield* t.findById(r.userId);
220
- if (!i || !i.mfaEnrolledAt || !i.mfaSecret) return I(401, { error: "mfa not enrolled" });
221
- let a = (n.now ?? Date.now)(), o = yield* t.isLockedOut(i.email, a);
222
- if (o.locked) return I(429, {
223
- error: "account_locked",
224
- retryAfterSeconds: o.retryAfterSeconds
225
- });
226
- let s = yield* z(i, n);
227
- if (s) return s;
228
- if (!(e.recoveryCode ? yield* t.consumeRecoveryCode(i.id, h(e.recoveryCode.trim())) : yield* D(i.mfaSecret, e.code ?? "").pipe(C.map(() => !0), C.catchTag("TotpVerifyError", () => C.succeed(!1))))) return yield* t.recordLoginFailure(i.email, a, Re(n)), I(401, { error: "invalid_code" });
229
- let { setCookie: c, subject: l } = yield* R(i, t, n, {
230
- ipAddress: e.ipAddress ?? null,
231
- userAgent: e.userAgent ?? null
232
- });
233
- return e.redirectAfter ? L(n.successRedirect ?? "/", { setCookie: c }) : I(200, {
234
- ok: !0,
235
- subject: l
236
- }, { setCookie: c });
237
- }), We = (t, n, r) => C.gen(function* () {
238
- if (!t.email || !t.password) return I(400, { error: "email and password required" });
239
- if (t.password.length < 8) return I(400, { error: "password must be at least 8 characters" });
240
- if (yield* n.findByEmail(t.email)) return I(409, { error: "email already registered" });
241
- let i = t.tenantId ?? r.defaultTenantId, a = yield* e(t.password), o = yield* n.insert({
242
- id: u("user"),
243
- email: t.email,
244
- passwordHash: a,
245
- tenantId: i
246
- });
247
- yield* n.addMembership({
248
- userId: o.id,
249
- tenantId: i,
250
- role: "owner"
251
- }).pipe(C.ignore), A(r.emailVerification) && (r.emailVerification?.sendOnSignUp ?? !0) && (yield* N(o, n, r).pipe(C.ignore));
252
- let { setCookie: s, subject: c } = yield* R(o, n, r);
253
- return t.redirectAfter ? L(r.successRedirect ?? "/", { setCookie: s }) : I(200, {
254
- ok: !0,
255
- subject: c
256
- }, { setCookie: s });
257
- }).pipe(C.catchAll((e) => C.succeed(I(500, {
258
- error: "signup_failed",
259
- cause: String(e)
260
- })))), Ge = (e) => ({
261
- status: 302,
262
- body: "",
263
- location: e.successRedirect ?? "/",
264
- setCookie: i({
265
- ...e.cookieDomain ? { domain: e.cookieDomain } : {},
266
- secure: e.cookieSecure ?? !0
267
- })
268
- }), Ke = (e, t) => C.gen(function* () {
269
- let n = yield* t.findById(e.userId);
270
- if (!n) return I(404, { error: "user_not_found" });
271
- let r = Te();
272
- return yield* t.setMfaSecret(n.id, r).pipe(C.catchAll(() => C.succeed(n))), I(200, {
273
- ok: !0,
274
- secret: r,
275
- otpauthUrl: Ee({
276
- issuer: e.issuer,
277
- accountName: e.accountName,
278
- secret: r
279
- })
280
- });
281
- }), qe = (e, t) => C.gen(function* () {
282
- let n = yield* t.findById(e.userId);
283
- if (!n) return I(404, { error: "user_not_found" });
284
- if (!n.mfaSecret) return I(400, { error: "no_enrollment_in_progress" });
285
- if (n.mfaEnrolledAt) return I(409, { error: "already_enrolled" });
286
- if (!(yield* D(n.mfaSecret, e.code).pipe(C.map(() => !0), C.catchTag("TotpVerifyError", () => C.succeed(!1))))) return I(401, { error: "invalid_code" });
287
- yield* t.markMfaEnrolled(n.id).pipe(C.catchAll(() => C.succeed(n)));
288
- let r = O();
289
- return yield* t.replaceRecoveryCodes(n.id, r.map((e) => h(e))).pipe(C.ignore), I(200, {
290
- ok: !0,
291
- enrolled: !0,
292
- recoveryCodes: r
293
- });
294
- }), Je = (e, t) => C.gen(function* () {
295
- let n = yield* t.findById(e.userId);
296
- if (!n) return I(404, { error: "user_not_found" });
297
- if (!n.mfaEnrolledAt) return I(400, { error: "mfa_not_enrolled" });
298
- let r = O();
299
- return yield* t.replaceRecoveryCodes(n.id, r.map((e) => h(e))).pipe(C.ignore), I(200, {
300
- ok: !0,
301
- recoveryCodes: r
302
- });
303
- }), Ye = (e, t) => C.gen(function* () {
304
- let n = yield* t.findById(e.userId);
305
- return n ? (yield* t.clearMfa(n.id).pipe(C.catchAll(() => C.succeed(n))), yield* t.replaceRecoveryCodes(n.id, []).pipe(C.ignore), I(200, {
306
- ok: !0,
307
- enrolled: !1
308
- })) : I(404, { error: "user_not_found" });
309
- }), Xe = (e) => {
310
- let t = y(ze(e)), n = oe(v, t, {
311
- httpOnly: !1,
312
- sameSite: "lax",
313
- secure: e.cookieSecure ?? !0,
314
- ...e.cookieDomain ? { domain: e.cookieDomain } : {},
315
- path: "/"
316
- });
317
- return I(200, { csrfToken: t }, { setCookie: n });
318
- }, Ze = (e, t, n) => `${e}${t}${t.includes("?") ? "&" : "?"}token=${encodeURIComponent(n)}`, Qe = (e, t, n) => C.gen(function* () {
319
- if (!e.email) return I(400, { error: "email required" });
320
- let r = yield* t.findByEmail(e.email);
321
- if (r) {
322
- let i = _("magic-link");
323
- yield* t.insertToken({
324
- id: u("tok"),
325
- tokenHash: i.tokenHash,
326
- userId: r.id,
327
- purpose: "magic-link",
328
- expiresAt: i.expiresAt
329
- }).pipe(C.ignore);
330
- let a = Ze(n.appBaseUrl ?? "", e.callbackPath ?? "/auth/magic-link/callback", i.token);
331
- n.sendEmail && (yield* C.tryPromise(() => n.sendEmail({
332
- to: r.email,
333
- kind: "magic-link",
334
- actionUrl: a,
335
- subject: "Your sign-in link",
336
- text: `Sign in: ${a}`,
337
- html: `<p><a href="${a}">Sign in</a> — this link expires shortly.</p>`
338
- })).pipe(C.ignore));
339
- }
340
- return I(202, { ok: !0 });
341
- }), $e = (e, t, n) => C.gen(function* () {
342
- if (!e.token) return I(400, { error: "token required" });
343
- let r = yield* t.consumeToken(h(e.token), "magic-link");
344
- if (!r) return I(401, { error: "invalid or expired token" });
345
- let i = yield* t.findById(r.userId);
346
- if (!i) return I(401, { error: "invalid or expired token" });
347
- let a = yield* t.markEmailVerified(i.id).pipe(C.orElseSucceed(() => i)), o = yield* z(a, n);
348
- if (o) return o;
349
- let { setCookie: s, subject: c } = yield* R(a, t, n);
350
- return e.redirectAfter ? L(n.successRedirect ?? "/", { setCookie: s }) : I(200, {
351
- ok: !0,
352
- subject: c
353
- }, { setCookie: s });
354
- }), et = (e, t, n) => C.gen(function* () {
355
- if (!e.email) return I(400, { error: "email required" });
356
- let r = yield* t.findByEmail(e.email);
357
- if (r) {
358
- let i = _("password-reset");
359
- yield* t.insertToken({
360
- id: u("tok"),
361
- tokenHash: i.tokenHash,
362
- userId: r.id,
363
- purpose: "password-reset",
364
- expiresAt: i.expiresAt
365
- }).pipe(C.ignore);
366
- let a = Ze(n.appBaseUrl ?? "", e.callbackPath ?? "/auth/password-reset/callback", i.token);
367
- n.sendEmail && (yield* C.tryPromise(() => n.sendEmail({
368
- to: r.email,
369
- kind: "password-reset",
370
- actionUrl: a,
371
- subject: "Reset your password",
372
- text: `Reset your password: ${a}`,
373
- html: `<p><a href="${a}">Reset your password</a> — this link expires shortly.</p>`
374
- })).pipe(C.ignore));
375
- }
376
- return I(202, { ok: !0 });
377
- }), tt = (t, n) => C.gen(function* () {
378
- if (!t.token || !t.newPassword) return I(400, { error: "token and newPassword required" });
379
- if (t.newPassword.length < 8) return I(400, { error: "password must be at least 8 characters" });
380
- let r = yield* n.consumeToken(h(t.token), "password-reset");
381
- if (!r) return I(401, { error: "invalid or expired token" });
382
- let i = yield* e(t.newPassword).pipe(C.orElseSucceed(() => ""));
383
- return i ? (yield* n.updatePassword(r.userId, i).pipe(C.ignore), yield* n.markEmailVerified(r.userId).pipe(C.ignore), yield* n.revokeAllOtherSessions(r.userId, "__none__").pipe(C.ignore), I(200, { ok: !0 })) : I(500, { error: "hash_failed" });
384
- }), nt = (e, t) => C.gen(function* () {
385
- return I(200, { sessions: (yield* t.listSessions(e.userId)).map((e) => ({
386
- id: e.id,
387
- tenantId: e.tenantId,
388
- ipAddress: e.ipAddress ?? null,
389
- userAgent: e.userAgent ?? null,
390
- createdAt: e.createdAt.toISOString(),
391
- lastSeenAt: e.lastSeenAt.toISOString(),
392
- expiresAt: e.expiresAt.toISOString()
393
- })) });
394
- }), rt = (e, t) => C.gen(function* () {
395
- return (yield* t.revokeSession(e.userId, e.sessionId)) ? I(200, {
396
- ok: !0,
397
- revoked: 1
398
- }) : I(404, { error: "session_not_found" });
399
- }), it = (e, t) => C.gen(function* () {
400
- return I(200, {
401
- ok: !0,
402
- revoked: yield* t.revokeAllOtherSessions(e.userId, e.keepSessionId)
403
- });
404
- }), at = (e, t, n, i, o) => C.gen(function* () {
405
- let s = yield* t.findById(e.userId);
406
- if (!s) return I(404, { error: "user_not_found" });
407
- let c = yield* t.listMemberships(s.id), l = c.find((t) => t.tenantId === e.targetTenantId)?.role ?? null;
408
- if (l === null) return I(403, { error: "not a member of the target tenant" });
409
- let u = e.metadata === void 0 ? void 0 : Object.fromEntries(Object.entries(e.metadata).filter(([e]) => e !== "memberships")), d = pe(p(s, { ...u === void 0 ? {} : { metadata: u } }), e.targetTenantId, c);
410
- o !== void 0 && d.type === "user" && (yield* C.promise(() => ue(o, {
411
- type: d.type,
412
- id: d.id
413
- }, e.targetTenantId)));
414
- let f = e.sessionId ? Ve(d, e.sessionId) : d, m = a(f, F(n), {
415
- ttlSeconds: Be,
416
- ...n.cookieDomain ? { domain: n.cookieDomain } : {},
417
- secure: n.cookieSecure ?? !0
418
- });
419
- return e.clientId !== void 0 && i && i(e.clientId, { cookies: { [r]: m.value } }), I(200, {
420
- ok: !0,
421
- tenantId: e.targetTenantId,
422
- role: l,
423
- subject: f
424
- }, { setCookie: m.setCookie });
425
- }), ot = (e, t) => C.gen(function* () {
426
- return I(200, { memberships: (yield* t.listMemberships(e.userId)).map((e) => ({
427
- tenantId: e.tenantId,
428
- role: e.role,
429
- joinedAt: e.joinedAt.toISOString()
430
- })) });
431
- }), B = (e, t, n = {}) => ({
432
- status: e,
433
- body: JSON.stringify(t),
434
- contentType: "application/json; charset=utf-8",
435
- ...n
436
- }), st = 604800, ct = () => {
437
- let e = /* @__PURE__ */ new Map();
438
- return {
439
- put: (t, n, r) => C.sync(() => {
440
- e.set(t, {
441
- challenge: n,
442
- expiresAt: Date.now() + r * 1e3
443
- });
444
- }),
445
- take: (t) => C.sync(() => {
446
- let n = e.get(t);
447
- return e.delete(t), !n || n.expiresAt < Date.now() ? null : n.challenge;
448
- })
449
- };
450
- }, lt = (e) => ({
451
- put: (t, n, r) => C.gen(function* () {
452
- let i = /* @__PURE__ */ new Date(), a = new Date(i.getTime() + r * 1e3);
453
- (yield* e`
454
- UPDATE "passkeyChallenges"
455
- SET challenge = ${n}, "expiresAt" = ${a}, "createdAt" = ${i}
456
- WHERE key = ${t}
457
- RETURNING key
458
- `).length === 0 && (yield* e`
459
- INSERT INTO "passkeyChallenges" (id, key, challenge, "expiresAt", "createdAt")
460
- VALUES (${u("pkc")}, ${t}, ${n}, ${a}, ${i})
461
- `);
462
- }).pipe(C.asVoid, C.orElseSucceed(() => void 0)),
463
- take: (t) => C.gen(function* () {
464
- let n = (yield* e`
465
- DELETE FROM "passkeyChallenges" WHERE key = ${t}
466
- RETURNING challenge, "expiresAt"
467
- `)[0];
468
- return n && n.expiresAt.getTime() > Date.now() ? n.challenge : null;
469
- }).pipe(C.orElseSucceed(() => null))
470
- }), V = 300, ut = (e, t, n, r) => C.gen(function* () {
471
- if (!(yield* t.findById(e.userId))) return B(404, { error: "user_not_found" });
472
- let i = b();
473
- yield* n.put(`${e.userId}:register`, i, V);
474
- let a = yield* t.listPasskeys(e.userId);
475
- return B(200, {
476
- challenge: i,
477
- rp: {
478
- id: r.rpId,
479
- name: r.rpName
480
- },
481
- user: {
482
- id: e.userId,
483
- name: e.userName,
484
- displayName: e.userName
485
- },
486
- pubKeyCredParams: [{
487
- type: "public-key",
488
- alg: -7
489
- }, {
490
- type: "public-key",
491
- alg: -257
492
- }],
493
- excludeCredentials: a.map((e) => ({
494
- type: "public-key",
495
- id: e.credentialId
496
- })),
497
- authenticatorSelection: {
498
- userVerification: "preferred",
499
- residentKey: "preferred"
500
- },
501
- timeout: V * 1e3
502
- });
503
- }), dt = (e, t, n, r) => C.gen(function* () {
504
- if (!(yield* t.findById(e.userId))) return B(404, { error: "user_not_found" });
505
- let i = yield* n.take(`${e.userId}:register`);
506
- if (!i) return B(400, { error: "no_pending_challenge" });
507
- let a = ne({
508
- clientDataJSON: e.clientDataJSON,
509
- authenticatorData: e.authenticatorData,
510
- expectedChallenge: i,
511
- expectedOrigin: r.origin,
512
- rpId: r.rpId
513
- });
514
- return a.ok ? (yield* t.insertPasskey({
515
- id: u("pk"),
516
- credentialId: a.result.credentialId,
517
- userId: e.userId,
518
- publicKey: a.result.publicKey,
519
- counter: a.result.counter,
520
- transports: e.transports ?? null
521
- }).pipe(C.ignore), B(200, {
522
- ok: !0,
523
- credentialId: a.result.credentialId
524
- })) : B(400, { error: a.error });
525
- }), ft = (e, t, n, r) => C.gen(function* () {
526
- let i = b(), a = e.userId ? `${e.userId}:assert` : "*:assert";
527
- yield* n.put(a, i, V);
528
- let o = e.userId ? (yield* t.listPasskeys(e.userId)).map((e) => ({
529
- type: "public-key",
530
- id: e.credentialId
531
- })) : [];
532
- return B(200, {
533
- challenge: i,
534
- rpId: r.rpId,
535
- allowCredentials: o,
536
- userVerification: "preferred",
537
- timeout: V * 1e3
538
- });
539
- }), pt = (e, t, n, r, i) => C.gen(function* () {
540
- let o = yield* t.findPasskey(e.credentialId);
541
- if (!o) return B(401, { error: "unknown_credential" });
542
- let s = e.userId ? `${e.userId}:assert` : "*:assert", c = yield* n.take(s);
543
- if (!c) return B(400, { error: "no_pending_challenge" });
544
- let l = x({
545
- clientDataJSON: e.clientDataJSON,
546
- authenticatorData: e.authenticatorData,
547
- signature: e.signature,
548
- expectedChallenge: c,
549
- expectedOrigin: r.origin,
550
- rpId: r.rpId,
551
- storedPublicKey: o.publicKey,
552
- storedCounter: o.counter
553
- });
554
- if (!l.ok) return B(401, { error: l.error });
555
- if (!(yield* t.advancePasskeyCounter(e.credentialId, l.result.newCounter)) && l.result.newCounter !== 0) return B(401, { error: "counter_regressed" });
556
- let d = yield* t.findById(o.userId);
557
- if (!d) return B(401, { error: "unknown_credential" });
558
- let f = yield* z(d, i);
559
- if (f) return f;
560
- let m = yield* t.listMemberships(d.id), h = u("sess");
561
- yield* t.insertSession({
562
- id: h,
563
- userId: d.id,
564
- tenantId: d.tenantId,
565
- expiresAt: new Date(Date.now() + st * 1e3),
566
- ipAddress: null,
567
- userAgent: null
568
- }).pipe(C.ignore);
569
- let g = p(d, { memberships: m }), _ = g.type === "user" ? {
570
- ...g,
571
- metadata: {
572
- ...g.metadata ?? {},
573
- sessionId: h
574
- }
575
- } : g, v = a(_, F(i), {
576
- ttlSeconds: st,
577
- ...i.cookieDomain ? { domain: i.cookieDomain } : {},
578
- secure: i.cookieSecure ?? !0
579
- });
580
- return B(200, {
581
- ok: !0,
582
- subject: _
583
- }, { setCookie: v.setCookie });
584
- }), mt = 604800, ht = 500, H = [
585
- "owner",
586
- "admin",
587
- "member",
588
- "viewer"
589
- ], gt = ["owner", "admin"], _t = (e, t) => t.indexOf(e), vt = (e, t = H) => {
590
- let n = _t(e.inviterRole, t);
591
- if (n < 0) return !1;
592
- let r = _t(e.requestedRole, t);
593
- return r < 0 ? n === 0 : r >= n;
594
- }, yt = (e) => {
595
- let t = e?.roleRank ?? H;
596
- return e?.canGrantRole ?? ((e) => vt(e, t));
597
- }, U = (e, t, n = {}) => ({
598
- status: e,
599
- body: JSON.stringify(t),
600
- contentType: "application/json; charset=utf-8",
601
- ...n
602
- }), bt = (e, t, n) => `${e}${t}${t.includes("?") ? "&" : "?"}token=${encodeURIComponent(n)}`, xt = (e) => ({
603
- id: e.id,
604
- email: e.email,
605
- tenantId: e.tenantId,
606
- role: e.role,
607
- invitedByUserId: e.invitedByUserId,
608
- expiresAt: e.expiresAt.toISOString(),
609
- acceptedAt: e.acceptedAt?.toISOString() ?? null,
610
- revokedAt: e.revokedAt?.toISOString() ?? null,
611
- createdAt: e.createdAt.toISOString()
612
- }), W = (e, t, n, r) => C.gen(function* () {
613
- let i = yield* n.membershipRole(e, t);
614
- return i === null || !(r?.inviterRoles ?? gt).includes(i) ? U(403, { error: "not_permitted" }) : { role: i };
615
- }), G = (e) => "status" in e, St = (e, t, n) => C.gen(function* () {
616
- let r = e.email.trim().toLowerCase();
617
- if (!r || !r.includes("@")) return U(400, { error: "a valid email is required" });
618
- if (!e.tenantId) return U(400, { error: "tenantId required" });
619
- if (!e.role) return U(400, { error: "role required" });
620
- let i = n.invitations, a = yield* W(e.inviterUserId, e.tenantId, t, i);
621
- if (G(a)) return a;
622
- if (!yt(i)({
623
- inviterRole: a.role,
624
- requestedRole: e.role,
625
- tenantId: e.tenantId
626
- })) return U(403, {
627
- error: "role_not_grantable",
628
- message: `A '${a.role}' may not invite a '${e.role}'.`
629
- });
630
- let o = new Date((n.now ?? Date.now)());
631
- if ((yield* t.countPendingInvitations(e.tenantId, o)) >= (i?.maxPending ?? 500)) return U(429, { error: "too_many_pending_invitations" });
632
- yield* t.supersedePendingInvitations(e.tenantId, r, e.inviterUserId, o).pipe(C.ignore);
633
- let s = i?.ttlSeconds ?? 604800, c = g(s), l = yield* t.insertInvitation({
634
- id: u("inv"),
635
- tokenHash: c.tokenHash,
636
- email: r,
637
- tenantId: e.tenantId,
638
- role: e.role,
639
- invitedByUserId: e.inviterUserId,
640
- expiresAt: new Date(o.getTime() + s * 1e3)
641
- });
642
- if (n.sendEmail) {
643
- let t = bt(n.appBaseUrl ?? "", i?.callbackPath ?? "/auth/invitations/accept", c.token);
644
- yield* C.tryPromise(() => n.sendEmail({
645
- to: r,
646
- kind: "invitation",
647
- actionUrl: t,
648
- subject: "You have been invited",
649
- text: `You have been invited to join as ${e.role}: ${t}`,
650
- html: `<p>You have been invited to join as <strong>${e.role}</strong>. <a href="${t}">Accept the invitation</a> — this link expires.</p>`
651
- })).pipe(C.ignore);
652
- }
653
- return U(201, {
654
- ok: !0,
655
- invitation: xt(l)
656
- });
657
- }), Ct = (e, t, n) => C.gen(function* () {
658
- let r = yield* W(e.callerUserId, e.tenantId, t, n.invitations);
659
- return G(r) ? r : U(200, { invitations: (yield* t.listInvitations(e.tenantId)).map(xt) });
660
- }), wt = (e, t, n) => C.gen(function* () {
661
- if (!e.invitationId) return U(400, { error: "invitationId required" });
662
- let r = yield* W(e.callerUserId, e.tenantId, t, n.invitations);
663
- if (G(r)) return r;
664
- let i = new Date((n.now ?? Date.now)());
665
- return (yield* t.revokeInvitation(e.invitationId, e.tenantId, e.callerUserId, i)) ? U(200, {
666
- ok: !0,
667
- revoked: 1
668
- }) : U(404, { error: "invitation_not_found" });
669
- }), Tt = (e, t, n = {}) => C.gen(function* () {
670
- if (!e.token) return U(400, { error: "token required" });
671
- let r = yield* t.findInvitationByHash(h(e.token));
672
- if (!r) return U(404, { error: "invalid_invitation" });
673
- let i = new Date((n.now ?? Date.now)());
674
- return U(200, {
675
- ok: !0,
676
- state: r.acceptedAt === null ? r.revokedAt === null ? r.expiresAt.getTime() <= i.getTime() ? "expired" : "pending" : "revoked" : "accepted",
677
- email: r.email,
678
- tenantId: r.tenantId,
679
- role: r.role,
680
- expiresAt: r.expiresAt.toISOString()
681
- });
682
- }), Et = (e, t, n = {}) => C.gen(function* () {
683
- if (!e.token) return U(400, { error: "token required" });
684
- let r = h(e.token), i = new Date((n.now ?? Date.now)()), a = yield* t.findInvitationByHash(r);
685
- if (!a) return U(401, { error: "invalid_or_expired_invitation" });
686
- if (!e.userId) return U(401, {
687
- error: "authentication_required",
688
- email: a.email
689
- });
690
- let o = yield* t.findById(e.userId);
691
- if (!o) return U(401, {
692
- error: "authentication_required",
693
- email: a.email
694
- });
695
- if (o.email.toLowerCase() !== a.email.toLowerCase()) return U(403, {
696
- error: "invitation_email_mismatch",
697
- message: "This invitation was issued to a different email address. Sign in as that address to accept it."
698
- });
699
- let s = yield* t.acceptInvitation(r, o.id, i);
700
- if (!s) return U(401, { error: "invalid_or_expired_invitation" });
701
- let c = yield* t.addMembership({
702
- userId: o.id,
703
- tenantId: s.tenantId,
704
- role: s.role
705
- });
706
- return yield* t.markEmailVerified(o.id).pipe(C.ignore), U(200, {
707
- ok: !0,
708
- membership: {
709
- tenantId: c.tenantId,
710
- role: c.role,
711
- joinedAt: c.joinedAt.toISOString()
712
- }
713
- });
714
- }), Dt = (e, t, n) => C.gen(function* () {
715
- if (!e.token || !e.password) return U(400, { error: "token and password required" });
716
- if (e.password.length < (n.minPasswordLength ?? 8)) return U(400, { error: "password must be at least 8 characters" });
717
- let r = h(e.token), i = new Date((n.now ?? Date.now)()), a = yield* t.findInvitationByHash(r);
718
- if (!a) return U(401, { error: "invalid_or_expired_invitation" });
719
- if (yield* t.findByEmail(a.email)) return U(409, {
720
- error: "account_exists",
721
- message: "An account already exists for this address. Sign in, then accept the invitation."
722
- });
723
- let o = yield* n.hashPassword(e.password).pipe(C.orElseSucceed(() => ""));
724
- if (!o) return U(500, { error: "hash_failed" });
725
- let s = u("user"), c = yield* t.acceptInvitation(r, s, i);
726
- if (!c) return U(401, { error: "invalid_or_expired_invitation" });
727
- let l = yield* t.insert({
728
- id: s,
729
- email: c.email,
730
- passwordHash: o,
731
- tenantId: c.tenantId
732
- }).pipe(C.orElseSucceed(() => null));
733
- return l ? (yield* t.addMembership({
734
- userId: l.id,
735
- tenantId: c.tenantId,
736
- role: c.role
737
- }), yield* t.markEmailVerified(l.id).pipe(C.ignore), U(201, {
738
- ok: !0,
739
- userId: l.id,
740
- tenantId: c.tenantId,
741
- role: c.role
742
- })) : U(409, { error: "account_exists" });
743
- }), Ot = 900, kt = [
744
- "/mfa/enroll/start",
745
- "/mfa/enroll/verify",
746
- "/mfa/unenroll",
747
- "/mfa/recovery-codes/regenerate",
748
- "/passkey/register/options",
749
- "/passkey/register/verify",
750
- "/sessions/revoke-others",
751
- "/switch-tenant",
752
- "/impersonate/start"
753
- ], At = (e) => {
754
- if (typeof e != "object" || !e) return null;
755
- let t = e;
756
- return typeof t.actorUserId != "string" || t.actorUserId.length === 0 || typeof t.grantId != "string" || typeof t.expiresAt != "string" || typeof t.actorSessionId != "string" || typeof t.startedAt != "string" ? null : {
757
- actorUserId: t.actorUserId,
758
- actorSessionId: t.actorSessionId,
759
- grantId: t.grantId,
760
- startedAt: t.startedAt,
761
- expiresAt: t.expiresAt,
762
- ...typeof t.reason == "string" ? { reason: t.reason } : {}
763
- };
764
- }, K = (e) => e.type === "user" ? At(fe(e)) : null, q = (e) => K(e) !== null, jt = () => (e) => {
765
- if (e.type !== "user" || e.metadata === void 0) return e;
766
- let t = At(e.metadata[w]), { metadata: n, ...r } = e;
767
- return {
768
- ...r,
769
- metadata: t === null ? { __redacted: "all" } : {
770
- __redacted: "all-except-impersonation",
771
- [w]: t
772
- }
773
- };
774
- }, J = (e, t, n = {}) => ({
775
- status: e,
776
- body: JSON.stringify(t),
777
- contentType: "application/json; charset=utf-8",
778
- ...n
779
- }), Y = (e, t) => C.suspend(() => {
780
- let n = e.audit(t);
781
- return n === void 0 ? C.void : C.isEffect(n) ? n : C.tryPromise({
782
- try: () => Promise.resolve(n),
783
- catch: (e) => e
784
- });
785
- }).pipe(C.catchAllCause(() => C.void)), Mt = (e, t, n) => C.gen(function* () {
786
- let r = n.impersonation, i = (n.now ?? Date.now)(), a = new Date(i), o = e.actorSubject, s = (t, n, i = {}) => Y(r, {
787
- kind: "refused",
788
- at: a,
789
- actorUserId: i.actorUser?.id ?? (o.type === "user" ? o.id : null),
790
- actorEmail: i.actorUser?.email ?? null,
791
- targetUserId: i.target?.id ?? null,
792
- targetEmail: i.target?.email ?? null,
793
- tenantId: o.tenantId ?? null,
794
- reason: e.reason ?? null,
795
- refusal: n,
796
- ipAddress: e.ipAddress ?? null,
797
- userAgent: e.userAgent ?? null
798
- }).pipe(C.as(J(t, {
799
- error: n,
800
- ...i.message === void 0 ? {} : { message: i.message }
801
- })));
802
- if (o.type !== "user") return yield* s(401, "unauthenticated");
803
- if (q(o)) return yield* s(403, "nested_impersonation", { message: "An impersonated session may not start another impersonation. Stop the current one first." });
804
- if (!e.actorSessionId) return yield* s(400, "no_actor_session");
805
- let c = yield* t.findById(o.id);
806
- if (!c) return yield* s(401, "unauthenticated");
807
- if (!e.targetUserId && !e.targetEmail) return yield* s(400, "target_required", { actorUser: c });
808
- let l = e.targetUserId ? yield* t.findById(e.targetUserId) : yield* t.findByEmail(e.targetEmail);
809
- if (!l) return yield* s(404, "target_not_found", { actorUser: c });
810
- if (l.id === c.id) return yield* s(400, "self_impersonation", {
811
- actorUser: c,
812
- target: l
813
- });
814
- let d = e.reason?.trim() ?? "";
815
- if ((r.requireReason ?? !0) && d.length === 0) return yield* s(400, "reason_required", {
816
- actorUser: c,
817
- target: l,
818
- message: "A reason is required to impersonate a user."
819
- });
820
- if (!(yield* r.authority({
821
- actor: o,
822
- actorUser: c,
823
- target: l
824
- }).pipe(C.catchAllCause(() => C.succeed(!1))))) return yield* s(403, "not_permitted", {
825
- actorUser: c,
826
- target: l
827
- });
828
- if (yield* r.authority({
829
- actor: p(l),
830
- actorUser: l,
831
- target: c
832
- }).pipe(C.catchAllCause(() => C.succeed(!0)))) return yield* s(403, "target_may_impersonate", {
833
- actorUser: c,
834
- target: l,
835
- message: "This user may themselves impersonate others and cannot be impersonated."
836
- });
837
- let f = r.maxDurationSeconds ?? 900, m = Math.max(1, Math.min(e.durationSeconds ?? f, f)), h = new Date(i + m * 1e3), g = u("sess"), _ = yield* t.insertImpersonationGrant({
838
- id: u("imp"),
839
- sessionId: g,
840
- actorUserId: c.id,
841
- actorSessionId: e.actorSessionId,
842
- targetUserId: l.id,
843
- tenantId: l.tenantId,
844
- reason: d.length > 0 ? d : null,
845
- expiresAt: h,
846
- ipAddress: e.ipAddress ?? null,
847
- userAgent: e.userAgent ?? null
848
- }), v = {
849
- actorUserId: c.id,
850
- actorSessionId: e.actorSessionId,
851
- grantId: _.id,
852
- startedAt: _.startedAt.toISOString(),
853
- expiresAt: h.toISOString(),
854
- ...d.length > 0 ? { reason: d } : {}
855
- }, ee = yield* n.issuer.issue(l, {
856
- sessionId: g,
857
- ttlSeconds: m,
858
- metadata: { [w]: v },
859
- ipAddress: e.ipAddress ?? null,
860
- userAgent: e.userAgent ?? null
861
- });
862
- return yield* Y(r, {
863
- kind: "started",
864
- at: a,
865
- actorUserId: c.id,
866
- actorEmail: c.email,
867
- targetUserId: l.id,
868
- targetEmail: l.email,
869
- tenantId: l.tenantId,
870
- sessionId: g,
871
- grantId: _.id,
872
- reason: d.length > 0 ? d : null,
873
- expiresAt: h,
874
- ipAddress: e.ipAddress ?? null,
875
- userAgent: e.userAgent ?? null
876
- }), J(200, {
877
- ok: !0,
878
- impersonation: v,
879
- subject: ee.subject
880
- }, { setCookie: ee.setCookie });
881
- }), Nt = (e, t, n) => C.gen(function* () {
882
- let r = n.impersonation, i = (n.now ?? Date.now)(), a = new Date(i), o = K(e.subject);
883
- if (!o || !e.sessionId) return J(400, { error: "not_impersonating" });
884
- let s = yield* t.findImpersonationGrantBySession(e.sessionId);
885
- yield* t.endImpersonationGrant(e.sessionId, "stopped", a).pipe(C.ignore), e.subject.type === "user" && (yield* t.revokeSession(e.subject.id, e.sessionId).pipe(C.ignore)), n.invalidateSession?.(e.sessionId);
886
- let c = yield* t.findById(o.actorUserId), l = yield* t.findSession(o.actorSessionId), u = l ? Math.floor((l.expiresAt.getTime() - i) / 1e3) : 0;
887
- if (yield* Y(r, {
888
- kind: "stopped",
889
- at: a,
890
- actorUserId: o.actorUserId,
891
- actorEmail: c?.email ?? null,
892
- targetUserId: e.subject.type === "user" ? e.subject.id : null,
893
- targetEmail: null,
894
- tenantId: s?.tenantId ?? e.subject.tenantId ?? null,
895
- sessionId: e.sessionId,
896
- ...s ? { grantId: s.id } : {},
897
- reason: s?.reason ?? null
898
- }), !c || !l || u <= 0) return J(200, {
899
- ok: !0,
900
- restored: !1
901
- }, { setCookie: n.issuer.clear() });
902
- let d = yield* n.issuer.reissue(c, {
903
- sessionId: o.actorSessionId,
904
- ttlSeconds: u
905
- });
906
- return J(200, {
907
- ok: !0,
908
- restored: !0,
909
- subject: d.subject
910
- }, { setCookie: d.setCookie });
911
- }), Pt = (e) => {
912
- for (let t of e) if (Ft(t)) return t.auth.sessionStrategy;
913
- return null;
914
- }, Ft = (e) => typeof e.auth == "object" && e.auth !== null && typeof e.auth.sessionStrategy == "object", It = "/auth", Lt = (e) => {
915
- let t = Buffer.from(e.rawBody).toString("utf8");
916
- if (!t) return {};
917
- if ((e.headers["content-type"] ?? "").includes("application/x-www-form-urlencoded")) {
918
- let e = new URLSearchParams(t), n = {};
919
- for (let [t, r] of e) n[t] = r;
920
- return n;
921
- }
922
- try {
923
- let e = JSON.parse(t);
924
- return typeof e == "object" && e ? e : {};
925
- } catch {
926
- return {};
927
- }
928
- }, X = (e) => ({
929
- status: e.status,
930
- ...e.body === void 0 ? {} : { body: e.body },
931
- ...e.contentType === void 0 ? {} : { contentType: e.contentType },
932
- headers: {
933
- ...e.setCookie === void 0 ? {} : { "set-cookie": e.setCookie },
934
- ...e.location === void 0 ? {} : { location: e.location }
935
- }
936
- }), Z = (e, t) => ({
937
- status: e,
938
- body: JSON.stringify(t),
939
- contentType: "application/json; charset=utf-8"
940
- }), Q = (e) => typeof e == "string" ? e : void 0, $ = (e, t) => typeof t == "string" && t.length > 0 ? { [e]: t } : {}, Rt = (t) => {
941
- let n = t.prefix ?? It, r = t.store, s = t.challengeStore ?? ct(), c = { current: void 0 }, l = {
942
- ...t.secret === void 0 ? {} : { secret: t.secret },
943
- ...t.secrets === void 0 ? {} : { secrets: t.secrets },
944
- defaultTenantId: t.defaultTenantId,
945
- ...t.cookieDomain === void 0 ? {} : { cookieDomain: t.cookieDomain },
946
- ...t.cookieSecure === void 0 ? {} : { cookieSecure: t.cookieSecure },
947
- ...t.successRedirect === void 0 ? {} : { successRedirect: t.successRedirect },
948
- ...t.sendEmail === void 0 ? {} : { sendEmail: t.sendEmail },
949
- ...t.appBaseUrl === void 0 ? {} : { appBaseUrl: t.appBaseUrl },
950
- subjectGuards: [...t.subjectGuards ?? [], ...A(t.emailVerification) ? [Me(t.emailVerification)] : []],
951
- ...t.lockout === void 0 ? {} : { lockout: t.lockout },
952
- ...t.now === void 0 ? {} : { now: t.now },
953
- ...t.emailVerification === void 0 ? {} : { emailVerification: t.emailVerification }
954
- }, u = {
955
- ...t.appBaseUrl === void 0 ? {} : { appBaseUrl: t.appBaseUrl },
956
- ...t.sendEmail === void 0 ? {} : { sendEmail: t.sendEmail },
957
- ...t.invitations === void 0 ? {} : { invitations: t.invitations },
958
- ...t.now === void 0 ? {} : { now: t.now }
959
- };
960
- t.invitations !== void 0 && ce({
961
- source: "plugin",
962
- table: "invitations",
963
- timeColumn: "createdAt",
964
- ttlMs: le(process.env.VOLTRO_INVITATIONS_TTL_HOURS, 2160),
965
- envVar: "VOLTRO_INVITATIONS_TTL_HOURS"
966
- }), t.impersonation !== void 0 && ce({
967
- source: "plugin",
968
- table: "impersonationGrants",
969
- timeColumn: "startedAt",
970
- ttlMs: le(process.env.VOLTRO_IMPERSONATION_GRANTS_TTL_HOURS, 8760),
971
- envVar: "VOLTRO_IMPERSONATION_GRANTS_TTL_HOURS"
972
- });
973
- let p = d(r, t.sessionRevocation ?? {}), h = async (e) => {
974
- let t = F(l), n = o(e.headers.cookie, t);
975
- if (!n) return null;
976
- let i = f(n.subject);
977
- if (i && !await C.runPromise(p.isLive(i))) return null;
978
- if (n.renew || n.kid !== t.current.kid) {
979
- let e = Math.max(n.exp - n.iat, 60), o = a(n.subject, t, {
980
- ttlSeconds: e,
981
- ...l.cookieDomain ? { domain: l.cookieDomain } : {},
982
- secure: l.cookieSecure ?? !0
983
- });
984
- return i && await C.runPromise(r.touchSession(i, new Date(Date.now() + e * 1e3))), {
985
- subject: n.subject,
986
- sessionId: i,
987
- renewSetCookie: o.setCookie
988
- };
989
- }
990
- return {
991
- subject: n.subject,
992
- sessionId: i
993
- };
994
- }, g = (e, t) => {
995
- if (!t) return e;
996
- let n = e.headers ?? {};
997
- return n["set-cookie"] ? e : {
998
- ...e,
999
- headers: {
1000
- ...n,
1001
- "set-cookie": t
1002
- }
1003
- };
1004
- }, _ = (e) => {
1005
- let n = e.headers[ee], r = se(e.headers.cookie, v);
1006
- return te(n, r, ze(t));
1007
- }, y = (e, t) => e.path === `${n}${t}`, b = (e) => C.runPromise(e).then(X), x = {
1008
- issue: (e, t) => R(e, r, l, {
1009
- sessionId: t.sessionId,
1010
- ttlSeconds: t.ttlSeconds,
1011
- metadata: t.metadata,
1012
- ipAddress: t.ipAddress ?? null,
1013
- userAgent: t.userAgent ?? null,
1014
- clearLoginFailures: !1
1015
- }),
1016
- reissue: (e, t) => R(e, r, l, {
1017
- sessionId: t.sessionId,
1018
- ttlSeconds: t.ttlSeconds,
1019
- sessionRow: "existing",
1020
- clearLoginFailures: !1
1021
- }),
1022
- clear: () => i({
1023
- ...l.cookieDomain ? { domain: l.cookieDomain } : {},
1024
- secure: l.cookieSecure ?? !0
1025
- })
1026
- }, ne = {
1027
- method: "*",
1028
- path: n,
1029
- handle: async (i) => {
1030
- let a = i.method === "GET" ? {} : Lt(i);
1031
- if (i.method === "GET" && y(i, "/csrf")) return X(Xe(l));
1032
- if (i.method === "POST" && y(i, "/sign-in")) return b(He({
1033
- email: Q(a.email) ?? "",
1034
- password: Q(a.password) ?? "",
1035
- ...a.redirectAfter === void 0 ? {} : { redirectAfter: !!a.redirectAfter },
1036
- ...i.remoteAddr ? { ipAddress: i.remoteAddr } : {},
1037
- ...i.headers["user-agent"] ? { userAgent: i.headers["user-agent"] } : {}
1038
- }, r, l));
1039
- if (i.method === "POST" && y(i, "/sign-up")) return b(We({
1040
- email: Q(a.email) ?? "",
1041
- password: Q(a.password) ?? "",
1042
- ...$("tenantId", a.tenantId),
1043
- ...a.redirectAfter === void 0 ? {} : { redirectAfter: !!a.redirectAfter }
1044
- }, r, l));
1045
- if (i.method === "POST" && y(i, "/sign-out")) {
1046
- let e = await h(i);
1047
- return e && e.subject.type === "user" && e.sessionId && (await C.runPromise(r.revokeSession(e.subject.id, e.sessionId)), p.invalidate(e.sessionId)), X(Ge(l));
1048
- }
1049
- if (i.method === "POST" && y(i, "/magic-link")) return b(Qe({
1050
- email: Q(a.email) ?? "",
1051
- ...$("callbackPath", a.callbackPath)
1052
- }, r, l));
1053
- if (i.method === "POST" && y(i, "/magic-link/callback")) return b($e({
1054
- token: Q(a.token) ?? "",
1055
- ...a.redirectAfter === void 0 ? {} : { redirectAfter: !!a.redirectAfter }
1056
- }, r, l));
1057
- if (i.method === "POST" && y(i, "/password-reset")) return b(et({
1058
- email: Q(a.email) ?? "",
1059
- ...$("callbackPath", a.callbackPath)
1060
- }, r, l));
1061
- if (i.method === "POST" && y(i, "/password-reset/confirm")) return b(tt({
1062
- token: Q(a.token) ?? "",
1063
- newPassword: Q(a.newPassword) ?? ""
1064
- }, r));
1065
- if (i.method === "POST" && y(i, "/verify-email")) {
1066
- let e = Q(a.email);
1067
- if (e) return b(P({ email: e }, r, l));
1068
- let t = await h(i);
1069
- return !t || t.subject.type !== "user" ? Z(400, { error: "email required" }) : b(P({ userId: t.subject.id }, r, l));
1070
- }
1071
- if (i.method === "POST" && y(i, "/verify-email/callback")) return b(Pe({ token: Q(a.token) ?? "" }, r));
1072
- if (t.invitations && i.method === "POST" && y(i, "/invitations/preview")) return b(Tt({ token: Q(a.token) ?? "" }, r, u));
1073
- if (t.invitations && i.method === "POST" && y(i, "/invitations/sign-up")) return b(Dt({
1074
- token: Q(a.token) ?? "",
1075
- password: Q(a.password) ?? ""
1076
- }, r, {
1077
- ...u,
1078
- hashPassword: e
1079
- }));
1080
- if (i.method === "POST" && y(i, "/mfa/verify")) return b(Ue({
1081
- pendingToken: Q(a.pendingToken) ?? "",
1082
- ...$("code", a.code),
1083
- ...$("recoveryCode", a.recoveryCode),
1084
- ...a.redirectAfter === void 0 ? {} : { redirectAfter: !!a.redirectAfter },
1085
- ...i.remoteAddr ? { ipAddress: i.remoteAddr } : {},
1086
- ...i.headers["user-agent"] ? { userAgent: i.headers["user-agent"] } : {}
1087
- }, r, l));
1088
- if (t.passkey && i.method === "POST" && y(i, "/passkey/assert/options")) return b(ft({ ...$("userId", a.userId) }, r, s, t.passkey));
1089
- if (t.passkey && i.method === "POST" && y(i, "/passkey/assert/verify")) return b(pt({
1090
- credentialId: Q(a.credentialId) ?? "",
1091
- clientDataJSON: Q(a.clientDataJSON) ?? "",
1092
- authenticatorData: Q(a.authenticatorData) ?? "",
1093
- signature: Q(a.signature) ?? "",
1094
- ...$("userId", a.userId)
1095
- }, r, s, t.passkey, l));
1096
- let o = await h(i), d = o?.subject ?? null, f = () => !d || d.type !== "user" ? Z(401, { error: "unauthenticated" }) : { userId: d.id }, m = () => _(i) ? null : Z(403, { error: "csrf" }), v = (e) => b(e).then((e) => g(e, o?.renewSetCookie));
1097
- if (d && q(d)) {
1098
- let e = i.path.startsWith(n) ? i.path.slice(n.length) : "";
1099
- if (kt.includes(e)) return Z(403, {
1100
- error: "forbidden_while_impersonating",
1101
- message: "This action changes account credentials or session scope and is refused while impersonating. Stop the impersonation first."
1102
- });
1103
- }
1104
- if (i.method === "GET" && y(i, "/session")) {
1105
- let e = f();
1106
- return "status" in e ? e : g(Z(200, {
1107
- ok: !0,
1108
- subject: d,
1109
- renewed: o?.renewSetCookie !== void 0
1110
- }), o?.renewSetCookie);
1111
- }
1112
- if (i.method === "GET" && y(i, "/sessions")) {
1113
- let e = f();
1114
- return "status" in e ? e : v(nt({ userId: e.userId }, r));
1115
- }
1116
- if (i.method === "GET" && y(i, "/memberships")) {
1117
- let e = f();
1118
- return "status" in e ? e : v(ot({ userId: e.userId }, r));
1119
- }
1120
- if (i.method === "POST" && y(i, "/sessions/revoke")) {
1121
- let e = f();
1122
- if ("status" in e) return e;
1123
- let t = m();
1124
- if (t) return t;
1125
- let n = Q(a.sessionId) ?? "";
1126
- return v(rt({
1127
- userId: e.userId,
1128
- sessionId: n
1129
- }, r)).then((e) => (n && p.invalidate(n), e));
1130
- }
1131
- if (i.method === "POST" && y(i, "/sessions/revoke-others")) {
1132
- let e = f();
1133
- return "status" in e ? e : m() || v(it({
1134
- userId: e.userId,
1135
- keepSessionId: o?.sessionId ?? "__none__"
1136
- }, r));
1137
- }
1138
- if (t.invitations && i.method === "POST" && y(i, "/invitations")) {
1139
- let e = f();
1140
- return "status" in e ? e : m() || v(St({
1141
- inviterUserId: e.userId,
1142
- tenantId: Q(a.tenantId) ?? d?.tenantId ?? "",
1143
- email: Q(a.email) ?? "",
1144
- role: Q(a.role) ?? ""
1145
- }, r, u));
1146
- }
1147
- if (t.invitations && i.method === "GET" && i.path === `${n}/invitations`) {
1148
- let e = f();
1149
- return "status" in e ? e : v(Ct({
1150
- callerUserId: e.userId,
1151
- tenantId: d?.tenantId ?? ""
1152
- }, r, u));
1153
- }
1154
- if (t.invitations && i.method === "POST" && y(i, "/invitations/revoke")) {
1155
- let e = f();
1156
- return "status" in e ? e : m() || v(wt({
1157
- callerUserId: e.userId,
1158
- tenantId: Q(a.tenantId) ?? d?.tenantId ?? "",
1159
- invitationId: Q(a.invitationId) ?? ""
1160
- }, r, u));
1161
- }
1162
- if (t.invitations && i.method === "POST" && y(i, "/invitations/accept")) {
1163
- let e = f();
1164
- if (!("status" in e)) {
1165
- let e = m();
1166
- if (e) return e;
1167
- }
1168
- return v(Et({
1169
- token: Q(a.token) ?? "",
1170
- ..."status" in e ? {} : { userId: e.userId }
1171
- }, r, u));
1172
- }
1173
- if (t.impersonation && i.method === "POST" && y(i, "/impersonate/start")) {
1174
- let e = f();
1175
- return "status" in e ? e : m() || b(Mt({
1176
- actorSubject: d,
1177
- actorSessionId: o?.sessionId ?? "",
1178
- ...$("targetUserId", a.userId),
1179
- ...$("targetEmail", a.email),
1180
- ...$("reason", a.reason),
1181
- ...typeof a.durationSeconds == "number" ? { durationSeconds: a.durationSeconds } : {},
1182
- ipAddress: i.remoteAddr ?? null,
1183
- userAgent: i.headers["user-agent"] ?? null
1184
- }, r, {
1185
- impersonation: t.impersonation,
1186
- issuer: x,
1187
- ...t.now === void 0 ? {} : { now: t.now },
1188
- invalidateSession: (e) => p.invalidate(e)
1189
- }));
1190
- }
1191
- if (t.impersonation && i.method === "POST" && y(i, "/impersonate/stop")) {
1192
- let e = f();
1193
- return "status" in e ? e : m() || b(Nt({
1194
- subject: d,
1195
- sessionId: o?.sessionId ?? ""
1196
- }, r, {
1197
- impersonation: t.impersonation,
1198
- issuer: x,
1199
- ...t.now === void 0 ? {} : { now: t.now },
1200
- invalidateSession: (e) => p.invalidate(e)
1201
- }));
1202
- }
1203
- if (i.method === "POST" && y(i, "/switch-tenant")) {
1204
- let e = f();
1205
- return "status" in e ? e : m() || b(at({
1206
- userId: e.userId,
1207
- targetTenantId: Q(a.targetTenantId) ?? "",
1208
- ...typeof a.clientId == "number" ? { clientId: a.clientId } : {},
1209
- ...o?.sessionId ? { sessionId: o.sessionId } : {},
1210
- ...d?.type === "user" && d.metadata ? { metadata: d.metadata } : {}
1211
- }, r, l, t.rebind, c.current));
1212
- }
1213
- if (t.mfa && i.method === "POST" && y(i, "/mfa/enroll/start")) {
1214
- let e = f();
1215
- if ("status" in e) return e;
1216
- let n = m();
1217
- if (n) return n;
1218
- let i = await C.runPromise(r.findById(e.userId));
1219
- return v(Ke({
1220
- userId: e.userId,
1221
- issuer: t.mfa.issuer,
1222
- accountName: i?.email ?? e.userId
1223
- }, r));
1224
- }
1225
- if (t.mfa && i.method === "POST" && y(i, "/mfa/enroll/verify")) {
1226
- let e = f();
1227
- return "status" in e ? e : m() || v(qe({
1228
- userId: e.userId,
1229
- code: Q(a.code) ?? ""
1230
- }, r));
1231
- }
1232
- if (t.mfa && i.method === "POST" && y(i, "/mfa/unenroll")) {
1233
- let e = f();
1234
- return "status" in e ? e : m() || v(Ye({ userId: e.userId }, r));
1235
- }
1236
- if (t.mfa && i.method === "POST" && y(i, "/mfa/recovery-codes/regenerate")) {
1237
- let e = f();
1238
- return "status" in e ? e : m() || v(Je({ userId: e.userId }, r));
1239
- }
1240
- if (t.passkey && i.method === "POST" && y(i, "/passkey/register/options")) {
1241
- let e = f();
1242
- return "status" in e ? e : m() || v(ut({
1243
- userId: e.userId,
1244
- userName: Q(a.userName) ?? e.userId
1245
- }, r, s, t.passkey));
1246
- }
1247
- if (t.passkey && i.method === "POST" && y(i, "/passkey/register/verify")) {
1248
- let e = f();
1249
- return "status" in e ? e : m() || v(dt({
1250
- userId: e.userId,
1251
- credentialId: Q(a.credentialId) ?? "",
1252
- clientDataJSON: Q(a.clientDataJSON) ?? "",
1253
- authenticatorData: Q(a.authenticatorData) ?? "",
1254
- ...$("transports", a.transports)
1255
- }, r, s, t.passkey));
1256
- }
1257
- return !t.passkey && i.path.startsWith(`${n}/passkey/`) ? Z(501, { error: "passkey_not_configured" }) : Z(404, { error: "not_found" });
1258
- }
1259
- }, re = de({
1260
- name: t.name ? `@voltro/plugin-auth#${t.name}` : "@voltro/plugin-auth",
1261
- description: "Auth routes — mounts the full auth surface (password, magic-link, password-reset, CSRF, session probe/list/revoke, memberships, switch-tenant, passkey ceremonies) under /auth on the framework listener, with keyed secret rotation + request-time revocation.",
1262
- permissions: [],
1263
- declaredEnv: zt,
1264
- httpRoutes: [ne],
1265
- bindDataStore: (e) => {
1266
- c.current = e;
1267
- },
1268
- onActivate: (e) => C.sync(() => {
1269
- e.logger.info("auth routes active", {
1270
- prefix: n,
1271
- passkey: t.passkey ? t.passkey.rpId : "disabled",
1272
- email: t.sendEmail ? "wired" : "unwired",
1273
- revocationWindowMs: p.ttlMs,
1274
- emailVerification: t.emailVerification?.policy ?? "off",
1275
- invitations: t.invitations ? "enabled" : "disabled",
1276
- impersonation: t.impersonation ? `enabled(max ${t.impersonation.maxDurationSeconds ?? 900}s)` : "disabled"
1277
- });
1278
- }),
1279
- onDeactivate: () => C.void
1280
- }), S = t.secrets ?? t.secret;
1281
- return {
1282
- ...re,
1283
- auth: { sessionStrategy: m({
1284
- ...S === void 0 ? {} : { secret: S },
1285
- store: r,
1286
- revocation: p
1287
- }) }
1288
- };
1289
- }, zt = [
1290
- {
1291
- name: "VOLTRO_SESSION_SECRET_PREVIOUS",
1292
- required: !1,
1293
- secret: !0,
1294
- description: "The session secret being rotated AWAY from. While set, cookies signed with it still verify (and get re-issued under the current secret on the next authenticated /auth response). Set the new secret as VOLTRO_SESSION_SECRET, move the old one here for one session lifetime, then drop it.",
1295
- example: "the-previous-signing-secret"
1296
- },
1297
- {
1298
- name: "VOLTRO_SESSION_KID",
1299
- required: !1,
1300
- secret: !1,
1301
- description: "Non-secret key-id label stamped into cookies signed with the current secret (default 'k0').",
1302
- example: "2026-07"
1303
- },
1304
- {
1305
- name: "VOLTRO_SESSION_KID_PREVIOUS",
1306
- required: !1,
1307
- secret: !1,
1308
- description: "Non-secret key-id label for the previous secret (default 'k-previous').",
1309
- example: "2026-01"
1310
- },
1311
- {
1312
- name: "VOLTRO_INVITATIONS_TTL_HOURS",
1313
- required: !1,
1314
- secret: !1,
1315
- description: "How long an `invitations` row is kept before the retention sweep DELETES it. Default 2160 (90 days). Only armed when `invitations` is configured. Every invitation is dead long before this — the lifetime that decides redeemability is `invitations.ttlSeconds` (7 days by default) — so this bounds the history, not the grant.",
1316
- example: "2160"
1317
- },
1318
- {
1319
- name: "VOLTRO_IMPERSONATION_GRANTS_TTL_HOURS",
1320
- required: !1,
1321
- secret: !1,
1322
- description: "How long an `impersonationGrants` row is kept before the retention sweep DELETES it. Default 8760 (365 days), matching the audit log — this table answers \"who was acting as whom, when\", which is compliance evidence rather than operational state. Only armed when `impersonation` is configured. Raise it to your audit-retention obligation; lowering it shortens how far back an impersonation can be traced.",
1323
- example: "8760"
1324
- },
1325
- {
1326
- name: "VOLTRO_AUTH_SCOPE_CACHE_TTL_MS",
1327
- required: !1,
1328
- secret: !1,
1329
- description: "How long a resolved authority (auth.resolveScopes) stays cached, in milliseconds. Default 30000 — the same window as the session-revocation check, so the two per-request store reads expire together. This is the staleness window for a role change: remove a permission and every live session observes it within this long. Set it to 0 to resolve on every request (staleness zero, one store read per request). An explicit ttlMs in code wins over this variable.",
1330
- example: "30000"
1331
- }
1332
- ];
1333
- //#endregion
1334
- export { Pe as $, ot as A, et as B, pt as C, F as D, ct as E, qe as F, We as G, rt as H, Je as I, ke as J, at as K, Ye as L, $e as M, Qe as N, z as O, Ke as P, Me as Q, Ue as R, ft as S, dt as T, He as U, it as V, Ge as W, Oe as X, k as Y, A as Z, St as _, Mt as a, O as at, wt as b, K as c, Ee as ct, H as d, P as et, ht as f, Dt as g, Et as h, kt as i, E as it, nt as j, Xe as k, q as l, D as lt, vt as m, Pt as n, N as nt, Nt as o, De as ot, mt as p, R as q, Ot as r, j as rt, jt as s, Te as st, Rt as t, je as tt, gt as u, he as ut, Ct as v, ut as w, lt as x, Tt as y, tt as z };