@voltro/plugin-auth 0.84.0 → 0.86.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/plugin.js CHANGED
@@ -1,5 +1,498 @@
1
- import "./password.js";
2
- import "./session.js";
3
- import { n as e, t } from "./plugin-CNW-X70p.js";
4
- import "./csrf.js";
5
- export { t as authRoutesPlugin, e as getAuthSessionStrategy };
1
+ import { hashPassword as e } from "./password.js";
2
+ import { clearSessionCookie as t, issueSession as n, readSessionFromHeaders as r } from "./session.js";
3
+ import { makeSessionRevocationChecker as i } from "./revocation.js";
4
+ import { sessionIdOfSubject as a } from "./userStore.js";
5
+ import { deferredUserStore as o } from "./appUserStore.js";
6
+ import { CSRF_COOKIE_NAME as s, CSRF_HEADER_NAME as c, verifyCsrf as ee } from "./csrf.js";
7
+ import { emailVerificationActive as te, emailVerificationGuard as ne, handleEmailVerifyConfirm as re, handleEmailVerifyRequest as l } from "./emailVerification.js";
8
+ import { configSessionSecrets as ie, configSigningKey as ae, handleCsrf as oe, handleListMemberships as se, handleListSessions as ce, handleMagicLinkConsume as le, handleMagicLinkRequest as ue, handleMfaEnrollStart as de, handleMfaEnrollVerify as fe, handleMfaRegenerateRecoveryCodes as pe, handleMfaUnenroll as me, handleMfaVerify as he, handlePasswordResetConfirm as ge, handlePasswordResetRequest as _e, handleRevokeAllOtherSessions as ve, handleRevokeSession as ye, handleSignIn as be, handleSignOut as xe, handleSignUp as Se, handleSwitchTenant as Ce, issueUserSession as u } from "./handlers.js";
9
+ import { handlePasskeyAssertOptions as d, handlePasskeyAssertVerify as f, handlePasskeyRegisterOptions as p, handlePasskeyRegisterVerify as m, memoryChallengeStore as h } from "./passkeyHandlers.js";
10
+ import { handleAcceptInvitation as g, handleAcceptInvitationWithSignUp as _, handleCreateInvitation as v, handleListInvitations as y, handlePreviewInvitation as b, handleRevokeInvitation as x } from "./invitations.js";
11
+ import { IMPERSONATION_FORBIDDEN_SUBPATHS as S, handleImpersonationStart as C, handleImpersonationStop as w, isImpersonated as T } from "./impersonation.js";
12
+ import { handleNativeAuthorize as E, handleNativeComplete as D, handleNativeExchange as O } from "./nativeRoutes.js";
13
+ import { voltroPasswordStrategy as k } from "./strategy.js";
14
+ import { Effect as A } from "effect";
15
+ import { SESSION_HEADER_NAME as j, readCookie as we } from "@voltro/protocol/session";
16
+ import { registerRetention as M, retentionTtlMsFromEnv as N } from "@voltro/database";
17
+ import { definePlugin as Te } from "@voltro/protocol";
18
+ //#region src/plugin.ts
19
+ var P = (e) => {
20
+ for (let t of e) if (F(t)) return t.auth.sessionStrategy;
21
+ return null;
22
+ }, F = (e) => typeof e.auth == "object" && e.auth !== null && typeof e.auth.sessionStrategy == "object", Ee = "/auth", De = (e) => {
23
+ let t = Buffer.from(e.rawBody).toString("utf8");
24
+ if (!t) return {};
25
+ if ((e.headers["content-type"] ?? "").includes("application/x-www-form-urlencoded")) {
26
+ let e = new URLSearchParams(t), n = {};
27
+ for (let [t, r] of e) n[t] = r;
28
+ return n;
29
+ }
30
+ try {
31
+ let e = JSON.parse(t);
32
+ return typeof e == "object" && e ? e : {};
33
+ } catch {
34
+ return {};
35
+ }
36
+ }, I = (e) => ({
37
+ status: e.status,
38
+ ...e.body === void 0 ? {} : { body: e.body },
39
+ ...e.contentType === void 0 ? {} : { contentType: e.contentType },
40
+ headers: {
41
+ ...e.setCookie === void 0 ? {} : { "set-cookie": e.setCookie },
42
+ ...e.location === void 0 ? {} : { location: e.location }
43
+ }
44
+ }), Oe = "actors", L = (e, t) => ({
45
+ status: e,
46
+ body: JSON.stringify(t),
47
+ contentType: "application/json; charset=utf-8"
48
+ }), R = (e) => typeof e == "string" ? e : void 0, z = (e, t) => typeof t == "string" && t.length > 0 ? { [e]: t } : {}, B = (P) => {
49
+ let F = P.prefix ?? Ee, B = { current: typeof P.store == "function" ? void 0 : P.store }, V = typeof P.store == "function" ? o(() => B.current) : P.store, H = P.challengeStore ?? h(), U = { current: void 0 }, W = P.admit === void 0 ? void 0 : (e, t) => P.admit(e, { store: t.store ?? U.current }), G = {
50
+ ...P.secret === void 0 ? {} : { secret: P.secret },
51
+ ...P.secrets === void 0 ? {} : { secrets: P.secrets },
52
+ defaultTenantId: P.defaultTenantId,
53
+ ...P.cookieDomain === void 0 ? {} : { cookieDomain: P.cookieDomain },
54
+ ...P.cookieSecure === void 0 ? {} : { cookieSecure: P.cookieSecure },
55
+ ...P.successRedirect === void 0 ? {} : { successRedirect: P.successRedirect },
56
+ ...P.signUpTenants === void 0 ? {} : { signUpTenants: P.signUpTenants },
57
+ ...P.sendEmail === void 0 ? {} : { sendEmail: P.sendEmail },
58
+ ...P.appBaseUrl === void 0 ? {} : { appBaseUrl: P.appBaseUrl },
59
+ subjectGuards: [...P.subjectGuards ?? [], ...te(P.emailVerification) ? [ne(P.emailVerification)] : []],
60
+ ...P.lockout === void 0 ? {} : { lockout: P.lockout },
61
+ ...P.now === void 0 ? {} : { now: P.now },
62
+ ...W === void 0 ? {} : { admit: W },
63
+ ensureActor: P.ensureActor ?? ((e) => {
64
+ let t = U.current;
65
+ return t === void 0 ? A.void : A.promise(() => t.insertIgnore(Oe, {
66
+ id: e.id,
67
+ kind: "user",
68
+ displayName: null
69
+ }, { conflictColumns: ["id"] }));
70
+ }),
71
+ ...P.emailVerification === void 0 ? {} : { emailVerification: P.emailVerification }
72
+ }, K = {
73
+ ...P.appBaseUrl === void 0 ? {} : { appBaseUrl: P.appBaseUrl },
74
+ ...P.sendEmail === void 0 ? {} : { sendEmail: P.sendEmail },
75
+ ...P.invitations === void 0 ? {} : { invitations: P.invitations },
76
+ ...P.now === void 0 ? {} : { now: P.now },
77
+ ...W === void 0 ? {} : { admit: W }
78
+ };
79
+ P.invitations !== void 0 && M({
80
+ source: "plugin",
81
+ table: "invitations",
82
+ timeColumn: "createdAt",
83
+ ttlMs: N(process.env.VOLTRO_INVITATIONS_TTL_HOURS, 2160),
84
+ envVar: "VOLTRO_INVITATIONS_TTL_HOURS"
85
+ }), P.impersonation !== void 0 && M({
86
+ source: "plugin",
87
+ table: "impersonationGrants",
88
+ timeColumn: "startedAt",
89
+ ttlMs: N(process.env.VOLTRO_IMPERSONATION_GRANTS_TTL_HOURS, 8760),
90
+ envVar: "VOLTRO_IMPERSONATION_GRANTS_TTL_HOURS"
91
+ });
92
+ let q = i(V, P.sessionRevocation ?? {}), J = async (e) => {
93
+ let t = ie(G), i = r(e.headers, t);
94
+ if (!i) return null;
95
+ let o = e.headers[j] ? "header" : "cookie", s = a(i.subject);
96
+ if (s && !await A.runPromise(q.isLive(s))) return null;
97
+ if (i.renew || i.kid !== t.current.kid) {
98
+ let e = Math.max(i.exp - i.iat, 60), r = n(i.subject, t, {
99
+ ttlSeconds: e,
100
+ ...G.cookieDomain ? { domain: G.cookieDomain } : {},
101
+ secure: G.cookieSecure ?? !0
102
+ });
103
+ return s && await A.runPromise(V.touchSession(s, new Date(Date.now() + e * 1e3))), {
104
+ subject: i.subject,
105
+ sessionId: s,
106
+ renewSetCookie: r.setCookie,
107
+ renewValue: r.value,
108
+ via: o,
109
+ expiresAt: Math.floor(Date.now() / 1e3) + e
110
+ };
111
+ }
112
+ return {
113
+ subject: i.subject,
114
+ sessionId: s,
115
+ via: o,
116
+ expiresAt: i.exp
117
+ };
118
+ }, Y = (e, t) => {
119
+ if (!t) return e;
120
+ let n = e.headers ?? {};
121
+ return n["set-cookie"] ? e : {
122
+ ...e,
123
+ headers: {
124
+ ...n,
125
+ "set-cookie": t
126
+ }
127
+ };
128
+ }, je = (e) => {
129
+ let t = e.headers[c], n = we(e.headers.cookie, s);
130
+ return ee(t, n, ae(P));
131
+ }, X = (e, t) => e.path === `${F}${t}`, Z = (e) => A.runPromise(e).then(I), Q = {
132
+ issue: (e, t) => u(e, V, G, {
133
+ sessionId: t.sessionId,
134
+ ttlSeconds: t.ttlSeconds,
135
+ metadata: t.metadata,
136
+ ipAddress: t.ipAddress ?? null,
137
+ userAgent: t.userAgent ?? null,
138
+ clearLoginFailures: !1
139
+ }),
140
+ reissue: (e, t) => u(e, V, G, {
141
+ sessionId: t.sessionId,
142
+ ttlSeconds: t.ttlSeconds,
143
+ sessionRow: "existing",
144
+ clearLoginFailures: !1
145
+ }),
146
+ clear: () => t({
147
+ ...G.cookieDomain ? { domain: G.cookieDomain } : {},
148
+ secure: G.cookieSecure ?? !0
149
+ })
150
+ }, Me = {
151
+ method: "*",
152
+ path: F,
153
+ handle: async (t) => {
154
+ let n = t.method === "GET" ? {} : De(t);
155
+ if (P.native !== void 0) {
156
+ let e = {
157
+ prefix: F,
158
+ config: G,
159
+ native: P.native,
160
+ ...P.now === void 0 ? {} : { now: P.now }
161
+ };
162
+ if (t.method === "GET" && X(t, "/native/authorize")) {
163
+ let n = await J(t);
164
+ return Z(E(new URLSearchParams(t.query), n, V, e));
165
+ }
166
+ if (t.method === "GET" && X(t, "/native/complete")) {
167
+ let n = await J(t);
168
+ return Z(D(t.headers.cookie, n, V, e));
169
+ }
170
+ if (t.method === "POST" && X(t, "/native/exchange")) return Z(O({
171
+ code: R(n.code) ?? "",
172
+ codeVerifier: R(n.codeVerifier) ?? ""
173
+ }, V, e));
174
+ } else if (t.path.startsWith(`${F}/native/`)) return L(501, { error: "native_login_not_configured" });
175
+ if (t.method === "GET" && X(t, "/csrf")) return I(oe(G));
176
+ if (t.method === "POST" && X(t, "/sign-in")) return Z(be({
177
+ email: R(n.email) ?? "",
178
+ password: R(n.password) ?? "",
179
+ ...n.redirectAfter === void 0 ? {} : { redirectAfter: !!n.redirectAfter },
180
+ ...t.remoteAddr ? { ipAddress: t.remoteAddr } : {},
181
+ ...t.headers["user-agent"] ? { userAgent: t.headers["user-agent"] } : {}
182
+ }, V, G));
183
+ if (t.method === "POST" && X(t, "/sign-up")) {
184
+ let e = R(n.tenantId);
185
+ return e !== void 0 && e !== G.defaultTenantId && !(G.signUpTenants ?? []).includes(e) ? {
186
+ status: 400,
187
+ headers: { "content-type": "application/json" },
188
+ body: JSON.stringify({ error: "tenant_not_open" })
189
+ } : Z(Se({
190
+ email: R(n.email) ?? "",
191
+ password: R(n.password) ?? "",
192
+ ...z("tenantId", e),
193
+ ...n.redirectAfter === void 0 ? {} : { redirectAfter: !!n.redirectAfter }
194
+ }, V, G));
195
+ }
196
+ if (t.method === "POST" && X(t, "/sign-out")) {
197
+ let e = await J(t);
198
+ return e && e.subject.type === "user" && e.sessionId && (await A.runPromise(V.revokeSession(e.subject.id, e.sessionId)), q.invalidate(e.sessionId)), I(xe(G));
199
+ }
200
+ if (t.method === "POST" && X(t, "/magic-link")) return Z(ue({
201
+ email: R(n.email) ?? "",
202
+ ...z("callbackPath", n.callbackPath)
203
+ }, V, G));
204
+ if (t.method === "POST" && X(t, "/magic-link/callback")) return Z(le({
205
+ token: R(n.token) ?? "",
206
+ ...n.redirectAfter === void 0 ? {} : { redirectAfter: !!n.redirectAfter }
207
+ }, V, G));
208
+ if (t.method === "POST" && X(t, "/password-reset")) return Z(_e({
209
+ email: R(n.email) ?? "",
210
+ ...z("callbackPath", n.callbackPath)
211
+ }, V, G));
212
+ if (t.method === "POST" && X(t, "/password-reset/confirm")) return Z(ge({
213
+ token: R(n.token) ?? "",
214
+ newPassword: R(n.newPassword) ?? ""
215
+ }, V));
216
+ if (t.method === "POST" && X(t, "/verify-email")) {
217
+ let e = R(n.email);
218
+ if (e) return Z(l({ email: e }, V, G));
219
+ let r = await J(t);
220
+ return !r || r.subject.type !== "user" ? L(400, { error: "email required" }) : Z(l({ userId: r.subject.id }, V, G));
221
+ }
222
+ if (t.method === "POST" && X(t, "/verify-email/callback")) return Z(re({ token: R(n.token) ?? "" }, V));
223
+ if (P.invitations && t.method === "POST" && X(t, "/invitations/preview")) return Z(b({ token: R(n.token) ?? "" }, V, K));
224
+ if (P.invitations && t.method === "POST" && X(t, "/invitations/sign-up")) return Z(_({
225
+ token: R(n.token) ?? "",
226
+ password: R(n.password) ?? ""
227
+ }, V, {
228
+ ...K,
229
+ hashPassword: e
230
+ }));
231
+ if (t.method === "POST" && X(t, "/mfa/verify")) return Z(he({
232
+ pendingToken: R(n.pendingToken) ?? "",
233
+ ...z("code", n.code),
234
+ ...z("recoveryCode", n.recoveryCode),
235
+ ...n.redirectAfter === void 0 ? {} : { redirectAfter: !!n.redirectAfter },
236
+ ...t.remoteAddr ? { ipAddress: t.remoteAddr } : {},
237
+ ...t.headers["user-agent"] ? { userAgent: t.headers["user-agent"] } : {}
238
+ }, V, G));
239
+ if (P.passkey && t.method === "POST" && X(t, "/passkey/assert/options")) return Z(d({ ...z("userId", n.userId) }, V, H, P.passkey));
240
+ if (P.passkey && t.method === "POST" && X(t, "/passkey/assert/verify")) return Z(f({
241
+ credentialId: R(n.credentialId) ?? "",
242
+ clientDataJSON: R(n.clientDataJSON) ?? "",
243
+ authenticatorData: R(n.authenticatorData) ?? "",
244
+ signature: R(n.signature) ?? "",
245
+ ...z("userId", n.userId)
246
+ }, V, H, P.passkey, G));
247
+ let r = await J(t), i = r?.subject ?? null, a = () => !i || i.type !== "user" ? L(401, { error: "unauthenticated" }) : { userId: i.id }, o = () => je(t) ? null : L(403, { error: "csrf" }), s = (e) => Z(e).then((e) => Y(e, r?.renewSetCookie));
248
+ if (i && T(i)) {
249
+ let e = t.path.startsWith(F) ? t.path.slice(F.length) : "";
250
+ if (S.includes(e)) return L(403, {
251
+ error: "forbidden_while_impersonating",
252
+ message: "This action changes account credentials or session scope and is refused while impersonating. Stop the impersonation first."
253
+ });
254
+ }
255
+ if (t.method === "GET" && X(t, "/session")) {
256
+ let e = a();
257
+ return "status" in e ? e : Y(L(200, {
258
+ ok: !0,
259
+ subject: i,
260
+ renewed: r?.renewSetCookie !== void 0,
261
+ expiresAt: r?.expiresAt,
262
+ ...r?.via === "header" && r.renewValue !== void 0 ? { session: r.renewValue } : {}
263
+ }), r?.renewSetCookie);
264
+ }
265
+ if (t.method === "GET" && X(t, "/sessions")) {
266
+ let e = a();
267
+ return "status" in e ? e : s(ce({ userId: e.userId }, V));
268
+ }
269
+ if (t.method === "GET" && X(t, "/memberships")) {
270
+ let e = a();
271
+ return "status" in e ? e : s(se({ userId: e.userId }, V));
272
+ }
273
+ if (t.method === "POST" && X(t, "/sessions/revoke")) {
274
+ let e = a();
275
+ if ("status" in e) return e;
276
+ let t = o();
277
+ if (t) return t;
278
+ let r = R(n.sessionId) ?? "";
279
+ return s(ye({
280
+ userId: e.userId,
281
+ sessionId: r
282
+ }, V)).then((e) => (r && q.invalidate(r), e));
283
+ }
284
+ if (t.method === "POST" && X(t, "/sessions/revoke-others")) {
285
+ let e = a();
286
+ return "status" in e ? e : o() || s(ve({
287
+ userId: e.userId,
288
+ keepSessionId: r?.sessionId ?? "__none__"
289
+ }, V));
290
+ }
291
+ if (P.invitations && t.method === "POST" && X(t, "/invitations")) {
292
+ let e = a();
293
+ return "status" in e ? e : o() || s(v({
294
+ inviterUserId: e.userId,
295
+ tenantId: R(n.tenantId) ?? i?.tenantId ?? "",
296
+ email: R(n.email) ?? "",
297
+ role: R(n.role) ?? ""
298
+ }, V, K));
299
+ }
300
+ if (P.invitations && t.method === "GET" && t.path === `${F}/invitations`) {
301
+ let e = a();
302
+ return "status" in e ? e : s(y({
303
+ callerUserId: e.userId,
304
+ tenantId: i?.tenantId ?? ""
305
+ }, V, K));
306
+ }
307
+ if (P.invitations && t.method === "POST" && X(t, "/invitations/revoke")) {
308
+ let e = a();
309
+ return "status" in e ? e : o() || s(x({
310
+ callerUserId: e.userId,
311
+ tenantId: R(n.tenantId) ?? i?.tenantId ?? "",
312
+ invitationId: R(n.invitationId) ?? ""
313
+ }, V, K));
314
+ }
315
+ if (P.invitations && t.method === "POST" && X(t, "/invitations/accept")) {
316
+ let e = a();
317
+ if (!("status" in e)) {
318
+ let e = o();
319
+ if (e) return e;
320
+ }
321
+ return s(g({
322
+ token: R(n.token) ?? "",
323
+ ..."status" in e ? {} : { userId: e.userId }
324
+ }, V, K));
325
+ }
326
+ if (P.impersonation && t.method === "POST" && X(t, "/impersonate/start")) {
327
+ let e = a();
328
+ return "status" in e ? e : o() || Z(C({
329
+ actorSubject: i,
330
+ actorSessionId: r?.sessionId ?? "",
331
+ ...z("targetUserId", n.userId),
332
+ ...z("targetEmail", n.email),
333
+ ...z("reason", n.reason),
334
+ ...typeof n.durationSeconds == "number" ? { durationSeconds: n.durationSeconds } : {},
335
+ ipAddress: t.remoteAddr ?? null,
336
+ userAgent: t.headers["user-agent"] ?? null
337
+ }, V, {
338
+ impersonation: P.impersonation,
339
+ issuer: Q,
340
+ ...P.now === void 0 ? {} : { now: P.now },
341
+ invalidateSession: (e) => q.invalidate(e)
342
+ }));
343
+ }
344
+ if (P.impersonation && t.method === "POST" && X(t, "/impersonate/stop")) {
345
+ let e = a();
346
+ return "status" in e ? e : o() || Z(w({
347
+ subject: i,
348
+ sessionId: r?.sessionId ?? ""
349
+ }, V, {
350
+ impersonation: P.impersonation,
351
+ issuer: Q,
352
+ ...P.now === void 0 ? {} : { now: P.now },
353
+ invalidateSession: (e) => q.invalidate(e)
354
+ }));
355
+ }
356
+ if (t.method === "POST" && X(t, "/switch-tenant")) {
357
+ let e = a();
358
+ return "status" in e ? e : o() || Z(Ce({
359
+ userId: e.userId,
360
+ targetTenantId: R(n.targetTenantId) ?? "",
361
+ ...typeof n.clientId == "number" ? { clientId: n.clientId } : {},
362
+ ...r?.sessionId ? { sessionId: r.sessionId } : {},
363
+ ...i?.type === "user" && i.metadata ? { metadata: i.metadata } : {}
364
+ }, V, G, P.rebind, U.current));
365
+ }
366
+ if (P.mfa && t.method === "POST" && X(t, "/mfa/enroll/start")) {
367
+ let e = a();
368
+ if ("status" in e) return e;
369
+ let t = o();
370
+ if (t) return t;
371
+ let n = await A.runPromise(V.findById(e.userId));
372
+ return s(de({
373
+ userId: e.userId,
374
+ issuer: P.mfa.issuer,
375
+ accountName: n?.email ?? e.userId
376
+ }, V));
377
+ }
378
+ if (P.mfa && t.method === "POST" && X(t, "/mfa/enroll/verify")) {
379
+ let e = a();
380
+ return "status" in e ? e : o() || s(fe({
381
+ userId: e.userId,
382
+ code: R(n.code) ?? ""
383
+ }, V));
384
+ }
385
+ if (P.mfa && t.method === "POST" && X(t, "/mfa/unenroll")) {
386
+ let e = a();
387
+ return "status" in e ? e : o() || s(me({ userId: e.userId }, V));
388
+ }
389
+ if (P.mfa && t.method === "POST" && X(t, "/mfa/recovery-codes/regenerate")) {
390
+ let e = a();
391
+ return "status" in e ? e : o() || s(pe({ userId: e.userId }, V));
392
+ }
393
+ if (P.passkey && t.method === "POST" && X(t, "/passkey/register/options")) {
394
+ let e = a();
395
+ return "status" in e ? e : o() || s(p({
396
+ userId: e.userId,
397
+ userName: R(n.userName) ?? e.userId
398
+ }, V, H, P.passkey));
399
+ }
400
+ if (P.passkey && t.method === "POST" && X(t, "/passkey/register/verify")) {
401
+ let e = a();
402
+ return "status" in e ? e : o() || s(m({
403
+ userId: e.userId,
404
+ credentialId: R(n.credentialId) ?? "",
405
+ clientDataJSON: R(n.clientDataJSON) ?? "",
406
+ authenticatorData: R(n.authenticatorData) ?? "",
407
+ ...z("transports", n.transports)
408
+ }, V, H, P.passkey));
409
+ }
410
+ return !P.passkey && t.path.startsWith(`${F}/passkey/`) ? L(501, { error: "passkey_not_configured" }) : L(404, { error: "not_found" });
411
+ }
412
+ }, Ne = Te({
413
+ name: P.name ? `@voltro/plugin-auth#${P.name}` : "@voltro/plugin-auth",
414
+ 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.",
415
+ permissions: [],
416
+ declaredEnv: Ae,
417
+ privacy: ke,
418
+ httpRoutes: [Me],
419
+ bindDataStore: (e, t) => {
420
+ U.current = e, typeof P.store == "function" && (B.current = P.store(t?.sql === void 0 ? {} : { sql: t.sql }));
421
+ },
422
+ onActivate: (e) => A.sync(() => {
423
+ e.logger.info("auth routes active", {
424
+ prefix: F,
425
+ passkey: P.passkey ? P.passkey.rpId : "disabled",
426
+ email: P.sendEmail ? "wired" : "unwired",
427
+ revocationWindowMs: q.ttlMs,
428
+ emailVerification: P.emailVerification?.policy ?? "off",
429
+ invitations: P.invitations ? "enabled" : "disabled",
430
+ impersonation: P.impersonation ? `enabled(max ${P.impersonation.maxDurationSeconds ?? 900}s)` : "disabled"
431
+ });
432
+ }),
433
+ onDeactivate: () => A.void
434
+ }), $ = P.secrets ?? P.secret;
435
+ return {
436
+ ...Ne,
437
+ auth: { sessionStrategy: k({
438
+ ...$ === void 0 ? {} : { secret: $ },
439
+ store: V,
440
+ revocation: q
441
+ }) }
442
+ };
443
+ }, ke = { collects: [{
444
+ category: "identifiers",
445
+ linked: !0,
446
+ tracking: !1,
447
+ purposes: ["app-functionality"]
448
+ }, {
449
+ category: "email",
450
+ linked: !0,
451
+ tracking: !1,
452
+ purposes: ["app-functionality"]
453
+ }] }, Ae = [
454
+ {
455
+ name: "VOLTRO_SESSION_SECRET_PREVIOUS",
456
+ required: !1,
457
+ secret: !0,
458
+ 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.",
459
+ example: "the-previous-signing-secret"
460
+ },
461
+ {
462
+ name: "VOLTRO_SESSION_KID",
463
+ required: !1,
464
+ secret: !1,
465
+ description: "Non-secret key-id label stamped into cookies signed with the current secret (default 'k0').",
466
+ example: "2026-07"
467
+ },
468
+ {
469
+ name: "VOLTRO_SESSION_KID_PREVIOUS",
470
+ required: !1,
471
+ secret: !1,
472
+ description: "Non-secret key-id label for the previous secret (default 'k-previous').",
473
+ example: "2026-01"
474
+ },
475
+ {
476
+ name: "VOLTRO_INVITATIONS_TTL_HOURS",
477
+ required: !1,
478
+ secret: !1,
479
+ 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.",
480
+ example: "2160"
481
+ },
482
+ {
483
+ name: "VOLTRO_IMPERSONATION_GRANTS_TTL_HOURS",
484
+ required: !1,
485
+ secret: !1,
486
+ 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.",
487
+ example: "8760"
488
+ },
489
+ {
490
+ name: "VOLTRO_AUTH_SCOPE_CACHE_TTL_MS",
491
+ required: !1,
492
+ secret: !1,
493
+ 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.",
494
+ example: "30000"
495
+ }
496
+ ];
497
+ //#endregion
498
+ export { B as authRoutesPlugin, P as getAuthSessionStrategy };