@voltro/plugin-auth 0.85.0 → 0.87.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. package/CHANGELOG.md +5987 -0
  2. package/README.md +93 -0
  3. package/THIRD-PARTY-NOTICES.md +1122 -31
  4. package/dist/admission.js +9 -0
  5. package/dist/appUserStore.js +19 -0
  6. package/dist/authExtensions.js +9 -0
  7. package/dist/emailVerification.js +64 -0
  8. package/dist/guest.d.ts +99 -0
  9. package/dist/guest.js +3 -0
  10. package/dist/guestClient.js +237 -0
  11. package/dist/guestCredential.js +12 -0
  12. package/dist/guestPasswordUpgrade.js +85 -0
  13. package/dist/guestPlugin.js +166 -0
  14. package/dist/guestSchema.js +20 -0
  15. package/dist/guestSecrets.js +10 -0
  16. package/dist/guestService.js +224 -0
  17. package/dist/handlers.js +337 -0
  18. package/dist/impersonation.js +176 -0
  19. package/dist/index.d.ts +282 -20
  20. package/dist/index.js +30 -88
  21. package/dist/invitations.js +187 -0
  22. package/dist/mail.js +10 -0
  23. package/dist/memberships.js +8 -0
  24. package/dist/native/guestStorage.js +41 -0
  25. package/dist/native.d.ts +110 -0
  26. package/dist/native.js +45 -43
  27. package/dist/nativeRoutes.js +109 -0
  28. package/dist/overlayPlugin.js +167 -0
  29. package/dist/passkeyHandlers.js +162 -0
  30. package/dist/plugin.d.ts +123 -10
  31. package/dist/plugin.js +519 -5
  32. package/dist/postgresUserStore.js +475 -0
  33. package/dist/revocation.js +50 -0
  34. package/dist/schema.d.ts +45 -6
  35. package/dist/schema.js +99 -96
  36. package/dist/sessionFamily.js +15 -0
  37. package/dist/sessionRegistry.js +133 -0
  38. package/dist/signedClaims.js +24 -0
  39. package/dist/strategy.d.ts +17 -9
  40. package/dist/strategy.js +69 -3
  41. package/dist/subjectGuard.js +11 -0
  42. package/dist/totp.js +55 -0
  43. package/dist/userStore.js +292 -0
  44. package/dist/web/guestStorage.js +47 -0
  45. package/dist/web/passkeyClient.js +78 -0
  46. package/dist/web/requireAuth.js +9 -0
  47. package/dist/web/subjectContext.js +13 -0
  48. package/dist/web.d.ts +94 -0
  49. package/dist/web.js +6 -90
  50. package/package.json +8 -3
  51. package/dist/plugin-CbmcyEJs.js +0 -2018
  52. package/dist/strategy-XaGjSicX.js +0 -353
package/dist/index.d.ts CHANGED
@@ -1,13 +1,17 @@
1
1
  import { AuthStrategy } from '@voltro/protocol';
2
+ import { ColumnBuilder } from '@voltro/database';
2
3
  import { ConnectionCredential } from '@voltro/protocol';
3
4
  import { DataStore } from '@voltro/database';
5
+ import { Duration } from 'effect';
4
6
  import { Effect } from 'effect';
7
+ import { FieldDefinitions } from '@voltro/database';
5
8
  import { KeyedSecret } from '@voltro/protocol/session';
6
9
  import { MembershipsSource } from '@voltro/protocol';
7
10
  import { SessionSecrets } from '@voltro/protocol/session';
8
11
  import { SqlClient } from 'effect/sql/SqlClient';
9
12
  import { Subject } from '@voltro/protocol';
10
13
  import { SubjectIdentity } from '@voltro/protocol';
14
+ import { Table } from '@voltro/database';
11
15
  import { VerifyOptions } from '@voltro/protocol/session';
12
16
  import { VerifyResult } from '@voltro/protocol/session';
13
17
  import { VoidIfEmpty } from 'effect/Types';
