@voltro/plugin-auth 0.86.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.
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,6 +355,10 @@ 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
@@ -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
  *
@@ -1370,6 +1569,13 @@ export declare interface LockoutStore {
1370
1569
  readonly clearLoginFailures: (email: string) => Effect.Effect<void>;
1371
1570
  }
1372
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
+
1373
1579
  /** Default magic-link lifetime — short, since it's an inbox round-trip. */
1374
1580
  export declare const MAGIC_LINK_TTL_S: number;
1375
1581
 
@@ -1611,6 +1817,32 @@ export declare const otpauthUrl: (params: {
1611
1817
  readonly secret: string;
1612
1818
  }) => string;
1613
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
+
1614
1846
  declare interface ParsedHash {
1615
1847
  readonly N: number;
1616
1848
  readonly r: number;
@@ -1618,6 +1850,13 @@ declare interface ParsedHash {
1618
1850
  readonly keyLen: number;
1619
1851
  }
1620
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
+
1621
1860
  /** Parse the scrypt parameters out of a stored hash. Returns null when the
1622
1861
  * string isn't a well-formed `scrypt$N$r$p$salt$derived`, or when its
1623
1862
  * parameters exceed the derivation ceiling. */
@@ -1913,15 +2152,18 @@ export declare interface SessionRecord {
1913
2152
  export declare type SessionRegistry = SessionStore;
1914
2153
 
1915
2154
  export declare interface SessionRegistryStoreOptions {
1916
- /** Borrow an already owned transaction; otherwise registry writes open one. */
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. */
1917
2159
  readonly transaction?: 'existing';
1918
2160
  }
1919
2161
 
1920
2162
  export declare interface SessionRevocationChecker {
1921
- /** Is this session still live? `false` means the row was revoked (or
1922
- * is past its `expiresAt`) — the caller rejects the cookie. Verdicts
1923
- * are cached for `ttlMs`. */
1924
- 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>;
1925
2167
  /** Drop the cached verdict for one session — called inline by
1926
2168
  * sign-out / revoke handlers so the kill is immediate on THIS
1927
2169
  * process (other replicas converge within `ttlMs`). */
@@ -1967,13 +2209,14 @@ export declare const sessionSecretsOf: (secret: SessionSecretInput) => SessionSe
1967
2209
 
1968
2210
  /** Server-side sessions — enumeration + revocation. */
1969
2211
  export declare interface SessionStore {
1970
- /** Active sessions for a user (most recent first). */
2212
+ /** Unrotated credentials with an existing canonical root owned by the same user, most recent first. */
1971
2213
  readonly listSessions: (userId: string) => Effect.Effect<ReadonlyArray<SessionRecord>>;
1972
2214
  /** Record a session on sign-in. */
1973
2215
  readonly insertSession: (session: Omit<SessionRecord, 'createdAt' | 'lastSeenAt'>) => Effect.Effect<SessionRecord>;
1974
- /** Look up one session row by id. The request-time revocation check
1975
- * (`makeSessionRevocationChecker`) calls this — a missing row means
1976
- * 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. */
1977
2220
  readonly findSession: (sessionId: string) => Effect.Effect<SessionRecord | null>;
1978
2221
  /** Slide a session row forward: bump `expiresAt` + `lastSeenAt`. Called
1979
2222
  * when the sliding-window renewal re-issues the cookie, so the row
@@ -1992,6 +2235,8 @@ export declare interface SessionStore {
1992
2235
  readonly revokeAllSessions: (userId: string) => Effect.Effect<number>;
1993
2236
  }
1994
2237
 
2238
+ export declare const signAuthClaims: (claims: Readonly<Record<string, unknown>>, secret: string, purpose: AuthClaimsPurpose) => string;
2239
+
1995
2240
  export declare interface SignInInput {
1996
2241
  readonly email: string;
1997
2242
  readonly password: string;
@@ -2171,7 +2416,7 @@ declare const UserNotFoundError_base: new <A extends Record<string, any> = {}>(a
2171
2416
 
2172
2417
  export declare interface UserRecord {
2173
2418
  readonly id: string;
2174
- readonly email: string;
2419
+ readonly email: string | null;
2175
2420
  /**
2176
2421
  * The stored scrypt hash — OPTIONAL, because not every identity has a
2177
2422
  * password. An SSO-only, magic-link-only, passkey-only, or provider-PAT
@@ -2236,6 +2481,8 @@ export declare type UserStoreResolver = (ctx: UserStoreBindContext) => UserStore
2236
2481
  /** Verify a passkey assertion (sign-in). */
2237
2482
  export declare const verifyAssertion: (input: AssertionVerifyInput) => AssertionOutcome;
2238
2483
 
2484
+ export declare const verifyAuthClaims: (value: string | undefined, secret: string, purpose: AuthClaimsPurpose, now?: () => number) => Record<string, unknown> | null;
2485
+
2239
2486
  /**
2240
2487
  * Verify a state-changing request's CSRF protection. Both the header
2241
2488
  * value and the cookie value must (a) be the SAME token and (b) carry a
@@ -2321,6 +2568,9 @@ export declare interface VoltroPasswordStrategyOptions {
2321
2568
  * (stateless); with it, nothing but a registered session authenticates,
2322
2569
  * and every session can be revoked. */
2323
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'>;
2324
2574
  }
2325
2575
 
2326
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
@@ -2,22 +2,30 @@ import { PasswordEmptyError as e, PasswordHashError as t, hashPassword as n, nee
2
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
3
  import { makeSessionRevocationChecker as g } from "./revocation.js";
4
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, rotateRegisteredSession as E, withSessionId as D } from "./sessionRegistry.js";
6
- import { authMemberships as O } from "./memberships.js";
7
- import { postgresUserStore as k } from "./postgresUserStore.js";
8
- import { appUserStore as A } from "./appUserStore.js";
9
- import { runSubjectGuards as j } from "./subjectGuard.js";
10
- import { TotpVerifyError as M, generateRecoveryCodes as N, generateTotpCode as P, generateTotpSecret as F, otpauthUrl as I, verifyTotpCode as L } from "./totp.js";
11
- import { EMAIL_VERIFY_TTL_S as R, MAGIC_LINK_TTL_S as z, MFA_PENDING_TTL_S as B, NATIVE_CODE_TTL_S as V, PASSWORD_RESET_TTL_S as H, hashToken as U, mintSecret as W, mintToken as G } from "./tokens.js";
12
- import { CSRF_COOKIE_NAME as K, CSRF_HEADER_NAME as q, isCsrfTokenWellFormed as J, issueCsrfToken as Y, verifyCsrf as X } from "./csrf.js";
13
- import { EMAIL_NOT_VERIFIED_CODE as Z, EMAIL_VERIFIED_METADATA_KEY as Q, EMAIL_VERIFY_RESEND_COOLDOWN_S as $, emailVerificationActive as ee, emailVerificationGuard as te, handleEmailVerifyConfirm as ne, handleEmailVerifyRequest as re, isEmailVerified as ie, sendEmailVerification as ae, userEmailVerified as oe } from "./emailVerification.js";
14
- import { configSessionSecrets as se, guardSubject as ce, handleCsrf as le, handleListMemberships as ue, handleListSessions as de, handleMagicLinkConsume as fe, handleMagicLinkRequest as pe, handleMfaEnrollStart as me, handleMfaEnrollVerify as he, handleMfaRegenerateRecoveryCodes as ge, handleMfaUnenroll as _e, handleMfaVerify as ve, handlePasswordResetConfirm as ye, handlePasswordResetRequest as be, handleRevokeAllOtherSessions as xe, handleRevokeSession as Se, handleSignIn as Ce, handleSignOut as we, handleSignUp as Te, handleSwitchTenant as Ee, issueUserSession as De } from "./handlers.js";
15
- import { generateChallenge as Oe, verifyAssertion as ke, verifyRegistration as Ae } from "./webauthn.js";
16
- import { dataStoreChallengeStore as je, handlePasskeyAssertOptions as Me, handlePasskeyAssertVerify as Ne, handlePasskeyRegisterOptions as Pe, handlePasskeyRegisterVerify as Fe, memoryChallengeStore as Ie } from "./passkeyHandlers.js";
17
- import { DEFAULT_INVITER_ROLES as Le, DEFAULT_ROLE_RANK as Re, INVITATION_MAX_PENDING as ze, INVITATION_TTL_S as Be, defaultCanGrantRole as Ve, handleAcceptInvitation as He, handleAcceptInvitationWithSignUp as Ue, handleCreateInvitation as We, handleListInvitations as Ge, handlePreviewInvitation as Ke, handleRevokeInvitation as qe } from "./invitations.js";
18
- import { IMPERSONATION_DEFAULT_DURATION_S as Je, IMPERSONATION_FORBIDDEN_SUBPATHS as Ye, handleImpersonationStart as Xe, handleImpersonationStop as Ze, impersonationAuditRedactor as Qe, impersonationOf as $e, isImpersonated as et } from "./impersonation.js";
19
- import { NATIVE_PENDING_COOKIE as tt, handleNativeAuthorize as nt, handleNativeComplete as rt, handleNativeExchange as it, isAllowedRedirectUri as at, nativeLoginRedirect as ot } from "./nativeRoutes.js";
20
- import { voltroPasswordStrategy as st } from "./strategy.js";
21
- import { authRoutesPlugin as ct, getAuthSessionStrategy as lt } from "./plugin.js";
22
- import { mailSender as ut } from "./mail.js";
23
- export { K as CSRF_COOKIE_NAME, q as CSRF_HEADER_NAME, Le as DEFAULT_INVITER_ROLES, Re as DEFAULT_ROLE_RANK, Z as EMAIL_NOT_VERIFIED_CODE, Q as EMAIL_VERIFIED_METADATA_KEY, $ as EMAIL_VERIFY_RESEND_COOLDOWN_S, R as EMAIL_VERIFY_TTL_S, Je as IMPERSONATION_DEFAULT_DURATION_S, Ye as IMPERSONATION_FORBIDDEN_SUBPATHS, ze as INVITATION_MAX_PENDING, Be as INVITATION_TTL_S, z as MAGIC_LINK_TTL_S, B as MFA_PENDING_TTL_S, V as NATIVE_CODE_TTL_S, tt as NATIVE_PENDING_COOKIE, H as PASSWORD_RESET_TTL_S, e as PasswordEmptyError, t as PasswordHashError, s as SESSION_COOKIE_NAME, M as TotpVerifyError, _ as UserAlreadyExistsError, v as UserNotFoundError, A as appUserStore, O as authMemberships, ct as authRoutesPlugin, c as clearSessionCookie, se as configSessionSecrets, je as dataStoreChallengeStore, w as dataStoreSessionRegistry, Ve as defaultCanGrantRole, ee as emailVerificationActive, te as emailVerificationGuard, Oe as generateChallenge, N as generateRecoveryCodes, P as generateTotpCode, F as generateTotpSecret, lt as getAuthSessionStrategy, ce as guardSubject, He as handleAcceptInvitation, Ue as handleAcceptInvitationWithSignUp, We as handleCreateInvitation, le as handleCsrf, ne as handleEmailVerifyConfirm, re as handleEmailVerifyRequest, Xe as handleImpersonationStart, Ze as handleImpersonationStop, Ge as handleListInvitations, ue as handleListMemberships, de as handleListSessions, fe as handleMagicLinkConsume, pe as handleMagicLinkRequest, me as handleMfaEnrollStart, he as handleMfaEnrollVerify, ge as handleMfaRegenerateRecoveryCodes, _e as handleMfaUnenroll, ve as handleMfaVerify, nt as handleNativeAuthorize, rt as handleNativeComplete, it as handleNativeExchange, Me as handlePasskeyAssertOptions, Ne as handlePasskeyAssertVerify, Pe as handlePasskeyRegisterOptions, Fe as handlePasskeyRegisterVerify, ye as handlePasswordResetConfirm, be as handlePasswordResetRequest, Ke as handlePreviewInvitation, xe as handleRevokeAllOtherSessions, qe as handleRevokeInvitation, Se as handleRevokeSession, Ce as handleSignIn, we as handleSignOut, Te as handleSignUp, Ee as handleSwitchTenant, n as hashPassword, U as hashToken, Qe as impersonationAuditRedactor, $e as impersonationOf, at as isAllowedRedirectUri, J as isCsrfTokenWellFormed, ie as isEmailVerified, et as isImpersonated, Y as issueCsrfToken, T as issueRegisteredSession, l as issueSession, De as issueUserSession, ut as mailSender, g as makeSessionRevocationChecker, Ie as memoryChallengeStore, y as memoryUserStore, W as mintSecret, G as mintToken, ot as nativeLoginRedirect, r as needsRehash, b as newAuthId, I as otpauthUrl, i as parseScryptParams, k as postgresUserStore, u as readSession, d as readSessionFromHeaders, f as readSessionKeyed, p as resolveSessionSecret, m as resolveSessionSecrets, E as rotateRegisteredSession, j as runSubjectGuards, ae as sendEmailVerification, x as sessionIdOfSubject, h as sessionSecretsOf, S as subjectFromUser, C as subjectMemberships, oe as userEmailVerified, ke as verifyAssertion, X as verifyCsrf, a as verifyPassword, o as verifyPasswordWithRehash, Ae as verifyRegistration, L as verifyTotpCode, st as voltroPasswordStrategy, D as withSessionId };
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 };
@@ -1,6 +1,6 @@
1
1
  import { newAuthId as e } from "./userStore.js";
2
- import { hashToken as t, mintSecret as n } from "./tokens.js";
3
- import { decideAdmission as r } from "./admission.js";
2
+ import { decideAdmission as t } from "./admission.js";
3
+ import { hashToken as n, mintSecret as r } from "./tokens.js";
4
4
  import { Effect as i } from "effect";
5
5
  //#region src/invitations.ts
6
6
  var a = 604800, o = 500, s = [
@@ -34,12 +34,12 @@ var a = 604800, o = 500, s = [
34
34
  }), h = (e, t, n, r) => i.gen(function* () {
35
35
  let i = yield* n.membershipRole(e, t);
36
36
  return i === null || !(r?.inviterRoles ?? c).includes(i) ? f(403, { error: "not_permitted" }) : { role: i };
37
- }), g = (e) => "status" in e, _ = (t, r, a) => i.gen(function* () {
37
+ }), g = (e) => "status" in e, _ = (t, n, a) => i.gen(function* () {
38
38
  let o = t.email.trim().toLowerCase();
39
39
  if (!o || !o.includes("@")) return f(400, { error: "a valid email is required" });
40
40
  if (!t.tenantId) return f(400, { error: "tenantId required" });
41
41
  if (!t.role) return f(400, { error: "role required" });
42
- let s = a.invitations, c = yield* h(t.inviterUserId, t.tenantId, r, s);
42
+ let s = a.invitations, c = yield* h(t.inviterUserId, t.tenantId, n, s);
43
43
  if (g(c)) return c;
44
44
  if (!d(s)({
45
45
  inviterRole: c.role,
@@ -50,9 +50,9 @@ var a = 604800, o = 500, s = [
50
50
  message: `A '${c.role}' may not invite a '${t.role}'.`
51
51
  });
52
52
  let l = new Date((a.now ?? Date.now)());
53
- if ((yield* r.countPendingInvitations(t.tenantId, l)) >= (s?.maxPending ?? 500)) return f(429, { error: "too_many_pending_invitations" });
54
- yield* r.supersedePendingInvitations(t.tenantId, o, t.inviterUserId, l).pipe(i.ignore);
55
- let u = s?.ttlSeconds ?? 604800, _ = n(u), v = yield* r.insertInvitation({
53
+ if ((yield* n.countPendingInvitations(t.tenantId, l)) >= (s?.maxPending ?? 500)) return f(429, { error: "too_many_pending_invitations" });
54
+ yield* n.supersedePendingInvitations(t.tenantId, o, t.inviterUserId, l).pipe(i.ignore);
55
+ let u = s?.ttlSeconds ?? 604800, _ = r(u), v = yield* n.insertInvitation({
56
56
  id: e("inv"),
57
57
  tokenHash: _.tokenHash,
58
58
  email: o,
@@ -88,9 +88,9 @@ var a = 604800, o = 500, s = [
88
88
  ok: !0,
89
89
  revoked: 1
90
90
  }) : f(404, { error: "invitation_not_found" });
91
- }), b = (e, n, r = {}) => i.gen(function* () {
91
+ }), b = (e, t, r = {}) => i.gen(function* () {
92
92
  if (!e.token) return f(400, { error: "token required" });
93
- let i = yield* n.findInvitationByHash(t(e.token));
93
+ let i = yield* t.findInvitationByHash(n(e.token));
94
94
  if (!i) return f(404, { error: "invalid_invitation" });
95
95
  let a = new Date((r.now ?? Date.now)());
96
96
  return f(200, {
@@ -101,24 +101,24 @@ var a = 604800, o = 500, s = [
101
101
  role: i.role,
102
102
  expiresAt: i.expiresAt.toISOString()
103
103
  });
104
- }), x = (e, n, a = {}) => i.gen(function* () {
104
+ }), x = (e, r, a = {}) => i.gen(function* () {
105
105
  if (!e.token) return f(400, { error: "token required" });
106
- let o = t(e.token), s = new Date((a.now ?? Date.now)()), c = yield* n.findInvitationByHash(o);
106
+ let o = n(e.token), s = new Date((a.now ?? Date.now)()), c = yield* r.findInvitationByHash(o);
107
107
  if (!c) return f(401, { error: "invalid_or_expired_invitation" });
108
108
  if (!e.userId) return f(401, {
109
109
  error: "authentication_required",
110
110
  email: c.email
111
111
  });
112
- let l = yield* n.findById(e.userId);
112
+ let l = yield* r.findById(e.userId);
113
113
  if (!l) return f(401, {
114
114
  error: "authentication_required",
115
115
  email: c.email
116
116
  });
117
- if (l.email.toLowerCase() !== c.email.toLowerCase()) return f(403, {
117
+ if (l.email === null || l.email.toLowerCase() !== c.email.toLowerCase()) return f(403, {
118
118
  error: "invitation_email_mismatch",
119
119
  message: "This invitation was issued to a different email address. Sign in as that address to accept it."
120
120
  });
121
- let u = yield* r(a.admit, {
121
+ let u = yield* t(a.admit, {
122
122
  email: c.email,
123
123
  tenantId: c.tenantId,
124
124
  via: "invitation"
@@ -128,14 +128,14 @@ var a = 604800, o = 500, s = [
128
128
  reason: u.reason,
129
129
  message: u.message
130
130
  });
131
- let d = yield* n.acceptInvitation(o, l.id, s);
131
+ let d = yield* r.acceptInvitation(o, l.id, s);
132
132
  if (!d) return f(401, { error: "invalid_or_expired_invitation" });
133
- let p = yield* n.addMembership({
133
+ let p = yield* r.addMembership({
134
134
  userId: l.id,
135
135
  tenantId: d.tenantId,
136
136
  role: d.role
137
137
  });
138
- return yield* n.markEmailVerified(l.id).pipe(i.ignore), f(200, {
138
+ return yield* r.markEmailVerified(l.id).pipe(i.ignore), f(200, {
139
139
  ok: !0,
140
140
  membership: {
141
141
  tenantId: p.tenantId,
@@ -143,18 +143,18 @@ var a = 604800, o = 500, s = [
143
143
  joinedAt: p.joinedAt.toISOString()
144
144
  }
145
145
  });
146
- }), S = (n, a, o) => i.gen(function* () {
147
- if (!n.token || !n.password) return f(400, { error: "token and password required" });
148
- if (n.password.length < (o.minPasswordLength ?? 8)) return f(400, { error: "password must be at least 8 characters" });
149
- let s = t(n.token), c = new Date((o.now ?? Date.now)()), l = yield* a.findInvitationByHash(s);
146
+ }), S = (r, a, o) => i.gen(function* () {
147
+ if (!r.token || !r.password) return f(400, { error: "token and password required" });
148
+ if (r.password.length < (o.minPasswordLength ?? 8)) return f(400, { error: "password must be at least 8 characters" });
149
+ let s = n(r.token), c = new Date((o.now ?? Date.now)()), l = yield* a.findInvitationByHash(s);
150
150
  if (!l) return f(401, { error: "invalid_or_expired_invitation" });
151
151
  if (yield* a.findByEmail(l.email)) return f(409, {
152
152
  error: "account_exists",
153
153
  message: "An account already exists for this address. Sign in, then accept the invitation."
154
154
  });
155
- let u = yield* o.hashPassword(n.password).pipe(i.orElseSucceed(() => ""));
155
+ let u = yield* o.hashPassword(r.password).pipe(i.orElseSucceed(() => ""));
156
156
  if (!u) return f(500, { error: "hash_failed" });
157
- let d = yield* r(o.admit, {
157
+ let d = yield* t(o.admit, {
158
158
  email: l.email,
159
159
  tenantId: l.tenantId,
160
160
  via: "invitation"