@@ -42,6 +46,9 @@ export declare interface AdmissionRefusal {
42
46
  /** Decide whether a person may join a tenant — `null` to let them in. */
43
47
  export declare type AdmitJoining = (joining: Joining, context: AdmissionContext) => Effect.Effect<AdmissionRefusal | null> | Promise<AdmissionRefusal | null>;
44
48
 
49
+ /** The page a ticket may open: same-origin, below an allowed path, no traversal. */
50
+ export declare const allowedOverlayPage: (next: string, paths: ReadonlyArray<string>) => boolean;
51
+
45
52
  /**
46
53
  * Keep users in the app's own database.
47
54
  *
@@ -83,6 +90,16 @@ export declare interface AssertionVerifyInput {
83
90
  readonly storedCounter: number;
84
91
  }
85
92
 
93
+ /** General authentication extensions; install `auth.guest(...)` or `auth.overlay(...)` in plugins. */
94
+ export declare const auth: {
95
+ readonly guest: (options: GuestPluginOptions) => GuestAuthPlugin;
96
+ readonly overlay: (options: OverlayPluginOptions) => VoltroPlugin;
97
+ };
98
+
99
+ /** Non-session handoffs have separate signature domains. A signed OAuth
100
+ * cookie must never become a native login code or a registered session. */
101
+ export declare type AuthClaimsPurpose = 'native-pending' | 'native-code' | 'social-login' | 'social-upgrade' | 'social-native-upgrade' | 'account-merge';
102
+
86
103
  export declare interface AuthConfig {
87
104
  /**
88
105
  * The session-signing key. OMIT IT and the value is read from
@@ -175,6 +192,27 @@ export declare interface AuthConfig {
175
192
  readonly emailVerification?: EmailVerificationConfig;
176
193
  }
177
194
 
195
+ /** Device credentials never appear in SQL, logs, exports or session claims. */
196
+ export declare const authGuestDevicesTable: Table<"authGuestDevices", FieldDefinitions<{
197
+ readonly id: ColumnBuilder<string, "id", boolean>;
198
+ readonly guestId: ColumnBuilder<string, "text", boolean>;
199
+ readonly tenantId: ColumnBuilder<string, "text", boolean>;
200
+ readonly secretHash: ColumnBuilder<string, "text", boolean>;
201
+ readonly generation: ColumnBuilder<number, "integer", boolean>;
202
+ readonly expiresAt: ColumnBuilder<Date, "timestamp", boolean>;
203
+ readonly createdAt: ColumnBuilder<Date, "timestamp", boolean>;
204
+ }>, true, never>;
205
+
206
+ /** Guest identity is independent of the password user's email record. */
207
+ export declare const authGuestsTable: Table<"authGuests", FieldDefinitions<{
208
+ readonly id: ColumnBuilder<string, "id", boolean>;
209
+ readonly tenantId: ColumnBuilder<string, "text", boolean>;
210
+ readonly upgradeable: ColumnBuilder<boolean, "boolean", boolean>;
211
+ readonly expiresAt: ColumnBuilder<Date, "timestamp", boolean>;
212
+ readonly createdAt: ColumnBuilder<Date, "timestamp", boolean>;
213
+ readonly lastSeenAt: ColumnBuilder<Date, "timestamp", boolean>;
214
+ }>, true, never>;
215
+
178
216
  export declare const authMemberships: (store: UserStore) => MembershipsSource;
179
217
 
180
218
  /**
@@ -183,7 +221,7 @@ export declare const authMemberships: (store: UserStore) => MembershipsSource;
183
221
  * routes use) so the framework's serve pipeline can slot it into the
184
222
  * per-app auth chain — the rpc/WS path then rejects revoked sessions
185
223
  * exactly like the `/auth/*` routes. Read it via
186
- * `getAuthSessionStrategy`.
224
+ * `getAuthSessionStrategies`.
187
225
  */
188
226
  export declare interface AuthPlugin extends VoltroPlugin {
189
227
  readonly auth: {
@@ -233,6 +271,15 @@ export declare interface AuthRoutesPluginOptions extends AuthConfig {
233
271
  * otherwise get 500s from a route it never asked for.
234
272
  */
235
273
  readonly invitations?: InvitationsConfig;
274
+ /**
275
+ * Guest → account with email and password. PRESENT mounts `POST /auth/upgrade-guest`
276
+ * (`{ credential, email, password }`, CSRF): the guest becomes a registered account under its
277
+ * own subject ID, so what it owns stays its own. Pass the installed guest lifecycle port,
278
+ * e.g. `{ service: () => guestPlugin.guest.service() }`. ABSENT mounts nothing.
279
+ */
280
+ readonly guestUpgrade?: {
281
+ readonly service: () => GuestAuthService;
282
+ };
236
283
  /**
237
284
  * User impersonation ("log in as"). PRESENT mounts `/auth/impersonate/start`
238
285
  * + `/stop`, arms the retention bound on `impersonationGrants`, and closes
@@ -267,6 +314,10 @@ export declare interface ChallengeStore {
267
314
  */
268
315
  export declare const clearSessionCookie: (options?: IssueSessionOptions) => string;
269
316
 
317
+ /** Upgrade the presented guest to an email/password account under the same subject ID. An email
318
+ * that already has an account refuses (`email_taken`); this never merges accounts. */
319
+ export declare const completeGuestPasswordUpgrade: (options: GuestPasswordUpgradeOptions) => Effect.Effect<GuestPasswordUpgradeResult>;
320
+
270
321
  /** The keyed secret set a handler signs/verifies with — the explicit
271
322
  * `config.secrets` when set, else `config.secret` widened with the
272
323
  * rotation env vars. */
@@ -304,13 +355,17 @@ export declare const CSRF_HEADER_NAME: "x-csrf-token";
304
355
  */
305
356
  export declare const dataStoreChallengeStore: (sql: SqlClient) => ChallengeStore;
306
357
 
358
+ /** Uses the app's existing DataStore and session issuer. Device proof grants
359
+ * guest identity only; provider linking is a separate trusted-account operation. */
360
+ export declare const dataStoreGuestAuth: (store: DataStore, options: GuestAuthOptions) => GuestAuthService;
361
+
307
362
  /**
308
363
  * Session rows in the app's own database — the `sessions` table from
309
364
  * `@voltro/plugin-auth/schema`, which the app registers like any other. Reads
310
365
  * and writes go through the store the framework opened, so there is no second
311
366
  * pool and no second schema.
312
367
  */
313
- export declare const dataStoreSessionRegistry: (store: DataStore) => SessionRegistry;
368
+ export declare const dataStoreSessionRegistry: (store: DataStore, options?: SessionRegistryStoreOptions) => SessionRegistry;
314
369
 
315
370
  /** Default set of roles allowed to invite at all. */
316
371
  export declare const DEFAULT_INVITER_ROLES: ReadonlyArray<string>;
@@ -450,11 +505,12 @@ export declare const generateTotpCode: (secretBase32: string, nowMs?: number) =>
450
505
  export declare const generateTotpSecret: () => string;
451
506
 
452
507
  /**
453
- * Pull the pre-wired session strategy off a plugin list (the framework's
454
- * serve pipeline calls this when composing the auth chain). Returns the
455
- * FIRST auth plugin's strategy, or null when no auth plugin is present.
508
+ * Collect pre-wired session strategies in plugin declaration order. The
509
+ * framework inserts each original strategy into its shared auth chain, so
510
+ * prefix ordering, collision diagnostics and boot verification see them all.
511
+ * Only `skip` advances; rejected credentials never get another attempt.
456
512
  */
457
- export declare const getAuthSessionStrategy: (plugins: ReadonlyArray<VoltroPlugin>) => AuthStrategy | null;
513
+ export declare const getAuthSessionStrategies: (plugins: ReadonlyArray<VoltroPlugin>) => ReadonlyArray<AuthStrategy>;
458
514
 
459
515
  export declare interface GrantRoleContext {
460
516
  /** The inviter's role in THIS tenant, as read from `memberships`. */
@@ -477,6 +533,149 @@ export declare interface GrantRoleContext {
477
533
  */
478
534
  export declare const guardSubject: (user: UserRecord, config: AuthConfig) => Effect.Effect<HandlerResult | null>;
479
535
 
536
+ export declare class GuestAuthError extends GuestAuthError_base<{
537
+ readonly code: 'credential_rejected' | 'store_unavailable' | 'upgrade_conflict';
538
+ }> {
539
+ }
540
+
541
+ declare const GuestAuthError_base: new <A extends Record<string, any> = {}>(args: VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }>) => YieldableError & {
542
+ readonly _tag: "GuestAuthError";
543
+ } & Readonly<A>;
544
+
545
+ export declare interface GuestAuthOptions {
546
+ /** Server-owned tenant selection; never copied from a device or request body. */
547
+ readonly tenantId: string;
548
+ readonly secret: SessionSecretInput | (() => SessionSecretInput);
549
+ readonly upgradeable?: boolean;
550
+ readonly ttlSeconds?: number;
551
+ readonly sessionTtlSeconds?: number;
552
+ readonly secure?: boolean;
553
+ readonly now?: () => number;
554
+ }
555
+
556
+ export declare interface GuestAuthPlugin extends AuthPlugin {
557
+ readonly guest: {
558
+ /** Server-side lifecycle/privacy port, available after normal store binding. */
559
+ readonly service: () => GuestAuthService;
560
+ };
561
+ }
562
+
563
+ export declare const guestAuthPlugin: (options: GuestPluginOptions) => GuestAuthPlugin;
564
+
565
+ /** Durable guest lifecycle port. Implementations must serialize every write
566
+ * against the guest identity and commit session rows before returning credentials. */
567
+ export declare interface GuestAuthService {
568
+ readonly create: () => Effect.Effect<GuestSession, GuestAuthError>;
569
+ readonly resume: (credential: string) => Effect.Effect<GuestSession, GuestAuthError>;
570
+ /** Propose a fresh server-minted proof without replacing the active proof. */
571
+ readonly prepareRotation: (credential: string) => Effect.Effect<{
572
+ readonly deviceCredential: string;
573
+ }, GuestAuthError>;
574
+ /** The caller durably journals both proofs before this atomic replacement. */
575
+ readonly rotate: (credential: string, nextCredential: string) => Effect.Effect<GuestSession, GuestAuthError>;
576
+ readonly revoke: (credential: string) => Effect.Effect<void, GuestAuthError>;
577
+ /** Server-side privacy hook: the caller must authorize this subject ID. */
578
+ readonly erase: (subjectId: string) => Effect.Effect<boolean, GuestAuthError>;
579
+ /** Server-side privacy hook: excludes credential hashes and credential bytes. */
580
+ readonly export: (subjectId: string) => Effect.Effect<GuestExport | null, GuestAuthError>;
581
+ readonly isLive: (subject: SubjectIdentity) => Effect.Effect<boolean, GuestAuthError>;
582
+ /** Trusted server operation. The writer must use this transaction to create
583
+ * the registered `users` row with exactly the guest ID and tenant. The guest
584
+ * fence, writer, device removal and guest-session revocation commit together.
585
+ * No client-supplied kind/state can authorize an upgrade. */
586
+ readonly upgrade: <A>(credential: string, writer: GuestUpgradeWriter<A>) => Effect.Effect<A, GuestAuthError>;
587
+ /** Trusted, separately confirmed merge into an EXISTING registered subject.
588
+ * The coordinator must prove target ownership and run all data hooks through
589
+ * this same transaction. A device proof alone never proves target ownership. */
590
+ readonly mergeInto: <A>(credential: string, targetId: string, writer: GuestMergeWriter<A>) => Effect.Effect<A, GuestAuthError>;
591
+ }
592
+
593
+ export declare interface GuestExport {
594
+ readonly subject: {
595
+ readonly id: string;
596
+ readonly tenantId: string;
597
+ readonly kind: 'guest';
598
+ readonly upgradeable: boolean;
599
+ readonly createdAt: number;
600
+ readonly expiresAt: number;
601
+ };
602
+ readonly devices: ReadonlyArray<{
603
+ readonly id: string;
604
+ readonly generation: number;
605
+ readonly createdAt: number;
606
+ readonly expiresAt: number;
607
+ }>;
608
+ readonly sessions: ReadonlyArray<{
609
+ readonly id: string;
610
+ readonly expiresAt: number;
611
+ }>;
612
+ }
613
+
614
+ export declare type GuestIdentity = Extract<SubjectIdentity, {
615
+ readonly type: 'user';
616
+ }> & {
617
+ readonly kind: 'guest';
618
+ };
619
+
620
+ export declare interface GuestMergeContext extends GuestUpgradeContext {
621
+ readonly targetId: string;
622
+ }
623
+
624
+ export declare type GuestMergeWriter<A> = (context: GuestMergeContext) => Promise<A>;
625
+
626
+ export declare interface GuestPasswordUpgradeOptions {
627
+ /** The guest's device proof, as the guest client presents it. */
628
+ readonly credential: string;
629
+ readonly email: string;
630
+ readonly password: string;
631
+ /** The installed guest lifecycle port (`guestPlugin.guest.service()`). */
632
+ readonly service: GuestAuthService;
633
+ readonly users: UserStore;
634
+ readonly config: AuthConfig;
635
+ readonly ipAddress?: string | null;
636
+ readonly userAgent?: string | null;
637
+ }
638
+
639
+ export declare type GuestPasswordUpgradeResult = {
640
+ readonly kind: 'ok';
641
+ readonly userId: string;
642
+ readonly subject: unknown;
643
+ readonly setCookie: string;
644
+ } | {
645
+ readonly kind: 'refused';
646
+ readonly status: number;
647
+ readonly code: string;
648
+ readonly message: string;
649
+ };
650
+
651
+ export declare interface GuestPluginOptions {
652
+ /** Server-owned tenant, required; client headers and device proof cannot pick it. */
653
+ readonly tenantId: string;
654
+ readonly upgradeable?: boolean;
655
+ readonly ttl?: Duration.Input;
656
+ readonly sessionTtl?: Duration.Input;
657
+ readonly secret?: SessionSecretInput;
658
+ readonly secure?: boolean;
659
+ readonly prefix?: string;
660
+ /** Inject a complete durable lifecycle port; never an ephemeral credential fallback. */
661
+ readonly service?: (store: DataStore) => GuestAuthService;
662
+ }
663
+
664
+ export declare interface GuestSession extends IssuedRegisteredSession {
665
+ readonly subject: GuestIdentity;
666
+ /** Opaque proof returned only on creation or explicit credential rotation. */
667
+ readonly deviceCredential?: string;
668
+ }
669
+
670
+ export declare interface GuestUpgradeContext {
671
+ readonly store: DataStore;
672
+ readonly subjectId: string;
673
+ readonly tenantId: string;
674
+ readonly createdAt: Date;
675
+ }
676
+
677
+ export declare type GuestUpgradeWriter<A> = (context: GuestUpgradeContext) => Promise<A>;
678
+
480
679
  /**
481
680
  * Accept an invitation as an ALREADY-AUTHENTICATED user.
482
681
  *
@@ -1234,11 +1433,6 @@ export declare interface IssuedSession {
1234
1433
  readonly setCookie: string;
1235
1434
  }
1236
1435
 
1237
- /**
1238
- * `issueSession` with a row behind it: writes the session to the registry,
1239
- * stamps its id into the cookie and signs. A cookie issued here can be killed
1240
- * before its `exp`; one issued with plain `issueSession` cannot.
1241
- */
1242
1436
  export declare const issueRegisteredSession: (registry: Pick<SessionRegistry, "insertSession">, subject: Subject | SubjectIdentity, secret: SessionSecretInput, options?: IssueRegisteredSessionOptions) => Promise<IssuedRegisteredSession>;
1243
1437
 
1244
1438
  export declare interface IssueRegisteredSessionOptions extends IssueSessionOptions {
@@ -1375,6 +1569,13 @@ export declare interface LockoutStore {
1375
1569
  readonly clearLoginFailures: (email: string) => Effect.Effect<void>;
1376
1570
  }
1377
1571
 
1572
+ /** Read an owned, unrotated credential under its canonical family fence.
1573
+ * Call only inside the caller's existing transaction, after any application
1574
+ * account/tenant fences. Locks remain held until that transaction ends. This
1575
+ * checks persistence identity, not the signed credential, expiry or permissions.
1576
+ * Locking-read conflicts follow the selected store's transaction semantics. */
1577
+ export declare const lockRegisteredSession: (store: DataStore, userId: string, sessionId: string) => Promise<SessionRecord | null>;
1578
+
1378
1579
  /** Default magic-link lifetime — short, since it's an inbox round-trip. */
1379
1580
  export declare const MAGIC_LINK_TTL_S: number;
1380
1581
 
@@ -1616,6 +1817,32 @@ export declare const otpauthUrl: (params: {
1616
1817
  readonly secret: string;
1617
1818
  }) => string;
1618
1819
 
1820
+ /**
1821
+ * Mount the web overlay handoff: an engine client trades its header session
1822
+ * for a one-time URL that signs an embedded browser in to `paths` of the web
1823
+ * app with the same session row. Install as `auth.overlay({ paths: ['/overlay'] })`.
1824
+ */
1825
+ export declare const overlayAuthPlugin: (options: OverlayPluginOptions) => VoltroPlugin;
1826
+
1827
+ /** Options of `auth.overlay(...)`, the web overlay handoff for engine clients. */
1828
+ export declare interface OverlayPluginOptions {
1829
+ /** Pages a ticket may open, e.g. `['/overlay']`. A page is allowed when its
1830
+ * path equals one of these or lies below it. Required: the handoff signs
1831
+ * the embedded browser in, so it lands only where the app expects it. */
1832
+ readonly paths: ReadonlyArray<string>;
1833
+ /** Lifetime of a ticket. Default 60 seconds, at most 5 minutes. */
1834
+ readonly ttl?: Duration.Input;
1835
+ /** Signing keys of the sessions this handoff accepts. Default: the
1836
+ * project's `VOLTRO_SESSION_SECRET` (and `_PREVIOUS`). */
1837
+ readonly secret?: SessionSecretInput;
1838
+ /** `Secure` on the cookie. Default `true`; `false` only for plain-http development. */
1839
+ readonly secure?: boolean;
1840
+ /** Cookie `Domain`, when the web app and the API share a parent domain. */
1841
+ readonly domain?: string;
1842
+ /** Route prefix. Default `/auth/overlay`. */
1843
+ readonly prefix?: string;
1844
+ }
1845
+
1619
1846
  declare interface ParsedHash {
1620
1847
  readonly N: number;
1621
1848
  readonly r: number;
@@ -1623,6 +1850,13 @@ declare interface ParsedHash {
1623
1850
  readonly keyLen: number;
1624
1851
  }
1625
1852
 
1853
+ /** Browser-safe shape only. Possession is verified against SQL on the server. */
1854
+ export declare const parseGuestCredential: (credential: string) => {
1855
+ readonly guestId: string;
1856
+ readonly deviceId: string;
1857
+ readonly secret: string;
1858
+ } | null;
1859
+
1626
1860
  /** Parse the scrypt parameters out of a stored hash. Returns null when the
1627
1861
  * string isn't a well-formed `scrypt$N$r$p$salt$derived`, or when its
1628
1862
  * parameters exceed the derivation ceiling. */
@@ -1799,6 +2033,9 @@ export declare interface RecoveryCodeRecord {
1799
2033
  readonly createdAt: Date;
1800
2034
  }
1801
2035
 
2036
+ export declare interface RegisteredSessionRotationOptions extends IssueRegisteredSessionOptions, SessionRegistryStoreOptions {
2037
+ }
2038
+
1802
2039
  export declare type RegistrationOutcome = {
1803
2040
  readonly ok: true;
1804
2041
  readonly result: RegistrationResult;
@@ -1846,6 +2083,11 @@ declare interface RevokeSessionInput {
1846
2083
  readonly sessionId: string;
1847
2084
  }
1848
2085
 
2086
+ /** Rotate one live credential atomically. Retained inactive aliases let
2087
+ * sign-out using an earlier signed cookie revoke this device's successor.
2088
+ * The caller must separately validate current account and tenant authority. */
2089
+ export declare const rotateRegisteredSession: (store: DataStore, subject: Subject | SubjectIdentity, presentedSessionId: string, secret: SessionSecretInput, options?: RegisteredSessionRotationOptions) => Promise<IssuedRegisteredSession | null>;
2090
+
1849
2091
  /**
1850
2092
  * Run the guards in order against `user`, short-circuiting on the FIRST
1851
2093
  * rejection. With no guards (or all allowing) it resolves `{ ok: true }`.
@@ -1891,6 +2133,10 @@ export declare const sessionIdOfSubject: (subject: Subject) => string | null;
1891
2133
  /** A server-side session row — enumerated so apps can list active
1892
2134
  * devices + revoke them. Written on sign-in; deleted on revoke. */
1893
2135
  export declare interface SessionRecord {
2136
+ /** Stable device lineage. The original row is its transaction lock anchor. */
2137
+ readonly familyId?: string | null;
2138
+ /** A replaced credential remains an inert revocation alias until the device ends. */
2139
+ readonly rotatedAt?: Date | null;
1894
2140
  readonly id: string;
1895
2141
  readonly userId: string;
1896
2142
  readonly tenantId: string;
@@ -1905,11 +2151,19 @@ export declare interface SessionRecord {
1905
2151
  * `UserStore`, usable without the rest of it. */
1906
2152
  export declare type SessionRegistry = SessionStore;
1907
2153
 
2154
+ export declare interface SessionRegistryStoreOptions {
2155
+ /** Borrow an already owned transaction; otherwise registry writes open one.
2156
+ * Account-wide revocation needs the application's real owner fence. With an
2157
+ * older repeatable-read snapshot, root issuance must also change that owner's
2158
+ * persisted revision so a conflicting old transaction retries or aborts. */
2159
+ readonly transaction?: 'existing';
2160
+ }
2161
+
1908
2162
  export declare interface SessionRevocationChecker {
1909
- /** Is this session still live? `false` means the row was revoked (or
1910
- * is past its `expiresAt`) — the caller rejects the cookie. Verdicts
1911
- * are cached for `ttlMs`. */
1912
- readonly isLive: (sessionId: string) => Effect.Effect<boolean>;
2163
+ /** Is this exact session credential live for the signed subject's user ID?
2164
+ * Refuse a missing/rotated/expired row or a different stored owner. Cache
2165
+ * verdicts retain native owner identity, never the requested owner. */
2166
+ readonly isLiveFor: (sessionId: string, userId: string) => Effect.Effect<boolean>;
1913
2167
  /** Drop the cached verdict for one session — called inline by
1914
2168
  * sign-out / revoke handlers so the kill is immediate on THIS
1915
2169
  * process (other replicas converge within `ttlMs`). */
@@ -1955,13 +2209,14 @@ export declare const sessionSecretsOf: (secret: SessionSecretInput) => SessionSe
1955
2209
 
1956
2210
  /** Server-side sessions — enumeration + revocation. */
1957
2211
  export declare interface SessionStore {
1958
- /** Active sessions for a user (most recent first). */
2212
+ /** Unrotated credentials with an existing canonical root owned by the same user, most recent first. */
1959
2213
  readonly listSessions: (userId: string) => Effect.Effect<ReadonlyArray<SessionRecord>>;
1960
2214
  /** Record a session on sign-in. */
1961
2215
  readonly insertSession: (session: Omit<SessionRecord, 'createdAt' | 'lastSeenAt'>) => Effect.Effect<SessionRecord>;
1962
- /** Look up one session row by id. The request-time revocation check
1963
- * (`makeSessionRevocationChecker`) calls this — a missing row means
1964
- * the session was revoked. */
2216
+ /** Look up an unrotated credential only while its canonical family root
2217
+ * exists and belongs to the same user. A rotated root remains an anchor;
2218
+ * its tenant does not replace the credential tenant. Custom stores must
2219
+ * enforce this before returning a row to request-time revocation checks. */
1965
2220
  readonly findSession: (sessionId: string) => Effect.Effect<SessionRecord | null>;
1966
2221
  /** Slide a session row forward: bump `expiresAt` + `lastSeenAt`. Called
1967
2222
  * when the sliding-window renewal re-issues the cookie, so the row
@@ -1980,6 +2235,8 @@ export declare interface SessionStore {
1980
2235
  readonly revokeAllSessions: (userId: string) => Effect.Effect<number>;
1981
2236
  }
1982
2237
 
2238
+ export declare const signAuthClaims: (claims: Readonly<Record<string, unknown>>, secret: string, purpose: AuthClaimsPurpose) => string;
2239
+
1983
2240
  export declare interface SignInInput {
1984
2241
  readonly email: string;
1985
2242
  readonly password: string;
@@ -2159,7 +2416,7 @@ declare const UserNotFoundError_base: new <A extends Record<string, any> = {}>(a
2159
2416
 
2160
2417
  export declare interface UserRecord {
2161
2418
  readonly id: string;
2162
- readonly email: string;
2419
+ readonly email: string | null;
2163
2420
  /**
2164
2421
  * The stored scrypt hash — OPTIONAL, because not every identity has a
2165
2422
  * password. An SSO-only, magic-link-only, passkey-only, or provider-PAT
@@ -2224,6 +2481,8 @@ export declare type UserStoreResolver = (ctx: UserStoreBindContext) => UserStore
2224
2481
  /** Verify a passkey assertion (sign-in). */
2225
2482
  export declare const verifyAssertion: (input: AssertionVerifyInput) => AssertionOutcome;
2226
2483
 
2484
+ export declare const verifyAuthClaims: (value: string | undefined, secret: string, purpose: AuthClaimsPurpose, now?: () => number) => Record<string, unknown> | null;
2485
+
2227
2486
  /**
2228
2487
  * Verify a state-changing request's CSRF protection. Both the header
2229
2488
  * value and the cookie value must (a) be the SAME token and (b) carry a
@@ -2309,6 +2568,9 @@ export declare interface VoltroPasswordStrategyOptions {
2309
2568
  * (stateless); with it, nothing but a registered session authenticates,
2310
2569
  * and every session can be revoked. */
2311
2570
  readonly requireSessionId?: boolean;
2571
+ /** Verified user identity kinds owned by this carrier. Other verified kinds
2572
+ * skip to their own carrier; malformed, forged or expired values still fail. */
2573
+ readonly userKinds?: ReadonlyArray<'registered' | 'guest'>;
2312
2574
  }
2313
2575
 
2314
2576
  export declare type WebAuthnError = 'bad-client-data' | 'wrong-type' | 'challenge-mismatch' | 'origin-mismatch' | 'rpid-mismatch' | 'user-not-present' | 'bad-auth-data' | 'no-credential' | 'unsupported-key' | 'bad-signature' | 'counter-replay';
package/dist/index.js CHANGED
@@ -1,89 +1,31 @@
1
1
  import { PasswordEmptyError as e, PasswordHashError as t, hashPassword as n, needsRehash as r, parseScryptParams as i, verifyPassword as a, verifyPasswordWithRehash as o } from "./password.js";
2
- import { SESSION_COOKIE_NAME as ee, clearSessionCookie as te, issueSession as s, readSession as c, readSessionFromHeaders as ne, readSessionKeyed as re, resolveSessionSecret as ie, resolveSessionSecrets as ae, sessionSecretsOf as oe } from "./session.js";
3
- import { a as l, c as se, i as u, l as d, n as f, o as p, r as m, s as h, t as g } from "./strategy-XaGjSicX.js";
4
- import { $ as _, A as v, B as y, C as b, D as x, E as S, F as C, G as w, H as T, I as E, J as D, K as O, L as k, M as A, N as j, O as M, P as N, Q as P, R as F, S as I, T as L, U as R, V as z, W as B, X as V, Y as H, Z as U, _ as W, _t as G, a as K, at as ce, b as le, c as ue, ct as de, d as fe, dt as pe, et as me, f as he, ft as ge, g as _e, gt as ve, h as ye, ht as be, i as xe, it as Se, j as Ce, k as we, l as Te, lt as Ee, m as De, mt as Oe, n as ke, nt as Ae, o as je, ot as Me, p as Ne, pt as Pe, q as Fe, r as Ie, rt as Le, s as Re, st as ze, t as Be, tt as Ve, u as He, ut as Ue, v as We, vt as Ge, w as Ke, x as qe, y as Je, z as Ye } from "./plugin-CbmcyEJs.js";
5
- import { EMAIL_VERIFY_TTL_S as Xe, MAGIC_LINK_TTL_S as Ze, MFA_PENDING_TTL_S as Qe, NATIVE_CODE_TTL_S as $e, PASSWORD_RESET_TTL_S as et, hashToken as tt, mintSecret as nt, mintToken as rt } from "./tokens.js";
6
- import { CSRF_COOKIE_NAME as it, CSRF_HEADER_NAME as at, isCsrfTokenWellFormed as ot, issueCsrfToken as st, verifyCsrf as q } from "./csrf.js";
7
- import { generateChallenge as ct, verifyAssertion as lt, verifyRegistration as ut } from "./webauthn.js";
8
- import { Effect as J } from "effect";
9
- import { eq as Y } from "@voltro/database";
10
- //#region src/sessionRegistry.ts
11
- var X = "sessions", dt = 604800, Z = (e) => ({
12
- ...e,
13
- expiresAt: new Date(e.expiresAt),
14
- createdAt: new Date(e.createdAt),
15
- lastSeenAt: new Date(e.lastSeenAt)
16
- }), Q = async (e, t) => await e.query({
17
- table: X,
18
- predicate: t,
19
- order: [],
20
- take: void 0,
21
- skip: void 0,
22
- projection: void 0
23
- }), ft = (e) => ({
24
- listSessions: (t) => J.promise(async () => (await Q(e, Y("userId", t))).map(Z).sort((e, t) => t.createdAt.getTime() - e.createdAt.getTime())),
25
- insertSession: (t) => J.promise(async () => {
26
- let n = /* @__PURE__ */ new Date(), r = {
27
- ...t,
28
- createdAt: n,
29
- lastSeenAt: n
30
- };
31
- return await e.insert(X, r), r;
32
- }),
33
- findSession: (t) => J.promise(async () => {
34
- let n = (await Q(e, Y("id", t)))[0];
35
- return n === void 0 ? null : Z(n);
36
- }),
37
- touchSession: (t, n) => J.promise(async () => {
38
- await e.update(X, t, {
39
- expiresAt: n,
40
- lastSeenAt: /* @__PURE__ */ new Date()
41
- });
42
- }),
43
- revokeSession: (t, n) => J.promise(async () => {
44
- let r = (await Q(e, Y("id", n)))[0];
45
- return r === void 0 || r.userId !== t ? !1 : e.delete(X, n);
46
- }),
47
- revokeAllOtherSessions: (t, n) => J.promise(async () => {
48
- let r = 0;
49
- for (let i of await Q(e, Y("userId", t))) i.id !== n && await e.delete(X, i.id) && (r += 1);
50
- return r;
51
- }),
52
- revokeAllSessions: (t) => J.promise(async () => {
53
- let n = 0;
54
- for (let r of await Q(e, Y("userId", t))) await e.delete(X, r.id) && (n += 1);
55
- return n;
56
- })
57
- }), $ = (e, t) => e.type === "user" ? {
58
- ...e,
59
- metadata: {
60
- ...e.metadata ?? {},
61
- sessionId: t
62
- }
63
- } : e, pt = async (e, t, n, r = {}) => {
64
- if (t.type !== "user") throw Error("issueRegisteredSession: only a user subject has a session row");
65
- let i = l("sess"), a = r.ttlSeconds ?? dt, o = (r.now ?? Date.now)();
66
- await J.runPromise(e.insertSession({
67
- id: i,
68
- userId: t.id,
69
- tenantId: t.tenantId ?? "",
70
- expiresAt: new Date(o + a * 1e3),
71
- ipAddress: r.ipAddress ?? null,
72
- userAgent: r.userAgent ?? null
73
- }));
74
- let { ipAddress: ee, userAgent: te, ...c } = r;
75
- return {
76
- ...s($(t, i), n, c),
77
- sessionId: i
78
- };
79
- }, mt = (e) => async (t) => t.type === "user" ? (await J.runPromise(e.listMemberships(t.id))).map((e) => ({
80
- tenantId: e.tenantId,
81
- role: e.role
82
- })) : [], ht = (e) => (t) => J.runPromise(e.send({
83
- to: t.to,
84
- subject: t.subject,
85
- html: t.html,
86
- text: t.text
87
- }).pipe(J.asVoid, J.orElseSucceed(() => void 0)));
88
- //#endregion
89
- export { it as CSRF_COOKIE_NAME, at as CSRF_HEADER_NAME, _e as DEFAULT_INVITER_ROLES, W as DEFAULT_ROLE_RANK, me as EMAIL_NOT_VERIFIED_CODE, Ve as EMAIL_VERIFIED_METADATA_KEY, Ae as EMAIL_VERIFY_RESEND_COOLDOWN_S, Xe as EMAIL_VERIFY_TTL_S, Te as IMPERSONATION_DEFAULT_DURATION_S, He as IMPERSONATION_FORBIDDEN_SUBPATHS, We as INVITATION_MAX_PENDING, Je as INVITATION_TTL_S, Ze as MAGIC_LINK_TTL_S, Qe as MFA_PENDING_TTL_S, $e as NATIVE_CODE_TTL_S, Ie as NATIVE_PENDING_COOKIE, et as PASSWORD_RESET_TTL_S, e as PasswordEmptyError, t as PasswordHashError, ee as SESSION_COOKIE_NAME, Ue as TotpVerifyError, f as UserAlreadyExistsError, m as UserNotFoundError, G as appUserStore, mt as authMemberships, Be as authRoutesPlugin, te as clearSessionCookie, j as configSessionSecrets, x as dataStoreChallengeStore, ft as dataStoreSessionRegistry, le as defaultCanGrantRole, Le as emailVerificationActive, Se as emailVerificationGuard, ct as generateChallenge, pe as generateRecoveryCodes, ge as generateTotpCode, Pe as generateTotpSecret, ke as getAuthSessionStrategy, N as guardSubject, qe as handleAcceptInvitation, I as handleAcceptInvitationWithSignUp, b as handleCreateInvitation, C as handleCsrf, ce as handleEmailVerifyConfirm, Me as handleEmailVerifyRequest, fe as handleImpersonationStart, he as handleImpersonationStop, Ke as handleListInvitations, E as handleListMemberships, k as handleListSessions, F as handleMagicLinkConsume, Ye as handleMagicLinkRequest, y as handleMfaEnrollStart, z as handleMfaEnrollVerify, T as handleMfaRegenerateRecoveryCodes, R as handleMfaUnenroll, B as handleMfaVerify, xe as handleNativeAuthorize, K as handleNativeComplete, je as handleNativeExchange, M as handlePasskeyAssertOptions, we as handlePasskeyAssertVerify, v as handlePasskeyRegisterOptions, Ce as handlePasskeyRegisterVerify, w as handlePasswordResetConfirm, O as handlePasswordResetRequest, L as handlePreviewInvitation, Fe as handleRevokeAllOtherSessions, S as handleRevokeInvitation, D as handleRevokeSession, H as handleSignIn, V as handleSignOut, U as handleSignUp, P as handleSwitchTenant, n as hashPassword, tt as hashToken, Ne as impersonationAuditRedactor, De as impersonationOf, Re as isAllowedRedirectUri, ot as isCsrfTokenWellFormed, ze as isEmailVerified, ye as isImpersonated, st as issueCsrfToken, pt as issueRegisteredSession, s as issueSession, _ as issueUserSession, ht as mailSender, d as makeSessionRevocationChecker, A as memoryChallengeStore, u as memoryUserStore, nt as mintSecret, rt as mintToken, ue as nativeLoginRedirect, r as needsRehash, l as newAuthId, Oe as otpauthUrl, i as parseScryptParams, Ge as postgresUserStore, c as readSession, ne as readSessionFromHeaders, re as readSessionKeyed, ie as resolveSessionSecret, ae as resolveSessionSecrets, ve as runSubjectGuards, de as sendEmailVerification, p as sessionIdOfSubject, oe as sessionSecretsOf, h as subjectFromUser, se as subjectMemberships, Ee as userEmailVerified, lt as verifyAssertion, q as verifyCsrf, a as verifyPassword, o as verifyPasswordWithRehash, ut as verifyRegistration, be as verifyTotpCode, g as voltroPasswordStrategy, $ as withSessionId };
2
+ import { SESSION_COOKIE_NAME as s, clearSessionCookie as c, issueSession as l, readSession as u, readSessionFromHeaders as d, readSessionKeyed as f, resolveSessionSecret as p, resolveSessionSecrets as m, sessionSecretsOf as h } from "./session.js";
3
+ import { makeSessionRevocationChecker as g } from "./revocation.js";
4
+ import { UserAlreadyExistsError as _, UserNotFoundError as v, memoryUserStore as y, newAuthId as b, sessionIdOfSubject as x, subjectFromUser as S, subjectMemberships as C } from "./userStore.js";
5
+ import { dataStoreSessionRegistry as w, issueRegisteredSession as T, lockRegisteredSession as E, rotateRegisteredSession as D, withSessionId as O } from "./sessionRegistry.js";
6
+ import { CSRF_COOKIE_NAME as k, CSRF_HEADER_NAME as A, isCsrfTokenWellFormed as j, issueCsrfToken as M, verifyCsrf as N } from "./csrf.js";
7
+ import { parseGuestCredential as P } from "./guestCredential.js";
8
+ import { GuestAuthError as F, dataStoreGuestAuth as I } from "./guestService.js";
9
+ import { authGuestDevicesTable as L, authGuestsTable as R } from "./guestSchema.js";
10
+ import { guestAuthPlugin as z } from "./guestPlugin.js";
11
+ import { allowedOverlayPage as B, overlayAuthPlugin as V } from "./overlayPlugin.js";
12
+ import { auth as H } from "./authExtensions.js";
13
+ import { TotpVerifyError as U, generateRecoveryCodes as W, generateTotpCode as G, generateTotpSecret as K, otpauthUrl as q, verifyTotpCode as J } from "./totp.js";
14
+ import { EMAIL_VERIFY_TTL_S as Y, MAGIC_LINK_TTL_S as X, MFA_PENDING_TTL_S as Z, NATIVE_CODE_TTL_S as Q, PASSWORD_RESET_TTL_S as $, hashToken as ee, mintSecret as te, mintToken as ne } from "./tokens.js";
15
+ import { runSubjectGuards as re } from "./subjectGuard.js";
16
+ import { EMAIL_NOT_VERIFIED_CODE as ie, EMAIL_VERIFIED_METADATA_KEY as ae, EMAIL_VERIFY_RESEND_COOLDOWN_S as oe, emailVerificationActive as se, emailVerificationGuard as ce, handleEmailVerifyConfirm as le, handleEmailVerifyRequest as ue, isEmailVerified as de, sendEmailVerification as fe, userEmailVerified as pe } from "./emailVerification.js";
17
+ import { configSessionSecrets as me, guardSubject as he, handleCsrf as ge, handleListMemberships as _e, handleListSessions as ve, handleMagicLinkConsume as ye, handleMagicLinkRequest as be, handleMfaEnrollStart as xe, handleMfaEnrollVerify as Se, handleMfaRegenerateRecoveryCodes as Ce, handleMfaUnenroll as we, handleMfaVerify as Te, handlePasswordResetConfirm as Ee, handlePasswordResetRequest as De, handleRevokeAllOtherSessions as Oe, handleRevokeSession as ke, handleSignIn as Ae, handleSignOut as je, handleSignUp as Me, handleSwitchTenant as Ne, issueUserSession as Pe } from "./handlers.js";
18
+ import { completeGuestPasswordUpgrade as Fe } from "./guestPasswordUpgrade.js";
19
+ import { authMemberships as Ie } from "./memberships.js";
20
+ import { postgresUserStore as Le } from "./postgresUserStore.js";
21
+ import { appUserStore as Re } from "./appUserStore.js";
22
+ import { generateChallenge as ze, verifyAssertion as Be, verifyRegistration as Ve } from "./webauthn.js";
23
+ import { dataStoreChallengeStore as He, handlePasskeyAssertOptions as Ue, handlePasskeyAssertVerify as We, handlePasskeyRegisterOptions as Ge, handlePasskeyRegisterVerify as Ke, memoryChallengeStore as qe } from "./passkeyHandlers.js";
24
+ import { DEFAULT_INVITER_ROLES as Je, DEFAULT_ROLE_RANK as Ye, INVITATION_MAX_PENDING as Xe, INVITATION_TTL_S as Ze, defaultCanGrantRole as Qe, handleAcceptInvitation as $e, handleAcceptInvitationWithSignUp as et, handleCreateInvitation as tt, handleListInvitations as nt, handlePreviewInvitation as rt, handleRevokeInvitation as it } from "./invitations.js";
25
+ import { IMPERSONATION_DEFAULT_DURATION_S as at, IMPERSONATION_FORBIDDEN_SUBPATHS as ot, handleImpersonationStart as st, handleImpersonationStop as ct, impersonationAuditRedactor as lt, impersonationOf as ut, isImpersonated as dt } from "./impersonation.js";
26
+ import { signAuthClaims as ft, verifyAuthClaims as pt } from "./signedClaims.js";
27
+ import { NATIVE_PENDING_COOKIE as mt, handleNativeAuthorize as ht, handleNativeComplete as gt, handleNativeExchange as _t, isAllowedRedirectUri as vt, nativeLoginRedirect as yt } from "./nativeRoutes.js";
28
+ import { voltroPasswordStrategy as bt } from "./strategy.js";
29
+ import { authRoutesPlugin as xt, getAuthSessionStrategies as St } from "./plugin.js";
30
+ import { mailSender as Ct } from "./mail.js";
31
+ export { k as CSRF_COOKIE_NAME, A as CSRF_HEADER_NAME, Je as DEFAULT_INVITER_ROLES, Ye as DEFAULT_ROLE_RANK, ie as EMAIL_NOT_VERIFIED_CODE, ae as EMAIL_VERIFIED_METADATA_KEY, oe as EMAIL_VERIFY_RESEND_COOLDOWN_S, Y as EMAIL_VERIFY_TTL_S, F as GuestAuthError, at as IMPERSONATION_DEFAULT_DURATION_S, ot as IMPERSONATION_FORBIDDEN_SUBPATHS, Xe as INVITATION_MAX_PENDING, Ze as INVITATION_TTL_S, X as MAGIC_LINK_TTL_S, Z as MFA_PENDING_TTL_S, Q as NATIVE_CODE_TTL_S, mt as NATIVE_PENDING_COOKIE, $ as PASSWORD_RESET_TTL_S, e as PasswordEmptyError, t as PasswordHashError, s as SESSION_COOKIE_NAME, U as TotpVerifyError, _ as UserAlreadyExistsError, v as UserNotFoundError, B as allowedOverlayPage, Re as appUserStore, H as auth, L as authGuestDevicesTable, R as authGuestsTable, Ie as authMemberships, xt as authRoutesPlugin, c as clearSessionCookie, Fe as completeGuestPasswordUpgrade, me as configSessionSecrets, He as dataStoreChallengeStore, I as dataStoreGuestAuth, w as dataStoreSessionRegistry, Qe as defaultCanGrantRole, se as emailVerificationActive, ce as emailVerificationGuard, ze as generateChallenge, W as generateRecoveryCodes, G as generateTotpCode, K as generateTotpSecret, St as getAuthSessionStrategies, he as guardSubject, z as guestAuthPlugin, $e as handleAcceptInvitation, et as handleAcceptInvitationWithSignUp, tt as handleCreateInvitation, ge as handleCsrf, le as handleEmailVerifyConfirm, ue as handleEmailVerifyRequest, st as handleImpersonationStart, ct as handleImpersonationStop, nt as handleListInvitations, _e as handleListMemberships, ve as handleListSessions, ye as handleMagicLinkConsume, be as handleMagicLinkRequest, xe as handleMfaEnrollStart, Se as handleMfaEnrollVerify, Ce as handleMfaRegenerateRecoveryCodes, we as handleMfaUnenroll, Te as handleMfaVerify, ht as handleNativeAuthorize, gt as handleNativeComplete, _t as handleNativeExchange, Ue as handlePasskeyAssertOptions, We as handlePasskeyAssertVerify, Ge as handlePasskeyRegisterOptions, Ke as handlePasskeyRegisterVerify, Ee as handlePasswordResetConfirm, De as handlePasswordResetRequest, rt as handlePreviewInvitation, Oe as handleRevokeAllOtherSessions, it as handleRevokeInvitation, ke as handleRevokeSession, Ae as handleSignIn, je as handleSignOut, Me as handleSignUp, Ne as handleSwitchTenant, n as hashPassword, ee as hashToken, lt as impersonationAuditRedactor, ut as impersonationOf, vt as isAllowedRedirectUri, j as isCsrfTokenWellFormed, de as isEmailVerified, dt as isImpersonated, M as issueCsrfToken, T as issueRegisteredSession, l as issueSession, Pe as issueUserSession, E as lockRegisteredSession, Ct as mailSender, g as makeSessionRevocationChecker, qe as memoryChallengeStore, y as memoryUserStore, te as mintSecret, ne as mintToken, yt as nativeLoginRedirect, r as needsRehash, b as newAuthId, q as otpauthUrl, V as overlayAuthPlugin, P as parseGuestCredential, i as parseScryptParams, Le as postgresUserStore, u as readSession, d as readSessionFromHeaders, f as readSessionKeyed, p as resolveSessionSecret, m as resolveSessionSecrets, D as rotateRegisteredSession, re as runSubjectGuards, fe as sendEmailVerification, x as sessionIdOfSubject, h as sessionSecretsOf, ft as signAuthClaims, S as subjectFromUser, C as subjectMemberships, pe as userEmailVerified, Be as verifyAssertion, pt as verifyAuthClaims, N as verifyCsrf, a as verifyPassword, o as verifyPasswordWithRehash, Ve as verifyRegistration, J as verifyTotpCode, bt as voltroPasswordStrategy, O as withSessionId };