@spfn/auth 0.3.0-beta.22 → 0.3.0-beta.24

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 (34) hide show
  1. package/README.md +308 -4
  2. package/dist/client-proof.js +13 -5
  3. package/dist/client-proof.js.map +1 -1
  4. package/dist/client.d.ts +52 -1
  5. package/dist/client.js +40 -0
  6. package/dist/client.js.map +1 -1
  7. package/dist/config.d.ts +120 -0
  8. package/dist/config.js +55 -0
  9. package/dist/config.js.map +1 -1
  10. package/dist/crypto.d.ts +1 -1
  11. package/dist/errors.d.ts +208 -3
  12. package/dist/errors.js +131 -2
  13. package/dist/errors.js.map +1 -1
  14. package/dist/index.d.ts +71 -5
  15. package/dist/index.js +134 -3
  16. package/dist/index.js.map +1 -1
  17. package/dist/{machine-principals-B7N8gux0.d.ts → machine-principals-ZJd9anVT.d.ts} +2045 -886
  18. package/dist/nextjs/api.js +156 -6
  19. package/dist/nextjs/api.js.map +1 -1
  20. package/dist/nextjs/server.d.ts +42 -24
  21. package/dist/nextjs/server.js +72 -5
  22. package/dist/nextjs/server.js.map +1 -1
  23. package/dist/server.d.ts +1033 -598
  24. package/dist/server.js +3239 -1541
  25. package/dist/server.js.map +1 -1
  26. package/dist/{session-Dfwu5g2W.d.ts → session-BbhAGZtA.d.ts} +57 -1
  27. package/dist/{types-DYyhze28.d.ts → types-CTdoTOxM.d.ts} +24 -1
  28. package/migrations/20260918162828_handy_titania/migration.sql +35 -0
  29. package/migrations/20260918162828_handy_titania/snapshot.json +5948 -0
  30. package/migrations/20260918184037_happy_mordo/migration.sql +4 -0
  31. package/migrations/20260918184037_happy_mordo/snapshot.json +6000 -0
  32. package/migrations/20260918184152_dear_rictor/migration.sql +3 -0
  33. package/migrations/20260918184152_dear_rictor/snapshot.json +6039 -0
  34. package/package.json +1 -1
package/dist/server.d.ts CHANGED
@@ -1,8 +1,8 @@
1
- import { A as AuthInitOptions, u as OAuthProvider, s as VerificationPurpose, N as NewPasskey, v as Passkey, q as PermissionCategory, w as AuthContext } from './machine-principals-B7N8gux0.js';
2
- export { x as ApproveDeviceAuthParams, y as AuthDeletionCancelledPayload, z as AuthDeletionCompletedPayload, B as AuthDeletionRequestedPayload, E as AuthDeviceRegisteredPayload, G as AuthLoginPayload, H as AuthPasswordResetPayload, J as AuthProfileOutcome, M as AuthProfileVerifier, Q as AuthProviderSchema, T as AuthRegisterPayload, W as ChangePasswordParams, X as CompletePasswordResetParams, Y as CompleteSignupParams, Z as ConfirmPasswordResetParams, d as ConfirmPasswordResetResult, _ as ConfirmSignupLinkParams, C as ConfirmSignupLinkResult, $ as DenyDeviceAuthParams, a0 as DeviceAuthApprovedResult, a1 as DeviceAuthInfoParams, D as DeviceAuthInfoResult, a2 as DeviceAuthPendingResult, a3 as DeviceAuthPollResponseSchema, a4 as DeviceNameSchema, a5 as DeviceRegistrationChannel, a6 as EmailSchema, a7 as FingerprintSchema, a8 as FinishPasskeyEnrollmentParams, F as FinishPasskeyEnrollmentResult, a9 as FinishPasskeyLoginParams, aa as InvitationAcceptedPayload, ab as InvitationCreatedPayload, I as IssueOneTimeTokenResult, ac as KEY_FINGERPRINT_PREFIX_LENGTH, ad as KeyIdSchema, K as KeySummary, ae as LoginParams, L as LoginResult, af as LogoutParams, ag as MachinePrincipal, ah as MachineVerifierRegistration, ai as NativeVerifyOptions, aj as NormalizedIdentity, l as OAuth2AuthorizationCodeIssued, ak as OAuth2AuthorizeParams, k as OAuth2ConsentView, n as OAuth2GrantSummary, al as OAuth2ScopeDescription, am as OAuthCallbackParams, an as OAuthCallbackResult, ao as OAuthCodeExchangeOptions, ap as OAuthNativeParams, i as OAuthNativeResult, aq as OAuthStartParams, O as OAuthStartResult, ar as OAuthTokens, as as OAuthUnlinkedPayload, at as PASSKEY_DEVICE_TYPES, au as PASSKEY_LABEL_MAX_LENGTH, av as PasskeyDeviceType, f as PasskeySummary, aw as PasswordSchema, ax as PhoneSchema, ay as PlatformSchema, az as PollDeviceAuthParams, aA as PollDeviceAuthResult, aB as PublicKeySchema, aC as RecentAuthenticationParams, aD as RegisterParams, aE as RegisterPublicKeyParams, a as RegisterResult, aF as RenamePasskeyParams, aG as RequestPasswordResetParams, c as RequestPasswordResetResult, aH as RequestSignupLinkParams, b as RequestSignupLinkResult, aI as RevokeAllKeysParams, h as RevokeAllKeysResult, aJ as RevokeKeyParams, aK as RevokePasskeyParams, aL as RotateKeyParams, g as RotateKeyResult, aM as SendVerificationCodeParams, S as SendVerificationCodeResult, aN as StartDeviceAuthParams, e as StartDeviceAuthResult, aO as StartPasskeyEnrollmentParams, aP as TargetTypeSchema, aQ as UnlinkNotification, aR as UnlinkNotifyRejection, aS as UnlinkNotifyRequest, aT as UnlinkNotifyResult, aU as UserCodeSchema, V as VERIFICATION_PURPOSES, r as VERIFICATION_TARGET_TYPES, aV as VerificationPurposeSchema, t as VerificationTargetType, aW as VerifyCodeParams, aX as VerifyCodeResult, aY as approveDeviceAuthService, aZ as approveOAuth2AuthorizeService, a_ as assertNotLastRecoveryCredential, a$ as assertRecentAuthentication, b0 as authDeletionCancelledEvent, b1 as authDeletionCompletedEvent, b2 as authDeletionRequestedEvent, b3 as authDeviceRegisteredEvent, b4 as authLoginEvent, b5 as authPasswordResetEvent, b6 as authRegisterEvent, m as authRouter, b7 as authenticate, b8 as buildOAuthErrorUrl, b9 as changePasswordService, ba as completePasswordResetService, bb as completeSignupService, bc as confirmPasswordResetService, bd as confirmSignupLinkService, be as denyDeviceAuthService, bf as denyOAuth2AuthorizeService, bg as describeOAuth2AuthorizeRequestService, bh as finishPasskeyEnrollmentService, bi as finishPasskeyLoginService, bj as getDeviceAuthInfoService, bk as getEnabledOAuthProviders, bl as getGoogleAccessToken, bm as getMachinePrincipal, bn as getOAuthProvider, bo as getRegisteredProviders, bp as invitationAcceptedEvent, bq as invitationCreatedEvent, br as isOAuthProviderEnabled, bs as issueOneTimeTokenService, bt as listKeysService, bu as listOAuth2GrantsService, bv as listPasskeysService, bw as loginService, bx as logoutService, by as machineAuth, bz as oauthCallbackService, bA as oauthNativeService, bB as oauthStartService, bC as oauthUnlinkNotifyService, bD as oauthUnlinkedEvent, bE as optionalAuth, bF as passkeys, bG as pollDeviceAuthService, bH as registerAuthProfile, bI as registerMachineVerifier, bJ as registerOAuthProvider, bK as registerPublicKeyService, bL as registerService, bM as renamePasskeyService, bN as requestPasswordResetService, bO as requestSignupLinkService, bP as requireEnabledProvider, bQ as requireMachineScope, bR as resolveAuthenticatedUser, bS as revokeAllKeysService, bT as revokeAllOAuth2GrantsForUser, bU as revokeKeyService, bV as revokeOAuth2GrantService, bW as revokePasskeyService, bX as rotateKeyService, bY as runAuthProfile, bZ as selectAuthProfile, b_ as sendVerificationCodeService, b$ as startDeviceAuthService, c0 as startPasskeyEnrollmentService, c1 as startPasskeyLoginService, c2 as verifyCodeService, c3 as verifyOneTimeTokenService } from './machine-principals-B7N8gux0.js';
1
+ import { A as AuthInitOptions, y as OAuthProvider, N as NewUserPublicKey, w as VerificationPurpose, z as NewPasskey, B as Passkey, E as MfaVerification, G as MfaVerificationMethod, u as PermissionCategory, H as UserPublicKey, J as AuthContext } from './machine-principals-ZJd9anVT.js';
2
+ export { Q as ApproveDeviceAuthParams, W as AssertStepUpParams, X as AuthDeletionCancelledPayload, Y as AuthDeletionCompletedPayload, Z as AuthDeletionRequestedPayload, _ as AuthDeviceRegisteredPayload, $ as AuthLoginPayload, a0 as AuthPasswordResetPayload, a1 as AuthProfileOutcome, a2 as AuthProfileVerifier, a3 as AuthProviderSchema, a4 as AuthRegisterPayload, a5 as BearerOutcome, a6 as BearerRefusal, a7 as ChangePasswordParams, a8 as CompletePasswordResetParams, a9 as CompleteSignupParams, aa as ConfirmPasswordResetParams, d as ConfirmPasswordResetResult, ab as ConfirmSignupLinkParams, C as ConfirmSignupLinkResult, ac as ConfirmTotpParams, h as ConfirmTotpResult, ad as DenyDeviceAuthParams, ae as DeviceAuthApprovedResult, af as DeviceAuthInfoParams, D as DeviceAuthInfoResult, ag as DeviceAuthPendingResult, ah as DeviceAuthPollResponseSchema, ai as DeviceNameSchema, aj as DeviceRegistrationChannel, ak as DisableSessionBindingParams, al as EmailSchema, am as FingerprintSchema, an as FinishPasskeyEnrollmentParams, F as FinishPasskeyEnrollmentResult, ao as FinishPasskeyLoginParams, ap as FinishSessionRenewParams, aq as InvitationAcceptedPayload, ar as InvitationCreatedPayload, I as IssueOneTimeTokenResult, as as KEY_FINGERPRINT_PREFIX_LENGTH, at as KeyIdSchema, K as KeySummary, au as LoginParams, e as LoginResult, av as LogoutParams, aw as MFA_VERIFICATION_METHODS, ax as MachinePrincipal, ay as MachineVerifierRegistration, az as MarkPasskeyParams, M as MfaStatus, aA as NativeVerifyOptions, aB as NewMfaVerification, aC as NormalizedIdentity, q as OAuth2AuthorizationCodeIssued, aD as OAuth2AuthorizeParams, p as OAuth2ConsentView, r as OAuth2GrantSummary, aE as OAuth2ScopeDescription, aF as OAuthCallbackParams, aG as OAuthCallbackResult, aH as OAuthCodeExchangeOptions, aI as OAuthNativeParams, n as OAuthNativeResult, aJ as OAuthStartParams, O as OAuthStartResult, aK as OAuthTokens, aL as OAuthUnlinkedPayload, aM as PASSKEY_DEVICE_TYPES, aN as PASSKEY_LABEL_MAX_LENGTH, aO as PasskeyDeviceType, g as PasskeySummary, aP as PasswordSchema, aQ as PhoneSchema, aR as PlatformSchema, aS as PollDeviceAuthParams, aT as PollDeviceAuthResult, aU as PublicKeySchema, aV as RecentAuthenticationParams, aW as RegisterParams, aX as RegisterPublicKeyParams, a as RegisterResult, aY as RenamePasskeyParams, aZ as RequestPasswordResetParams, c as RequestPasswordResetResult, a_ as RequestSignupLinkParams, b as RequestSignupLinkResult, a$ as RevokeAllKeysParams, j as RevokeAllKeysResult, b0 as RevokeKeyParams, b1 as RevokePasskeyParams, b2 as RotateKeyParams, i as RotateKeyResult, b3 as SendVerificationCodeParams, S as SendVerificationCodeResult, b4 as SessionBindingParams, k as SessionBindingResult, b5 as StartDeviceAuthParams, f as StartDeviceAuthResult, b6 as StartPasskeyEnrollmentParams, b7 as StartSessionRenewParams, b8 as StepUpParams, b9 as TargetTypeSchema, T as TotpEnrolmentResult, ba as UnlinkNotification, bb as UnlinkNotifyRejection, bc as UnlinkNotifyRequest, bd as UnlinkNotifyResult, be as UserCodeSchema, V as VERIFICATION_PURPOSES, v as VERIFICATION_TARGET_TYPES, bf as VerificationPurposeSchema, x as VerificationTargetType, bg as VerifyCodeParams, bh as VerifyCodeResult, bi as admitBearerKey, bj as approveDeviceAuthService, bk as approveOAuth2AuthorizeService, bl as assertNotLastRecoveryCredential, bm as assertRecentAuthentication, bn as assertStepUp, bo as authDeletionCancelledEvent, bp as authDeletionCompletedEvent, bq as authDeletionRequestedEvent, br as authDeviceRegisteredEvent, bs as authLoginEvent, bt as authPasswordResetEvent, bu as authRegisterEvent, m as authRouter, bv as authenticate, bw as bearerAuthContext, bx as buildOAuthErrorUrl, by as carryStepUpVerification, bz as changePasswordService, bA as completePasswordResetService, bB as completeSignupService, bC as confirmPasswordResetService, bD as confirmSignupLinkService, bE as confirmTotpEnrolmentService, bF as denyDeviceAuthService, bG as denyOAuth2AuthorizeService, bH as describeOAuth2AuthorizeRequestService, bI as disableMfaService, bJ as disableSessionBindingService, bK as enableSessionBindingService, bL as finishPasskeyEnrollmentService, bM as finishPasskeyLoginService, bN as finishSessionRenewService, bO as getDeviceAuthInfoService, bP as getEnabledOAuthProviders, bQ as getGoogleAccessToken, bR as getMachinePrincipal, bS as getOAuthProvider, bT as getRegisteredProviders, bU as getSessionBindingService, bV as invitationAcceptedEvent, bW as invitationCreatedEvent, bX as isOAuthProviderEnabled, bY as issueOneTimeTokenService, bZ as keySessionBindingService, b_ as listKeysService, b$ as listOAuth2GrantsService, c0 as listPasskeysService, c1 as loginService, c2 as logoutService, c3 as machineAuth, c4 as markPasskeySecondFactorService, c5 as mfaEnrolledForUser, c6 as mfaStatusService, c7 as mfaVerifications, c8 as oauthCallbackService, c9 as oauthNativeService, ca as oauthStartService, cb as oauthUnlinkNotifyService, cc as oauthUnlinkedEvent, cd as optionalAuth, ce as passkeys, cf as pollDeviceAuthService, cg as regenerateRecoveryCodesService, ch as registerAuthProfile, ci as registerMachineVerifier, cj as registerOAuthProvider, ck as registerPublicKeyService, cl as registerService, cm as renamePasskeyService, cn as requestPasswordResetService, co as requestSignupLinkService, cp as requireEnabledProvider, cq as requireMachineScope, cr as resolveAuthenticatedUser, cs as revokeAllKeysService, ct as revokeAllOAuth2GrantsForUser, cu as revokeKeyService, cv as revokeOAuth2GrantService, cw as revokePasskeyService, cx as rotateKeyService, cy as runAuthProfile, cz as selectAuthProfile, cA as sendVerificationCodeService, cB as startDeviceAuthService, cC as startPasskeyEnrollmentService, cD as startPasskeyLoginService, cE as startSessionBindingDisableService, cF as startSessionRenewService, cG as startStepUpService, cH as startTotpEnrolmentService, cI as stepUpService, cJ as sweepUnconfirmedMfaService, cK as userPublicKeys, cL as verifyCodeService, cM as verifyOneTimeTokenService, cN as verifySecondFactor } from './machine-principals-ZJd9anVT.js';
3
3
  import * as drizzle_orm_pg_core from 'drizzle-orm/pg-core';
4
- import { j as SocialProvider, K as KeyAlgorithmType, d as InvitationStatus, c as AccountDeletionRequestedBy, i as PurgeStrategy } from './types-DYyhze28.js';
5
- export { A as ACCOUNT_DELETION_REQUESTED_BY, a as ACCOUNT_DELETION_REQUEST_STATUSES, b as AccountDeletionRequestStatus, I as INVITATION_STATUSES, e as KEY_ALGORITHM, f as KEY_DEVICE_NAME_MAX_LENGTH, g as KEY_PLATFORM, h as KeyPlatformType, P as PURGE_STRATEGIES, S as SOCIAL_PROVIDERS, U as USER_STATUSES, k as UserStatus } from './types-DYyhze28.js';
4
+ import { l as SocialProvider, K as KeyAlgorithmType, d as InvitationStatus, c as AccountDeletionRequestedBy, i as PurgeStrategy } from './types-CTdoTOxM.js';
5
+ export { A as ACCOUNT_DELETION_REQUESTED_BY, a as ACCOUNT_DELETION_REQUEST_STATUSES, b as AccountDeletionRequestStatus, I as INVITATION_STATUSES, e as KEY_ALGORITHM, f as KEY_DEVICE_NAME_MAX_LENGTH, g as KEY_PLATFORM, h as KeyPlatformType, P as PURGE_STRATEGIES, j as SESSION_BINDINGS, k as SOCIAL_PROVIDERS, S as SessionBindingType, U as USER_STATUSES, m as UserStatus } from './types-CTdoTOxM.js';
6
6
  import { UserProfile as UserProfile$1, ProfileInfo } from '@spfn/auth';
7
7
  import { BaseRepository } from '@spfn/core/db';
8
8
  import { c as ClientIdentity } from './wire-version-CtzMKvBB.js';
@@ -10,7 +10,7 @@ import { Context } from 'hono';
10
10
  import * as _spfn_core_route from '@spfn/core/route';
11
11
  import { NamedMiddleware } from '@spfn/core/route';
12
12
  export { KeyPair, generateClientToken, generateKeyPair, generateKeyPairES256, generateKeyPairRS256, getKeySize, shouldRotateKey } from './crypto.js';
13
- export { S as SessionData, g as getSessionInfo, i as isSafeReturnPath, s as sealSession, a as shouldRefreshSession, u as unsealSession } from './session-Dfwu5g2W.js';
13
+ export { S as SessionData, g as getSessionInfo, i as isSafeReturnPath, s as sealSession, a as shouldRefreshSession, u as unsealSession } from './session-BbhAGZtA.js';
14
14
  import { JWTPayload } from 'jose';
15
15
  import { SSETokenManager, SSETokenStore } from '@spfn/core/event/sse';
16
16
  import * as _spfn_core_logger from '@spfn/core/logger';
@@ -21,95 +21,6 @@ import '@spfn/core/event';
21
21
  import '@spfn/auth/server';
22
22
  import 'jsonwebtoken';
23
23
 
24
- /**
25
- * @spfn/auth - Signed Sign-Out-Everywhere Link
26
- *
27
- * A link an app mails to an account's proven address, which signs every device
28
- * of that account out without a session. It exists for the case the rest of the
29
- * key surface cannot serve: the owner suspects a device they do not recognise,
30
- * and the device they would have to sign in on to do something about it is the
31
- * one they no longer trust.
32
- *
33
- * Three calls, and each is deliberately not the other two. `createRevokeAllLink`
34
- * mints the capability and hands the app a URL. `describeRevokeAllLink` says
35
- * whether that URL still works and changes nothing, so a mail scanner opening
36
- * the page costs nothing. `consumeRevokeAllLink` is the only one that revokes,
37
- * and it takes the owner pressing the button.
38
- *
39
- * The caller's obligation, which this package cannot enforce: the URL
40
- * `createRevokeAllLink` returns carries the plaintext token, because the package
41
- * sends no mail here. Do not log it, do not persist it, do not put it in a job
42
- * payload — hand it to the mail template and let it go. Every other link in this
43
- * package is minted inside the worker that sends it precisely so that no caller
44
- * ever holds one; this one cannot be, and the obligation moves to the app.
45
- */
46
- interface CreateRevokeAllLinkOptions {
47
- /**
48
- * Minutes the link stays valid. Defaults to
49
- * `SPFN_AUTH_REVOKE_ALL_LINK_TTL_MINUTES`, itself 30.
50
- */
51
- ttlMinutes?: number;
52
- }
53
- interface RevokeAllLink {
54
- /** Absolute URL of the app page, token in its query string. Never log it. */
55
- url: string;
56
- expiresAt: Date;
57
- }
58
- /** What the describe call tells the page, so it can say what pressing the button does. */
59
- interface RevokeAllLinkDescription {
60
- expiresAt: Date;
61
- activeKeyCount: number;
62
- }
63
- /**
64
- * Mint a sign-out-everywhere link for an account.
65
- *
66
- * Issuing supersedes every live link the account already has, the rule the
67
- * password-reset flow follows: an owner who asks twice must not leave the first
68
- * capability working in their mailbox after using the second.
69
- *
70
- * The row records the account's key generation, so the link dies the moment any
71
- * other path signs every device out — a password reset, a password change, a
72
- * deletion request, or the ordinary revoke-all route.
73
- *
74
- * Send it beside a password reset link. It ends the sessions; it does not change
75
- * the password that let them start.
76
- *
77
- * @param userId - The account to issue for
78
- * @throws ValidationError `ttlMinutes` that is not a positive integer — no row is written
79
- * @throws NotFoundError no such account, rather than a foreign-key failure at the insert
80
- */
81
- declare function createRevokeAllLink(userId: string | number, options?: CreateRevokeAllLinkOptions): Promise<RevokeAllLink>;
82
- /**
83
- * Say whether a presented link still works, and what pressing the button would
84
- * do. Changes nothing — a mail scanner that opens the page has done nothing.
85
- *
86
- * @throws RevokeAllLinkError every refusal, in one shape
87
- */
88
- declare function describeRevokeAllLink(token: string): Promise<RevokeAllLinkDescription>;
89
- /**
90
- * Spend a link and sign every device of the account out.
91
- *
92
- * The claim is one statement, so of two requests carrying the same token exactly
93
- * one revokes and the other is refused as if the token were unknown — which by
94
- * then it is. `includeCurrent` is true because there is no current device here:
95
- * the caller holds a mailbox, not a session, and sparing something would mean
96
- * sparing whatever the owner is trying to cut off.
97
- *
98
- * @returns how many device keys were revoked; zero is a success, not a refusal
99
- * @throws RevokeAllLinkError every refusal, in the same shape `describe` uses
100
- */
101
- declare function consumeRevokeAllLink(token: string): Promise<{
102
- revokedCount: number;
103
- }>;
104
- /**
105
- * Delete link rows that can no longer answer anything.
106
- *
107
- * @returns number of rows deleted
108
- */
109
- declare function purgeRevokeAllTokensService(): Promise<{
110
- deleted: number;
111
- }>;
112
-
113
24
  /**
114
25
  * @spfn/auth - Global Configuration
115
26
  *
@@ -341,6 +252,27 @@ declare function getCsrfMode(): CsrfMode;
341
252
  * Get the paths exempted from the CSRF check (exact match, backend route paths)
342
253
  */
343
254
  declare function getCsrfExemptPaths(): string[];
255
+ /**
256
+ * How long a key bound to a passkey lives, in milliseconds.
257
+ *
258
+ * A positive override is honoured; anything else falls back to the policy
259
+ * constant. Nothing refuses boot over it — unlike the passkey relying party, a
260
+ * nonsensical value here does not make every ceremony fail, it just means the
261
+ * default applies.
262
+ */
263
+ declare function getBoundKeyTtlMs(): number;
264
+ /** How long past expiry a bound key may still be renewed, in milliseconds. */
265
+ declare function getBoundKeyRenewGraceMs(): number;
266
+ /** How close two sightings from two addresses must be to count as concurrent. */
267
+ declare function getConcurrentUseWindowMs(): number;
268
+ /**
269
+ * The page a bound session whose key expired is sent to.
270
+ *
271
+ * `RequireAuth` redirects here instead of to the sign-in page; the app renders a
272
+ * client component there that calls `renewSession(api)` and returns the person to
273
+ * where they were.
274
+ */
275
+ declare function getSessionRenewPath(): string;
344
276
  /**
345
277
  * The relying party this deployment presents to authenticators, resolved.
346
278
  *
@@ -399,6 +331,116 @@ declare function getPasskeyConfig(env?: PasskeyEnvSource): PasskeyConfig;
399
331
  * @throws PasskeyConfigError when a passkey variable is set and cannot be honoured
400
332
  */
401
333
  declare function assertPasskeyConfig(env?: PasskeyEnvSource): void;
334
+ /** What the second-factor routes read out of the environment. */
335
+ interface MfaConfig {
336
+ /** Name the authenticator app files the account under. */
337
+ issuer: string;
338
+ /** How long a device's step-up stays good for a sensitive change. */
339
+ stepUpWindowMs: number;
340
+ }
341
+ /**
342
+ * Resolve the second-factor configuration.
343
+ *
344
+ * Deliberately reads no passkey setting beyond `SPFN_AUTH_PASSKEY_RP_NAME`,
345
+ * and reads that as a plain string rather than through `getPasskeyConfig()`:
346
+ * an app with no passkeys configured at all must be able to enrol a TOTP and
347
+ * to step up, and `getPasskeyConfig()` refuses to resolve for such an app.
348
+ *
349
+ * Nothing here can fail the way the passkey config can, so there is no boot
350
+ * check to match: a bad step-up window falls back to the default rather than
351
+ * refusing to start, because the value it would refuse over is a number of
352
+ * minutes and the default is the safe one.
353
+ */
354
+ declare function getMfaConfig(): MfaConfig;
355
+
356
+ /**
357
+ * @spfn/auth - Signed Sign-Out-Everywhere Link
358
+ *
359
+ * A link an app mails to an account's proven address, which signs every device
360
+ * of that account out without a session. It exists for the case the rest of the
361
+ * key surface cannot serve: the owner suspects a device they do not recognise,
362
+ * and the device they would have to sign in on to do something about it is the
363
+ * one they no longer trust.
364
+ *
365
+ * Three calls, and each is deliberately not the other two. `createRevokeAllLink`
366
+ * mints the capability and hands the app a URL. `describeRevokeAllLink` says
367
+ * whether that URL still works and changes nothing, so a mail scanner opening
368
+ * the page costs nothing. `consumeRevokeAllLink` is the only one that revokes,
369
+ * and it takes the owner pressing the button.
370
+ *
371
+ * The caller's obligation, which this package cannot enforce: the URL
372
+ * `createRevokeAllLink` returns carries the plaintext token, because the package
373
+ * sends no mail here. Do not log it, do not persist it, do not put it in a job
374
+ * payload — hand it to the mail template and let it go. Every other link in this
375
+ * package is minted inside the worker that sends it precisely so that no caller
376
+ * ever holds one; this one cannot be, and the obligation moves to the app.
377
+ */
378
+ interface CreateRevokeAllLinkOptions {
379
+ /**
380
+ * Minutes the link stays valid. Defaults to
381
+ * `SPFN_AUTH_REVOKE_ALL_LINK_TTL_MINUTES`, itself 30.
382
+ */
383
+ ttlMinutes?: number;
384
+ }
385
+ interface RevokeAllLink {
386
+ /** Absolute URL of the app page, token in its query string. Never log it. */
387
+ url: string;
388
+ expiresAt: Date;
389
+ }
390
+ /** What the describe call tells the page, so it can say what pressing the button does. */
391
+ interface RevokeAllLinkDescription {
392
+ expiresAt: Date;
393
+ activeKeyCount: number;
394
+ }
395
+ /**
396
+ * Mint a sign-out-everywhere link for an account.
397
+ *
398
+ * Issuing supersedes every live link the account already has, the rule the
399
+ * password-reset flow follows: an owner who asks twice must not leave the first
400
+ * capability working in their mailbox after using the second.
401
+ *
402
+ * The row records the account's key generation, so the link dies the moment any
403
+ * other path signs every device out — a password reset, a password change, a
404
+ * deletion request, or the ordinary revoke-all route.
405
+ *
406
+ * Send it beside a password reset link. It ends the sessions; it does not change
407
+ * the password that let them start.
408
+ *
409
+ * @param userId - The account to issue for
410
+ * @throws ValidationError `ttlMinutes` that is not a positive integer — no row is written
411
+ * @throws NotFoundError no such account, rather than a foreign-key failure at the insert
412
+ */
413
+ declare function createRevokeAllLink(userId: string | number, options?: CreateRevokeAllLinkOptions): Promise<RevokeAllLink>;
414
+ /**
415
+ * Say whether a presented link still works, and what pressing the button would
416
+ * do. Changes nothing — a mail scanner that opens the page has done nothing.
417
+ *
418
+ * @throws RevokeAllLinkError every refusal, in one shape
419
+ */
420
+ declare function describeRevokeAllLink(token: string): Promise<RevokeAllLinkDescription>;
421
+ /**
422
+ * Spend a link and sign every device of the account out.
423
+ *
424
+ * The claim is one statement, so of two requests carrying the same token exactly
425
+ * one revokes and the other is refused as if the token were unknown — which by
426
+ * then it is. `includeCurrent` is true because there is no current device here:
427
+ * the caller holds a mailbox, not a session, and sparing something would mean
428
+ * sparing whatever the owner is trying to cut off.
429
+ *
430
+ * @returns how many device keys were revoked; zero is a success, not a refusal
431
+ * @throws RevokeAllLinkError every refusal, in the same shape `describe` uses
432
+ */
433
+ declare function consumeRevokeAllLink(token: string): Promise<{
434
+ revokedCount: number;
435
+ }>;
436
+ /**
437
+ * Delete link rows that can no longer answer anything.
438
+ *
439
+ * @returns number of rows deleted
440
+ */
441
+ declare function purgeRevokeAllTokensService(): Promise<{
442
+ deleted: number;
443
+ }>;
402
444
 
403
445
  /**
404
446
  * @spfn/auth - Users Entity
@@ -657,6 +699,36 @@ declare const users: drizzle_orm_pg_core.PgTableWithColumns<{
657
699
  identity: undefined;
658
700
  generated: undefined;
659
701
  }>;
702
+ sessionBinding: drizzle_orm_pg_core.PgBuildColumn<"users", drizzle_orm_pg_core.SetHasDefault<drizzle_orm_pg_core.SetNotNull<drizzle_orm_pg_core.PgTextBuilder<["none", "passkey"] & [string, ...string[]]>>>, {
703
+ name: string;
704
+ tableName: "users";
705
+ dataType: "string enum";
706
+ data: "none" | "passkey";
707
+ driverParam: string;
708
+ notNull: true;
709
+ hasDefault: true;
710
+ isPrimaryKey: false;
711
+ isAutoincrement: false;
712
+ hasRuntimeDefault: false;
713
+ enumValues: ["none", "passkey"] & [string, ...string[]];
714
+ identity: undefined;
715
+ generated: undefined;
716
+ }>;
717
+ sessionBindingChangedAt: drizzle_orm_pg_core.PgBuildColumn<"users", drizzle_orm_pg_core.PgTimestampBuilder, {
718
+ name: string;
719
+ tableName: "users";
720
+ dataType: "object date";
721
+ data: Date;
722
+ driverParam: string;
723
+ notNull: false;
724
+ hasDefault: false;
725
+ isPrimaryKey: false;
726
+ isAutoincrement: false;
727
+ hasRuntimeDefault: false;
728
+ enumValues: undefined;
729
+ identity: undefined;
730
+ generated: undefined;
731
+ }>;
660
732
  lastLoginAt: drizzle_orm_pg_core.PgBuildColumn<"users", drizzle_orm_pg_core.PgTimestampBuilder, {
661
733
  name: string;
662
734
  tableName: "users";
@@ -704,6 +776,8 @@ declare function getUserByIdService(userId: number): Promise<{
704
776
  emailVerifiedAt: Date | null;
705
777
  phoneVerifiedAt: Date | null;
706
778
  keyEpoch: number;
779
+ sessionBinding: "none" | "passkey";
780
+ sessionBindingChangedAt: Date | null;
707
781
  lastLoginAt: Date | null;
708
782
  }>;
709
783
  /**
@@ -726,6 +800,8 @@ declare function getUserByEmailService(email: string): Promise<{
726
800
  emailVerifiedAt: Date | null;
727
801
  phoneVerifiedAt: Date | null;
728
802
  keyEpoch: number;
803
+ sessionBinding: "none" | "passkey";
804
+ sessionBindingChangedAt: Date | null;
729
805
  lastLoginAt: Date | null;
730
806
  }>;
731
807
  /**
@@ -748,6 +824,8 @@ declare function getUserByPhoneService(phone: string): Promise<{
748
824
  emailVerifiedAt: Date | null;
749
825
  phoneVerifiedAt: Date | null;
750
826
  keyEpoch: number;
827
+ sessionBinding: "none" | "passkey";
828
+ sessionBindingChangedAt: Date | null;
751
829
  lastLoginAt: Date | null;
752
830
  }>;
753
831
  /**
@@ -789,6 +867,8 @@ declare function updateUsernameService(userId: string | number | bigint, usernam
789
867
  emailVerifiedAt: Date | null;
790
868
  phoneVerifiedAt: Date | null;
791
869
  keyEpoch: number;
870
+ sessionBinding: "none" | "passkey";
871
+ sessionBindingChangedAt: Date | null;
792
872
  lastLoginAt: Date | null;
793
873
  }>;
794
874
 
@@ -2002,6 +2082,14 @@ declare function verifyIdToken(params: VerifyIdTokenParams): Promise<JWTPayload>
2002
2082
  * Never include token or key material in logs or error messages.
2003
2083
  */
2004
2084
 
2085
+ /**
2086
+ * Framing prefix of the current at-rest format: `enc:v2:<keyId>:<payload>`.
2087
+ *
2088
+ * Exported because the MFA secret cipher writes the same frame over the same
2089
+ * keyring (`lib/mfa-cipher.ts`) — one format for everything this package
2090
+ * encrypts at rest, rather than a second one nobody would remember to rotate.
2091
+ */
2092
+ declare const V2_PREFIX = "enc:v2:";
2005
2093
  type OAuthTokenType = 'access' | 'refresh';
2006
2094
  interface OAuthTokenContext {
2007
2095
  provider: SocialProvider;
@@ -2017,6 +2105,21 @@ interface OAuthTokenCipher {
2017
2105
  encrypt(value: string, context: OAuthTokenContext): Promise<string>;
2018
2106
  decrypt(value: string, context: OAuthTokenContext): Promise<DecryptedOAuthToken>;
2019
2107
  }
2108
+ interface TokenEncryptionKey {
2109
+ keyId: string;
2110
+ key: Buffer;
2111
+ }
2112
+ /**
2113
+ * Parse `<keyId>:<base64-encoded 32-byte key>` entries. The first entry is active.
2114
+ *
2115
+ * Empty when the variable is unset — whether that is a configuration error is
2116
+ * the caller's to decide, and the two callers answer it differently: an OAuth
2117
+ * token refuses outright, while an MFA secret raises `MfaConfigError` so the
2118
+ * refusal cannot be mistaken for a wrong code.
2119
+ *
2120
+ * @throws Error when an entry is present but malformed
2121
+ */
2122
+ declare function getEncryptionKeyring(): TokenEncryptionKey[];
2020
2123
  declare class EnvironmentKeyringTokenCipher implements OAuthTokenCipher {
2021
2124
  encrypt(value: string, context: OAuthTokenContext): Promise<string>;
2022
2125
  decrypt(value: string, context: OAuthTokenContext): Promise<DecryptedOAuthToken>;
@@ -3115,25 +3218,20 @@ type UserProfile = typeof userProfiles.$inferSelect;
3115
3218
  type NewUserProfile = typeof userProfiles.$inferInsert;
3116
3219
 
3117
3220
  /**
3118
- * @spfn/auth - User Public Keys Entity
3221
+ * @spfn/auth - User Social Accounts Entity
3119
3222
  *
3120
- * Stores client-generated public keys for JWT verification
3121
- * Supports key rotation and multi-key management per user
3122
- */
3123
- /**
3124
- * User Public Keys Table
3125
- * Each user can have multiple public keys (for rotation)
3223
+ * Stores OAuth connections for social login providers
3126
3224
  */
3127
- declare const userPublicKeys: drizzle_orm_pg_core.PgTableWithColumns<{
3128
- name: "user_public_keys";
3225
+ declare const userSocialAccounts: drizzle_orm_pg_core.PgTableWithColumns<{
3226
+ name: "user_social_accounts";
3129
3227
  schema: string;
3130
3228
  columns: {
3131
- id: drizzle_orm_pg_core.PgBuildColumn<"user_public_keys", drizzle_orm_pg_core.SetIsPrimaryKey<drizzle_orm_pg_core.PgBigSerial53Builder>, {
3229
+ createdAt: drizzle_orm_pg_core.PgBuildColumn<"user_social_accounts", drizzle_orm_pg_core.SetNotNull<drizzle_orm_pg_core.SetHasDefault<drizzle_orm_pg_core.PgTimestampBuilder>>, {
3132
3230
  name: string;
3133
- tableName: "user_public_keys";
3134
- dataType: "number int53";
3135
- data: number;
3136
- driverParam: number;
3231
+ tableName: "user_social_accounts";
3232
+ dataType: "object date";
3233
+ data: Date;
3234
+ driverParam: string;
3137
3235
  notNull: true;
3138
3236
  hasDefault: true;
3139
3237
  isPrimaryKey: false;
@@ -3143,14 +3241,14 @@ declare const userPublicKeys: drizzle_orm_pg_core.PgTableWithColumns<{
3143
3241
  identity: undefined;
3144
3242
  generated: undefined;
3145
3243
  }>;
3146
- userId: drizzle_orm_pg_core.PgBuildColumn<"user_public_keys", drizzle_orm_pg_core.SetNotNull<drizzle_orm_pg_core.PgBigInt53Builder>, {
3244
+ updatedAt: drizzle_orm_pg_core.PgBuildColumn<"user_social_accounts", drizzle_orm_pg_core.SetHasDefault<drizzle_orm_pg_core.SetNotNull<drizzle_orm_pg_core.SetHasDefault<drizzle_orm_pg_core.PgTimestampBuilder>>>, {
3147
3245
  name: string;
3148
- tableName: "user_public_keys";
3149
- dataType: "number int53";
3150
- data: number;
3151
- driverParam: string | number;
3246
+ tableName: "user_social_accounts";
3247
+ dataType: "object date";
3248
+ data: Date;
3249
+ driverParam: string;
3152
3250
  notNull: true;
3153
- hasDefault: false;
3251
+ hasDefault: true;
3154
3252
  isPrimaryKey: false;
3155
3253
  isAutoincrement: false;
3156
3254
  hasRuntimeDefault: false;
@@ -3158,27 +3256,27 @@ declare const userPublicKeys: drizzle_orm_pg_core.PgTableWithColumns<{
3158
3256
  identity: undefined;
3159
3257
  generated: undefined;
3160
3258
  }>;
3161
- keyId: drizzle_orm_pg_core.PgBuildColumn<"user_public_keys", drizzle_orm_pg_core.SetNotNull<drizzle_orm_pg_core.PgTextBuilder<[string, ...string[]]>>, {
3259
+ id: drizzle_orm_pg_core.PgBuildColumn<"user_social_accounts", drizzle_orm_pg_core.SetIsPrimaryKey<drizzle_orm_pg_core.PgBigSerial53Builder>, {
3162
3260
  name: string;
3163
- tableName: "user_public_keys";
3164
- dataType: "string";
3165
- data: string;
3166
- driverParam: string;
3167
- notNull: true;
3168
- hasDefault: false;
3169
- isPrimaryKey: false;
3261
+ tableName: "user_social_accounts";
3262
+ dataType: "number int53";
3263
+ data: number;
3264
+ driverParam: number;
3265
+ notNull: true;
3266
+ hasDefault: true;
3267
+ isPrimaryKey: false;
3170
3268
  isAutoincrement: false;
3171
3269
  hasRuntimeDefault: false;
3172
3270
  enumValues: undefined;
3173
3271
  identity: undefined;
3174
3272
  generated: undefined;
3175
3273
  }>;
3176
- publicKey: drizzle_orm_pg_core.PgBuildColumn<"user_public_keys", drizzle_orm_pg_core.SetNotNull<drizzle_orm_pg_core.PgTextBuilder<[string, ...string[]]>>, {
3274
+ userId: drizzle_orm_pg_core.PgBuildColumn<"user_social_accounts", drizzle_orm_pg_core.SetNotNull<drizzle_orm_pg_core.PgBigInt53Builder>, {
3177
3275
  name: string;
3178
- tableName: "user_public_keys";
3179
- dataType: "string";
3180
- data: string;
3181
- driverParam: string;
3276
+ tableName: "user_social_accounts";
3277
+ dataType: "number int53";
3278
+ data: number;
3279
+ driverParam: string | number;
3182
3280
  notNull: true;
3183
3281
  hasDefault: false;
3184
3282
  isPrimaryKey: false;
@@ -3188,24 +3286,24 @@ declare const userPublicKeys: drizzle_orm_pg_core.PgTableWithColumns<{
3188
3286
  identity: undefined;
3189
3287
  generated: undefined;
3190
3288
  }>;
3191
- algorithm: drizzle_orm_pg_core.PgBuildColumn<"user_public_keys", drizzle_orm_pg_core.SetHasDefault<drizzle_orm_pg_core.SetNotNull<drizzle_orm_pg_core.PgTextBuilder<["ES256", "RS256"] & [string, ...string[]]>>>, {
3289
+ provider: drizzle_orm_pg_core.PgBuildColumn<"user_social_accounts", drizzle_orm_pg_core.SetNotNull<drizzle_orm_pg_core.PgTextBuilder<["google", "apple", "github", "kakao", "naver", "superself"] & [string, ...string[]]>>, {
3192
3290
  name: string;
3193
- tableName: "user_public_keys";
3291
+ tableName: "user_social_accounts";
3194
3292
  dataType: "string enum";
3195
- data: "ES256" | "RS256";
3293
+ data: "google" | "apple" | "github" | "kakao" | "naver" | "superself";
3196
3294
  driverParam: string;
3197
3295
  notNull: true;
3198
- hasDefault: true;
3296
+ hasDefault: false;
3199
3297
  isPrimaryKey: false;
3200
3298
  isAutoincrement: false;
3201
3299
  hasRuntimeDefault: false;
3202
- enumValues: ["ES256", "RS256"] & [string, ...string[]];
3300
+ enumValues: ["google", "apple", "github", "kakao", "naver", "superself"] & [string, ...string[]];
3203
3301
  identity: undefined;
3204
3302
  generated: undefined;
3205
3303
  }>;
3206
- fingerprint: drizzle_orm_pg_core.PgBuildColumn<"user_public_keys", drizzle_orm_pg_core.SetNotNull<drizzle_orm_pg_core.PgTextBuilder<[string, ...string[]]>>, {
3304
+ providerUserId: drizzle_orm_pg_core.PgBuildColumn<"user_social_accounts", drizzle_orm_pg_core.SetNotNull<drizzle_orm_pg_core.PgTextBuilder<[string, ...string[]]>>, {
3207
3305
  name: string;
3208
- tableName: "user_public_keys";
3306
+ tableName: "user_social_accounts";
3209
3307
  dataType: "string";
3210
3308
  data: string;
3211
3309
  driverParam: string;
@@ -3218,9 +3316,9 @@ declare const userPublicKeys: drizzle_orm_pg_core.PgTableWithColumns<{
3218
3316
  identity: undefined;
3219
3317
  generated: undefined;
3220
3318
  }>;
3221
- deviceName: drizzle_orm_pg_core.PgBuildColumn<"user_public_keys", drizzle_orm_pg_core.PgTextBuilder<[string, ...string[]]>, {
3319
+ providerEmail: drizzle_orm_pg_core.PgBuildColumn<"user_social_accounts", drizzle_orm_pg_core.PgTextBuilder<[string, ...string[]]>, {
3222
3320
  name: string;
3223
- tableName: "user_public_keys";
3321
+ tableName: "user_social_accounts";
3224
3322
  dataType: "string";
3225
3323
  data: string;
3226
3324
  driverParam: string;
@@ -3233,24 +3331,24 @@ declare const userPublicKeys: drizzle_orm_pg_core.PgTableWithColumns<{
3233
3331
  identity: undefined;
3234
3332
  generated: undefined;
3235
3333
  }>;
3236
- platform: drizzle_orm_pg_core.PgBuildColumn<"user_public_keys", drizzle_orm_pg_core.PgTextBuilder<["ios", "android", "web", "desktop"] & [string, ...string[]]>, {
3334
+ accessToken: drizzle_orm_pg_core.PgBuildColumn<"user_social_accounts", drizzle_orm_pg_core.PgTextBuilder<[string, ...string[]]>, {
3237
3335
  name: string;
3238
- tableName: "user_public_keys";
3239
- dataType: "string enum";
3240
- data: "ios" | "android" | "web" | "desktop";
3336
+ tableName: "user_social_accounts";
3337
+ dataType: "string";
3338
+ data: string;
3241
3339
  driverParam: string;
3242
3340
  notNull: false;
3243
3341
  hasDefault: false;
3244
3342
  isPrimaryKey: false;
3245
3343
  isAutoincrement: false;
3246
3344
  hasRuntimeDefault: false;
3247
- enumValues: ["ios", "android", "web", "desktop"] & [string, ...string[]];
3345
+ enumValues: undefined;
3248
3346
  identity: undefined;
3249
3347
  generated: undefined;
3250
3348
  }>;
3251
- registeredIp: drizzle_orm_pg_core.PgBuildColumn<"user_public_keys", drizzle_orm_pg_core.PgTextBuilder<[string, ...string[]]>, {
3349
+ refreshToken: drizzle_orm_pg_core.PgBuildColumn<"user_social_accounts", drizzle_orm_pg_core.PgTextBuilder<[string, ...string[]]>, {
3252
3350
  name: string;
3253
- tableName: "user_public_keys";
3351
+ tableName: "user_social_accounts";
3254
3352
  dataType: "string";
3255
3353
  data: string;
3256
3354
  driverParam: string;
@@ -3263,11 +3361,11 @@ declare const userPublicKeys: drizzle_orm_pg_core.PgTableWithColumns<{
3263
3361
  identity: undefined;
3264
3362
  generated: undefined;
3265
3363
  }>;
3266
- registeredUserAgent: drizzle_orm_pg_core.PgBuildColumn<"user_public_keys", drizzle_orm_pg_core.PgTextBuilder<[string, ...string[]]>, {
3364
+ tokenExpiresAt: drizzle_orm_pg_core.PgBuildColumn<"user_social_accounts", drizzle_orm_pg_core.PgTimestampBuilder, {
3267
3365
  name: string;
3268
- tableName: "user_public_keys";
3269
- dataType: "string";
3270
- data: string;
3366
+ tableName: "user_social_accounts";
3367
+ dataType: "object date";
3368
+ data: Date;
3271
3369
  driverParam: string;
3272
3370
  notNull: false;
3273
3371
  hasDefault: false;
@@ -3278,29 +3376,45 @@ declare const userPublicKeys: drizzle_orm_pg_core.PgTableWithColumns<{
3278
3376
  identity: undefined;
3279
3377
  generated: undefined;
3280
3378
  }>;
3281
- clientKind: drizzle_orm_pg_core.PgBuildColumn<"user_public_keys", drizzle_orm_pg_core.PgTextBuilder<["web", "ios", "android"] & [string, ...string[]]>, {
3379
+ };
3380
+ dialect: "pg";
3381
+ }>;
3382
+ type UserSocialAccount = typeof userSocialAccounts.$inferSelect;
3383
+ type NewUserSocialAccount = typeof userSocialAccounts.$inferInsert;
3384
+
3385
+ /**
3386
+ * @spfn/auth - Verification Codes Entity
3387
+ *
3388
+ * Stores verification codes for email and phone verification
3389
+ * Codes expire after a configurable time period
3390
+ */
3391
+ declare const verificationCodes: drizzle_orm_pg_core.PgTableWithColumns<{
3392
+ name: "verification_codes";
3393
+ schema: string;
3394
+ columns: {
3395
+ createdAt: drizzle_orm_pg_core.PgBuildColumn<"verification_codes", drizzle_orm_pg_core.SetNotNull<drizzle_orm_pg_core.SetHasDefault<drizzle_orm_pg_core.PgTimestampBuilder>>, {
3282
3396
  name: string;
3283
- tableName: "user_public_keys";
3284
- dataType: "string enum";
3285
- data: "ios" | "android" | "web";
3397
+ tableName: "verification_codes";
3398
+ dataType: "object date";
3399
+ data: Date;
3286
3400
  driverParam: string;
3287
- notNull: false;
3288
- hasDefault: false;
3401
+ notNull: true;
3402
+ hasDefault: true;
3289
3403
  isPrimaryKey: false;
3290
3404
  isAutoincrement: false;
3291
3405
  hasRuntimeDefault: false;
3292
- enumValues: ["web", "ios", "android"] & [string, ...string[]];
3406
+ enumValues: undefined;
3293
3407
  identity: undefined;
3294
3408
  generated: undefined;
3295
3409
  }>;
3296
- clientVersion: drizzle_orm_pg_core.PgBuildColumn<"user_public_keys", drizzle_orm_pg_core.PgTextBuilder<[string, ...string[]]>, {
3410
+ updatedAt: drizzle_orm_pg_core.PgBuildColumn<"verification_codes", drizzle_orm_pg_core.SetHasDefault<drizzle_orm_pg_core.SetNotNull<drizzle_orm_pg_core.SetHasDefault<drizzle_orm_pg_core.PgTimestampBuilder>>>, {
3297
3411
  name: string;
3298
- tableName: "user_public_keys";
3299
- dataType: "string";
3300
- data: string;
3412
+ tableName: "verification_codes";
3413
+ dataType: "object date";
3414
+ data: Date;
3301
3415
  driverParam: string;
3302
- notNull: false;
3303
- hasDefault: false;
3416
+ notNull: true;
3417
+ hasDefault: true;
3304
3418
  isPrimaryKey: false;
3305
3419
  isAutoincrement: false;
3306
3420
  hasRuntimeDefault: false;
@@ -3308,14 +3422,14 @@ declare const userPublicKeys: drizzle_orm_pg_core.PgTableWithColumns<{
3308
3422
  identity: undefined;
3309
3423
  generated: undefined;
3310
3424
  }>;
3311
- clientContractVersion: drizzle_orm_pg_core.PgBuildColumn<"user_public_keys", drizzle_orm_pg_core.PgTextBuilder<[string, ...string[]]>, {
3425
+ id: drizzle_orm_pg_core.PgBuildColumn<"verification_codes", drizzle_orm_pg_core.SetIsPrimaryKey<drizzle_orm_pg_core.PgBigSerial53Builder>, {
3312
3426
  name: string;
3313
- tableName: "user_public_keys";
3314
- dataType: "string";
3315
- data: string;
3316
- driverParam: string;
3317
- notNull: false;
3318
- hasDefault: false;
3427
+ tableName: "verification_codes";
3428
+ dataType: "number int53";
3429
+ data: number;
3430
+ driverParam: number;
3431
+ notNull: true;
3432
+ hasDefault: true;
3319
3433
  isPrimaryKey: false;
3320
3434
  isAutoincrement: false;
3321
3435
  hasRuntimeDefault: false;
@@ -3323,13 +3437,13 @@ declare const userPublicKeys: drizzle_orm_pg_core.PgTableWithColumns<{
3323
3437
  identity: undefined;
3324
3438
  generated: undefined;
3325
3439
  }>;
3326
- clientSeenAt: drizzle_orm_pg_core.PgBuildColumn<"user_public_keys", drizzle_orm_pg_core.PgTimestampBuilder, {
3440
+ target: drizzle_orm_pg_core.PgBuildColumn<"verification_codes", drizzle_orm_pg_core.SetNotNull<drizzle_orm_pg_core.PgTextBuilder<[string, ...string[]]>>, {
3327
3441
  name: string;
3328
- tableName: "user_public_keys";
3329
- dataType: "object date";
3330
- data: Date;
3442
+ tableName: "verification_codes";
3443
+ dataType: "string";
3444
+ data: string;
3331
3445
  driverParam: string;
3332
- notNull: false;
3446
+ notNull: true;
3333
3447
  hasDefault: false;
3334
3448
  isPrimaryKey: false;
3335
3449
  isAutoincrement: false;
@@ -3338,29 +3452,29 @@ declare const userPublicKeys: drizzle_orm_pg_core.PgTableWithColumns<{
3338
3452
  identity: undefined;
3339
3453
  generated: undefined;
3340
3454
  }>;
3341
- isActive: drizzle_orm_pg_core.PgBuildColumn<"user_public_keys", drizzle_orm_pg_core.SetHasDefault<drizzle_orm_pg_core.SetNotNull<drizzle_orm_pg_core.PgBooleanBuilder>>, {
3455
+ targetType: drizzle_orm_pg_core.PgBuildColumn<"verification_codes", drizzle_orm_pg_core.SetNotNull<drizzle_orm_pg_core.PgTextBuilder<["email", "phone"] & [string, ...string[]]>>, {
3342
3456
  name: string;
3343
- tableName: "user_public_keys";
3344
- dataType: "boolean";
3345
- data: boolean;
3346
- driverParam: boolean;
3457
+ tableName: "verification_codes";
3458
+ dataType: "string enum";
3459
+ data: "email" | "phone";
3460
+ driverParam: string;
3347
3461
  notNull: true;
3348
- hasDefault: true;
3462
+ hasDefault: false;
3349
3463
  isPrimaryKey: false;
3350
3464
  isAutoincrement: false;
3351
3465
  hasRuntimeDefault: false;
3352
- enumValues: undefined;
3466
+ enumValues: ["email", "phone"] & [string, ...string[]];
3353
3467
  identity: undefined;
3354
3468
  generated: undefined;
3355
3469
  }>;
3356
- createdAt: drizzle_orm_pg_core.PgBuildColumn<"user_public_keys", drizzle_orm_pg_core.SetHasDefault<drizzle_orm_pg_core.SetNotNull<drizzle_orm_pg_core.PgTimestampBuilder>>, {
3470
+ code: drizzle_orm_pg_core.PgBuildColumn<"verification_codes", drizzle_orm_pg_core.SetNotNull<drizzle_orm_pg_core.PgTextBuilder<[string, ...string[]]>>, {
3357
3471
  name: string;
3358
- tableName: "user_public_keys";
3359
- dataType: "object date";
3360
- data: Date;
3472
+ tableName: "verification_codes";
3473
+ dataType: "string";
3474
+ data: string;
3361
3475
  driverParam: string;
3362
3476
  notNull: true;
3363
- hasDefault: true;
3477
+ hasDefault: false;
3364
3478
  isPrimaryKey: false;
3365
3479
  isAutoincrement: false;
3366
3480
  hasRuntimeDefault: false;
@@ -3368,28 +3482,28 @@ declare const userPublicKeys: drizzle_orm_pg_core.PgTableWithColumns<{
3368
3482
  identity: undefined;
3369
3483
  generated: undefined;
3370
3484
  }>;
3371
- lastUsedAt: drizzle_orm_pg_core.PgBuildColumn<"user_public_keys", drizzle_orm_pg_core.PgTimestampBuilder, {
3485
+ purpose: drizzle_orm_pg_core.PgBuildColumn<"verification_codes", drizzle_orm_pg_core.SetNotNull<drizzle_orm_pg_core.PgTextBuilder<["registration", "login", "password_reset", "email_change", "phone_change", "account_deletion"] & [string, ...string[]]>>, {
3372
3486
  name: string;
3373
- tableName: "user_public_keys";
3374
- dataType: "object date";
3375
- data: Date;
3487
+ tableName: "verification_codes";
3488
+ dataType: "string enum";
3489
+ data: "registration" | "login" | "password_reset" | "email_change" | "phone_change" | "account_deletion";
3376
3490
  driverParam: string;
3377
- notNull: false;
3491
+ notNull: true;
3378
3492
  hasDefault: false;
3379
3493
  isPrimaryKey: false;
3380
3494
  isAutoincrement: false;
3381
3495
  hasRuntimeDefault: false;
3382
- enumValues: undefined;
3496
+ enumValues: ["registration", "login", "password_reset", "email_change", "phone_change", "account_deletion"] & [string, ...string[]];
3383
3497
  identity: undefined;
3384
3498
  generated: undefined;
3385
3499
  }>;
3386
- expiresAt: drizzle_orm_pg_core.PgBuildColumn<"user_public_keys", drizzle_orm_pg_core.PgTimestampBuilder, {
3500
+ expiresAt: drizzle_orm_pg_core.PgBuildColumn<"verification_codes", drizzle_orm_pg_core.SetNotNull<drizzle_orm_pg_core.PgTimestampBuilder>, {
3387
3501
  name: string;
3388
- tableName: "user_public_keys";
3502
+ tableName: "verification_codes";
3389
3503
  dataType: "object date";
3390
3504
  data: Date;
3391
3505
  driverParam: string;
3392
- notNull: false;
3506
+ notNull: true;
3393
3507
  hasDefault: false;
3394
3508
  isPrimaryKey: false;
3395
3509
  isAutoincrement: false;
@@ -3398,9 +3512,9 @@ declare const userPublicKeys: drizzle_orm_pg_core.PgTableWithColumns<{
3398
3512
  identity: undefined;
3399
3513
  generated: undefined;
3400
3514
  }>;
3401
- revokedAt: drizzle_orm_pg_core.PgBuildColumn<"user_public_keys", drizzle_orm_pg_core.PgTimestampBuilder, {
3515
+ usedAt: drizzle_orm_pg_core.PgBuildColumn<"verification_codes", drizzle_orm_pg_core.PgTimestampBuilder, {
3402
3516
  name: string;
3403
- tableName: "user_public_keys";
3517
+ tableName: "verification_codes";
3404
3518
  dataType: "object date";
3405
3519
  data: Date;
3406
3520
  driverParam: string;
@@ -3413,14 +3527,14 @@ declare const userPublicKeys: drizzle_orm_pg_core.PgTableWithColumns<{
3413
3527
  identity: undefined;
3414
3528
  generated: undefined;
3415
3529
  }>;
3416
- revokedReason: drizzle_orm_pg_core.PgBuildColumn<"user_public_keys", drizzle_orm_pg_core.PgTextBuilder<[string, ...string[]]>, {
3530
+ attempts: drizzle_orm_pg_core.PgBuildColumn<"verification_codes", drizzle_orm_pg_core.SetHasDefault<drizzle_orm_pg_core.SetNotNull<drizzle_orm_pg_core.PgIntegerBuilder>>, {
3417
3531
  name: string;
3418
- tableName: "user_public_keys";
3419
- dataType: "string";
3420
- data: string;
3421
- driverParam: string;
3422
- notNull: false;
3423
- hasDefault: false;
3532
+ tableName: "verification_codes";
3533
+ dataType: "number int32";
3534
+ data: number;
3535
+ driverParam: string | number;
3536
+ notNull: true;
3537
+ hasDefault: true;
3424
3538
  isPrimaryKey: false;
3425
3539
  isAutoincrement: false;
3426
3540
  hasRuntimeDefault: false;
@@ -3431,21 +3545,29 @@ declare const userPublicKeys: drizzle_orm_pg_core.PgTableWithColumns<{
3431
3545
  };
3432
3546
  dialect: "pg";
3433
3547
  }>;
3434
- type UserPublicKey = typeof userPublicKeys.$inferSelect;
3435
- type NewUserPublicKey = typeof userPublicKeys.$inferInsert;
3548
+ type VerificationCode = typeof verificationCodes.$inferSelect;
3549
+ type NewVerificationCode = typeof verificationCodes.$inferInsert;
3436
3550
 
3437
3551
  /**
3438
- * @spfn/auth - User Social Accounts Entity
3552
+ * @spfn/auth - Signup Link Tokens Entity
3439
3553
  *
3440
- * Stores OAuth connections for social login providers
3554
+ * Backs the verified-email signup flow: a one-time confirmation link is emailed,
3555
+ * opening it mints a short-lived setup session, and setting a password on that
3556
+ * session creates the account.
3557
+ *
3558
+ * A separate table from `verification_codes` on purpose. That table stores its
3559
+ * six-digit code in plaintext, which is acceptable for a short-lived numeric code
3560
+ * bounded by an attempt counter, but a link token is a bearer credential sitting
3561
+ * in someone's mailbox and must only ever be stored as a hash. Sharing one table
3562
+ * would make "plaintext or hash" a per-row property that nothing enforces.
3441
3563
  */
3442
- declare const userSocialAccounts: drizzle_orm_pg_core.PgTableWithColumns<{
3443
- name: "user_social_accounts";
3564
+ declare const signupLinkTokens: drizzle_orm_pg_core.PgTableWithColumns<{
3565
+ name: "signup_link_tokens";
3444
3566
  schema: string;
3445
3567
  columns: {
3446
- createdAt: drizzle_orm_pg_core.PgBuildColumn<"user_social_accounts", drizzle_orm_pg_core.SetNotNull<drizzle_orm_pg_core.SetHasDefault<drizzle_orm_pg_core.PgTimestampBuilder>>, {
3568
+ createdAt: drizzle_orm_pg_core.PgBuildColumn<"signup_link_tokens", drizzle_orm_pg_core.SetNotNull<drizzle_orm_pg_core.SetHasDefault<drizzle_orm_pg_core.PgTimestampBuilder>>, {
3447
3569
  name: string;
3448
- tableName: "user_social_accounts";
3570
+ tableName: "signup_link_tokens";
3449
3571
  dataType: "object date";
3450
3572
  data: Date;
3451
3573
  driverParam: string;
@@ -3458,9 +3580,9 @@ declare const userSocialAccounts: drizzle_orm_pg_core.PgTableWithColumns<{
3458
3580
  identity: undefined;
3459
3581
  generated: undefined;
3460
3582
  }>;
3461
- updatedAt: drizzle_orm_pg_core.PgBuildColumn<"user_social_accounts", drizzle_orm_pg_core.SetHasDefault<drizzle_orm_pg_core.SetNotNull<drizzle_orm_pg_core.SetHasDefault<drizzle_orm_pg_core.PgTimestampBuilder>>>, {
3583
+ updatedAt: drizzle_orm_pg_core.PgBuildColumn<"signup_link_tokens", drizzle_orm_pg_core.SetHasDefault<drizzle_orm_pg_core.SetNotNull<drizzle_orm_pg_core.SetHasDefault<drizzle_orm_pg_core.PgTimestampBuilder>>>, {
3462
3584
  name: string;
3463
- tableName: "user_social_accounts";
3585
+ tableName: "signup_link_tokens";
3464
3586
  dataType: "object date";
3465
3587
  data: Date;
3466
3588
  driverParam: string;
@@ -3473,9 +3595,9 @@ declare const userSocialAccounts: drizzle_orm_pg_core.PgTableWithColumns<{
3473
3595
  identity: undefined;
3474
3596
  generated: undefined;
3475
3597
  }>;
3476
- id: drizzle_orm_pg_core.PgBuildColumn<"user_social_accounts", drizzle_orm_pg_core.SetIsPrimaryKey<drizzle_orm_pg_core.PgBigSerial53Builder>, {
3598
+ id: drizzle_orm_pg_core.PgBuildColumn<"signup_link_tokens", drizzle_orm_pg_core.SetIsPrimaryKey<drizzle_orm_pg_core.PgBigSerial53Builder>, {
3477
3599
  name: string;
3478
- tableName: "user_social_accounts";
3600
+ tableName: "signup_link_tokens";
3479
3601
  dataType: "number int53";
3480
3602
  data: number;
3481
3603
  driverParam: number;
@@ -3488,39 +3610,9 @@ declare const userSocialAccounts: drizzle_orm_pg_core.PgTableWithColumns<{
3488
3610
  identity: undefined;
3489
3611
  generated: undefined;
3490
3612
  }>;
3491
- userId: drizzle_orm_pg_core.PgBuildColumn<"user_social_accounts", drizzle_orm_pg_core.SetNotNull<drizzle_orm_pg_core.PgBigInt53Builder>, {
3492
- name: string;
3493
- tableName: "user_social_accounts";
3494
- dataType: "number int53";
3495
- data: number;
3496
- driverParam: string | number;
3497
- notNull: true;
3498
- hasDefault: false;
3499
- isPrimaryKey: false;
3500
- isAutoincrement: false;
3501
- hasRuntimeDefault: false;
3502
- enumValues: undefined;
3503
- identity: undefined;
3504
- generated: undefined;
3505
- }>;
3506
- provider: drizzle_orm_pg_core.PgBuildColumn<"user_social_accounts", drizzle_orm_pg_core.SetNotNull<drizzle_orm_pg_core.PgTextBuilder<["google", "apple", "github", "kakao", "naver", "superself"] & [string, ...string[]]>>, {
3507
- name: string;
3508
- tableName: "user_social_accounts";
3509
- dataType: "string enum";
3510
- data: "google" | "apple" | "github" | "kakao" | "naver" | "superself";
3511
- driverParam: string;
3512
- notNull: true;
3513
- hasDefault: false;
3514
- isPrimaryKey: false;
3515
- isAutoincrement: false;
3516
- hasRuntimeDefault: false;
3517
- enumValues: ["google", "apple", "github", "kakao", "naver", "superself"] & [string, ...string[]];
3518
- identity: undefined;
3519
- generated: undefined;
3520
- }>;
3521
- providerUserId: drizzle_orm_pg_core.PgBuildColumn<"user_social_accounts", drizzle_orm_pg_core.SetNotNull<drizzle_orm_pg_core.PgTextBuilder<[string, ...string[]]>>, {
3613
+ email: drizzle_orm_pg_core.PgBuildColumn<"signup_link_tokens", drizzle_orm_pg_core.SetNotNull<drizzle_orm_pg_core.PgTextBuilder<[string, ...string[]]>>, {
3522
3614
  name: string;
3523
- tableName: "user_social_accounts";
3615
+ tableName: "signup_link_tokens";
3524
3616
  dataType: "string";
3525
3617
  data: string;
3526
3618
  driverParam: string;
@@ -3533,24 +3625,9 @@ declare const userSocialAccounts: drizzle_orm_pg_core.PgTableWithColumns<{
3533
3625
  identity: undefined;
3534
3626
  generated: undefined;
3535
3627
  }>;
3536
- providerEmail: drizzle_orm_pg_core.PgBuildColumn<"user_social_accounts", drizzle_orm_pg_core.PgTextBuilder<[string, ...string[]]>, {
3537
- name: string;
3538
- tableName: "user_social_accounts";
3539
- dataType: "string";
3540
- data: string;
3541
- driverParam: string;
3542
- notNull: false;
3543
- hasDefault: false;
3544
- isPrimaryKey: false;
3545
- isAutoincrement: false;
3546
- hasRuntimeDefault: false;
3547
- enumValues: undefined;
3548
- identity: undefined;
3549
- generated: undefined;
3550
- }>;
3551
- accessToken: drizzle_orm_pg_core.PgBuildColumn<"user_social_accounts", drizzle_orm_pg_core.PgTextBuilder<[string, ...string[]]>, {
3628
+ tokenHash: drizzle_orm_pg_core.PgBuildColumn<"signup_link_tokens", drizzle_orm_pg_core.PgTextBuilder<[string, ...string[]]>, {
3552
3629
  name: string;
3553
- tableName: "user_social_accounts";
3630
+ tableName: "signup_link_tokens";
3554
3631
  dataType: "string";
3555
3632
  data: string;
3556
3633
  driverParam: string;
@@ -3563,9 +3640,9 @@ declare const userSocialAccounts: drizzle_orm_pg_core.PgTableWithColumns<{
3563
3640
  identity: undefined;
3564
3641
  generated: undefined;
3565
3642
  }>;
3566
- refreshToken: drizzle_orm_pg_core.PgBuildColumn<"user_social_accounts", drizzle_orm_pg_core.PgTextBuilder<[string, ...string[]]>, {
3643
+ returnPath: drizzle_orm_pg_core.PgBuildColumn<"signup_link_tokens", drizzle_orm_pg_core.PgTextBuilder<[string, ...string[]]>, {
3567
3644
  name: string;
3568
- tableName: "user_social_accounts";
3645
+ tableName: "signup_link_tokens";
3569
3646
  dataType: "string";
3570
3647
  data: string;
3571
3648
  driverParam: string;
@@ -3578,13 +3655,13 @@ declare const userSocialAccounts: drizzle_orm_pg_core.PgTableWithColumns<{
3578
3655
  identity: undefined;
3579
3656
  generated: undefined;
3580
3657
  }>;
3581
- tokenExpiresAt: drizzle_orm_pg_core.PgBuildColumn<"user_social_accounts", drizzle_orm_pg_core.PgTimestampBuilder, {
3658
+ expiresAt: drizzle_orm_pg_core.PgBuildColumn<"signup_link_tokens", drizzle_orm_pg_core.SetNotNull<drizzle_orm_pg_core.PgTimestampBuilder>, {
3582
3659
  name: string;
3583
- tableName: "user_social_accounts";
3660
+ tableName: "signup_link_tokens";
3584
3661
  dataType: "object date";
3585
3662
  data: Date;
3586
3663
  driverParam: string;
3587
- notNull: false;
3664
+ notNull: true;
3588
3665
  hasDefault: false;
3589
3666
  isPrimaryKey: false;
3590
3667
  isAutoincrement: false;
@@ -3593,30 +3670,14 @@ declare const userSocialAccounts: drizzle_orm_pg_core.PgTableWithColumns<{
3593
3670
  identity: undefined;
3594
3671
  generated: undefined;
3595
3672
  }>;
3596
- };
3597
- dialect: "pg";
3598
- }>;
3599
- type UserSocialAccount = typeof userSocialAccounts.$inferSelect;
3600
- type NewUserSocialAccount = typeof userSocialAccounts.$inferInsert;
3601
-
3602
- /**
3603
- * @spfn/auth - Verification Codes Entity
3604
- *
3605
- * Stores verification codes for email and phone verification
3606
- * Codes expire after a configurable time period
3607
- */
3608
- declare const verificationCodes: drizzle_orm_pg_core.PgTableWithColumns<{
3609
- name: "verification_codes";
3610
- schema: string;
3611
- columns: {
3612
- createdAt: drizzle_orm_pg_core.PgBuildColumn<"verification_codes", drizzle_orm_pg_core.SetNotNull<drizzle_orm_pg_core.SetHasDefault<drizzle_orm_pg_core.PgTimestampBuilder>>, {
3673
+ consumedAt: drizzle_orm_pg_core.PgBuildColumn<"signup_link_tokens", drizzle_orm_pg_core.PgTimestampBuilder, {
3613
3674
  name: string;
3614
- tableName: "verification_codes";
3675
+ tableName: "signup_link_tokens";
3615
3676
  dataType: "object date";
3616
3677
  data: Date;
3617
3678
  driverParam: string;
3618
- notNull: true;
3619
- hasDefault: true;
3679
+ notNull: false;
3680
+ hasDefault: false;
3620
3681
  isPrimaryKey: false;
3621
3682
  isAutoincrement: false;
3622
3683
  hasRuntimeDefault: false;
@@ -3624,43 +3685,13 @@ declare const verificationCodes: drizzle_orm_pg_core.PgTableWithColumns<{
3624
3685
  identity: undefined;
3625
3686
  generated: undefined;
3626
3687
  }>;
3627
- updatedAt: drizzle_orm_pg_core.PgBuildColumn<"verification_codes", drizzle_orm_pg_core.SetHasDefault<drizzle_orm_pg_core.SetNotNull<drizzle_orm_pg_core.SetHasDefault<drizzle_orm_pg_core.PgTimestampBuilder>>>, {
3688
+ supersededAt: drizzle_orm_pg_core.PgBuildColumn<"signup_link_tokens", drizzle_orm_pg_core.PgTimestampBuilder, {
3628
3689
  name: string;
3629
- tableName: "verification_codes";
3690
+ tableName: "signup_link_tokens";
3630
3691
  dataType: "object date";
3631
3692
  data: Date;
3632
3693
  driverParam: string;
3633
- notNull: true;
3634
- hasDefault: true;
3635
- isPrimaryKey: false;
3636
- isAutoincrement: false;
3637
- hasRuntimeDefault: false;
3638
- enumValues: undefined;
3639
- identity: undefined;
3640
- generated: undefined;
3641
- }>;
3642
- id: drizzle_orm_pg_core.PgBuildColumn<"verification_codes", drizzle_orm_pg_core.SetIsPrimaryKey<drizzle_orm_pg_core.PgBigSerial53Builder>, {
3643
- name: string;
3644
- tableName: "verification_codes";
3645
- dataType: "number int53";
3646
- data: number;
3647
- driverParam: number;
3648
- notNull: true;
3649
- hasDefault: true;
3650
- isPrimaryKey: false;
3651
- isAutoincrement: false;
3652
- hasRuntimeDefault: false;
3653
- enumValues: undefined;
3654
- identity: undefined;
3655
- generated: undefined;
3656
- }>;
3657
- target: drizzle_orm_pg_core.PgBuildColumn<"verification_codes", drizzle_orm_pg_core.SetNotNull<drizzle_orm_pg_core.PgTextBuilder<[string, ...string[]]>>, {
3658
- name: string;
3659
- tableName: "verification_codes";
3660
- dataType: "string";
3661
- data: string;
3662
- driverParam: string;
3663
- notNull: true;
3694
+ notNull: false;
3664
3695
  hasDefault: false;
3665
3696
  isPrimaryKey: false;
3666
3697
  isAutoincrement: false;
@@ -3669,28 +3700,13 @@ declare const verificationCodes: drizzle_orm_pg_core.PgTableWithColumns<{
3669
3700
  identity: undefined;
3670
3701
  generated: undefined;
3671
3702
  }>;
3672
- targetType: drizzle_orm_pg_core.PgBuildColumn<"verification_codes", drizzle_orm_pg_core.SetNotNull<drizzle_orm_pg_core.PgTextBuilder<["email", "phone"] & [string, ...string[]]>>, {
3673
- name: string;
3674
- tableName: "verification_codes";
3675
- dataType: "string enum";
3676
- data: "email" | "phone";
3677
- driverParam: string;
3678
- notNull: true;
3679
- hasDefault: false;
3680
- isPrimaryKey: false;
3681
- isAutoincrement: false;
3682
- hasRuntimeDefault: false;
3683
- enumValues: ["email", "phone"] & [string, ...string[]];
3684
- identity: undefined;
3685
- generated: undefined;
3686
- }>;
3687
- code: drizzle_orm_pg_core.PgBuildColumn<"verification_codes", drizzle_orm_pg_core.SetNotNull<drizzle_orm_pg_core.PgTextBuilder<[string, ...string[]]>>, {
3703
+ setupSecretHash: drizzle_orm_pg_core.PgBuildColumn<"signup_link_tokens", drizzle_orm_pg_core.PgTextBuilder<[string, ...string[]]>, {
3688
3704
  name: string;
3689
- tableName: "verification_codes";
3705
+ tableName: "signup_link_tokens";
3690
3706
  dataType: "string";
3691
3707
  data: string;
3692
3708
  driverParam: string;
3693
- notNull: true;
3709
+ notNull: false;
3694
3710
  hasDefault: false;
3695
3711
  isPrimaryKey: false;
3696
3712
  isAutoincrement: false;
@@ -3699,28 +3715,13 @@ declare const verificationCodes: drizzle_orm_pg_core.PgTableWithColumns<{
3699
3715
  identity: undefined;
3700
3716
  generated: undefined;
3701
3717
  }>;
3702
- purpose: drizzle_orm_pg_core.PgBuildColumn<"verification_codes", drizzle_orm_pg_core.SetNotNull<drizzle_orm_pg_core.PgTextBuilder<["registration", "login", "password_reset", "email_change", "phone_change", "account_deletion"] & [string, ...string[]]>>, {
3703
- name: string;
3704
- tableName: "verification_codes";
3705
- dataType: "string enum";
3706
- data: "registration" | "login" | "password_reset" | "email_change" | "phone_change" | "account_deletion";
3707
- driverParam: string;
3708
- notNull: true;
3709
- hasDefault: false;
3710
- isPrimaryKey: false;
3711
- isAutoincrement: false;
3712
- hasRuntimeDefault: false;
3713
- enumValues: ["registration", "login", "password_reset", "email_change", "phone_change", "account_deletion"] & [string, ...string[]];
3714
- identity: undefined;
3715
- generated: undefined;
3716
- }>;
3717
- expiresAt: drizzle_orm_pg_core.PgBuildColumn<"verification_codes", drizzle_orm_pg_core.SetNotNull<drizzle_orm_pg_core.PgTimestampBuilder>, {
3718
+ setupExpiresAt: drizzle_orm_pg_core.PgBuildColumn<"signup_link_tokens", drizzle_orm_pg_core.PgTimestampBuilder, {
3718
3719
  name: string;
3719
- tableName: "verification_codes";
3720
+ tableName: "signup_link_tokens";
3720
3721
  dataType: "object date";
3721
3722
  data: Date;
3722
3723
  driverParam: string;
3723
- notNull: true;
3724
+ notNull: false;
3724
3725
  hasDefault: false;
3725
3726
  isPrimaryKey: false;
3726
3727
  isAutoincrement: false;
@@ -3729,9 +3730,9 @@ declare const verificationCodes: drizzle_orm_pg_core.PgTableWithColumns<{
3729
3730
  identity: undefined;
3730
3731
  generated: undefined;
3731
3732
  }>;
3732
- usedAt: drizzle_orm_pg_core.PgBuildColumn<"verification_codes", drizzle_orm_pg_core.PgTimestampBuilder, {
3733
+ completedAt: drizzle_orm_pg_core.PgBuildColumn<"signup_link_tokens", drizzle_orm_pg_core.PgTimestampBuilder, {
3733
3734
  name: string;
3734
- tableName: "verification_codes";
3735
+ tableName: "signup_link_tokens";
3735
3736
  dataType: "object date";
3736
3737
  data: Date;
3737
3738
  driverParam: string;
@@ -3744,47 +3745,53 @@ declare const verificationCodes: drizzle_orm_pg_core.PgTableWithColumns<{
3744
3745
  identity: undefined;
3745
3746
  generated: undefined;
3746
3747
  }>;
3747
- attempts: drizzle_orm_pg_core.PgBuildColumn<"verification_codes", drizzle_orm_pg_core.SetHasDefault<drizzle_orm_pg_core.SetNotNull<drizzle_orm_pg_core.PgIntegerBuilder>>, {
3748
- name: string;
3749
- tableName: "verification_codes";
3750
- dataType: "number int32";
3751
- data: number;
3752
- driverParam: string | number;
3753
- notNull: true;
3754
- hasDefault: true;
3755
- isPrimaryKey: false;
3756
- isAutoincrement: false;
3757
- hasRuntimeDefault: false;
3758
- enumValues: undefined;
3759
- identity: undefined;
3760
- generated: undefined;
3761
- }>;
3762
3748
  };
3763
3749
  dialect: "pg";
3764
3750
  }>;
3765
- type VerificationCode = typeof verificationCodes.$inferSelect;
3766
- type NewVerificationCode = typeof verificationCodes.$inferInsert;
3751
+ type SignupLinkToken = typeof signupLinkTokens.$inferSelect;
3752
+ type NewSignupLinkToken = typeof signupLinkTokens.$inferInsert;
3767
3753
 
3768
3754
  /**
3769
- * @spfn/auth - Signup Link Tokens Entity
3755
+ * @spfn/auth - Password Reset Tokens Entity
3770
3756
  *
3771
- * Backs the verified-email signup flow: a one-time confirmation link is emailed,
3772
- * opening it mints a short-lived setup session, and setting a password on that
3773
- * session creates the account.
3757
+ * Backs the password reset flow: a one-time link is emailed to an address the
3758
+ * account has already proved, opening it mints a short-lived setup session, and
3759
+ * setting a password on that session replaces the old one and signs the browser
3760
+ * in.
3774
3761
  *
3775
- * A separate table from `verification_codes` on purpose. That table stores its
3776
- * six-digit code in plaintext, which is acceptable for a short-lived numeric code
3777
- * bounded by an attempt counter, but a link token is a bearer credential sitting
3778
- * in someone's mailbox and must only ever be stored as a hash. Sharing one table
3779
- * would make "plaintext or hash" a per-row property that nothing enforces.
3762
+ * A separate table from `signup_link_tokens` even though the two carry the same
3763
+ * columns. They are the same shape, not the same thing: one names an address
3764
+ * that has no account and the other names a user that does, and the setup
3765
+ * secrets they mint must not be interchangeable. Sharing one table would make
3766
+ * "which flow is this row" a per-row property that the lookup would have to
3767
+ * remember to filter on — and a lookup that forgets would accept a signup secret
3768
+ * as a reset secret.
3769
+ *
3770
+ * The link token and the setup secret are bearer credentials, so only their
3771
+ * SHA-256 hashes are stored.
3780
3772
  */
3781
- declare const signupLinkTokens: drizzle_orm_pg_core.PgTableWithColumns<{
3782
- name: "signup_link_tokens";
3773
+ declare const passwordResetTokens: drizzle_orm_pg_core.PgTableWithColumns<{
3774
+ name: "password_reset_tokens";
3783
3775
  schema: string;
3784
3776
  columns: {
3785
- createdAt: drizzle_orm_pg_core.PgBuildColumn<"signup_link_tokens", drizzle_orm_pg_core.SetNotNull<drizzle_orm_pg_core.SetHasDefault<drizzle_orm_pg_core.PgTimestampBuilder>>, {
3777
+ createdAt: drizzle_orm_pg_core.PgBuildColumn<"password_reset_tokens", drizzle_orm_pg_core.SetNotNull<drizzle_orm_pg_core.SetHasDefault<drizzle_orm_pg_core.PgTimestampBuilder>>, {
3778
+ name: string;
3779
+ tableName: "password_reset_tokens";
3780
+ dataType: "object date";
3781
+ data: Date;
3782
+ driverParam: string;
3783
+ notNull: true;
3784
+ hasDefault: true;
3785
+ isPrimaryKey: false;
3786
+ isAutoincrement: false;
3787
+ hasRuntimeDefault: false;
3788
+ enumValues: undefined;
3789
+ identity: undefined;
3790
+ generated: undefined;
3791
+ }>;
3792
+ updatedAt: drizzle_orm_pg_core.PgBuildColumn<"password_reset_tokens", drizzle_orm_pg_core.SetHasDefault<drizzle_orm_pg_core.SetNotNull<drizzle_orm_pg_core.SetHasDefault<drizzle_orm_pg_core.PgTimestampBuilder>>>, {
3786
3793
  name: string;
3787
- tableName: "signup_link_tokens";
3794
+ tableName: "password_reset_tokens";
3788
3795
  dataType: "object date";
3789
3796
  data: Date;
3790
3797
  driverParam: string;
@@ -3797,12 +3804,12 @@ declare const signupLinkTokens: drizzle_orm_pg_core.PgTableWithColumns<{
3797
3804
  identity: undefined;
3798
3805
  generated: undefined;
3799
3806
  }>;
3800
- updatedAt: drizzle_orm_pg_core.PgBuildColumn<"signup_link_tokens", drizzle_orm_pg_core.SetHasDefault<drizzle_orm_pg_core.SetNotNull<drizzle_orm_pg_core.SetHasDefault<drizzle_orm_pg_core.PgTimestampBuilder>>>, {
3807
+ id: drizzle_orm_pg_core.PgBuildColumn<"password_reset_tokens", drizzle_orm_pg_core.SetIsPrimaryKey<drizzle_orm_pg_core.PgBigSerial53Builder>, {
3801
3808
  name: string;
3802
- tableName: "signup_link_tokens";
3803
- dataType: "object date";
3804
- data: Date;
3805
- driverParam: string;
3809
+ tableName: "password_reset_tokens";
3810
+ dataType: "number int53";
3811
+ data: number;
3812
+ driverParam: number;
3806
3813
  notNull: true;
3807
3814
  hasDefault: true;
3808
3815
  isPrimaryKey: false;
@@ -3812,14 +3819,14 @@ declare const signupLinkTokens: drizzle_orm_pg_core.PgTableWithColumns<{
3812
3819
  identity: undefined;
3813
3820
  generated: undefined;
3814
3821
  }>;
3815
- id: drizzle_orm_pg_core.PgBuildColumn<"signup_link_tokens", drizzle_orm_pg_core.SetIsPrimaryKey<drizzle_orm_pg_core.PgBigSerial53Builder>, {
3822
+ userId: drizzle_orm_pg_core.PgBuildColumn<"password_reset_tokens", drizzle_orm_pg_core.SetNotNull<drizzle_orm_pg_core.PgBigInt53Builder>, {
3816
3823
  name: string;
3817
- tableName: "signup_link_tokens";
3824
+ tableName: "password_reset_tokens";
3818
3825
  dataType: "number int53";
3819
3826
  data: number;
3820
- driverParam: number;
3827
+ driverParam: string | number;
3821
3828
  notNull: true;
3822
- hasDefault: true;
3829
+ hasDefault: false;
3823
3830
  isPrimaryKey: false;
3824
3831
  isAutoincrement: false;
3825
3832
  hasRuntimeDefault: false;
@@ -3827,9 +3834,9 @@ declare const signupLinkTokens: drizzle_orm_pg_core.PgTableWithColumns<{
3827
3834
  identity: undefined;
3828
3835
  generated: undefined;
3829
3836
  }>;
3830
- email: drizzle_orm_pg_core.PgBuildColumn<"signup_link_tokens", drizzle_orm_pg_core.SetNotNull<drizzle_orm_pg_core.PgTextBuilder<[string, ...string[]]>>, {
3837
+ email: drizzle_orm_pg_core.PgBuildColumn<"password_reset_tokens", drizzle_orm_pg_core.SetNotNull<drizzle_orm_pg_core.PgTextBuilder<[string, ...string[]]>>, {
3831
3838
  name: string;
3832
- tableName: "signup_link_tokens";
3839
+ tableName: "password_reset_tokens";
3833
3840
  dataType: "string";
3834
3841
  data: string;
3835
3842
  driverParam: string;
@@ -3842,9 +3849,9 @@ declare const signupLinkTokens: drizzle_orm_pg_core.PgTableWithColumns<{
3842
3849
  identity: undefined;
3843
3850
  generated: undefined;
3844
3851
  }>;
3845
- tokenHash: drizzle_orm_pg_core.PgBuildColumn<"signup_link_tokens", drizzle_orm_pg_core.PgTextBuilder<[string, ...string[]]>, {
3852
+ tokenHash: drizzle_orm_pg_core.PgBuildColumn<"password_reset_tokens", drizzle_orm_pg_core.PgTextBuilder<[string, ...string[]]>, {
3846
3853
  name: string;
3847
- tableName: "signup_link_tokens";
3854
+ tableName: "password_reset_tokens";
3848
3855
  dataType: "string";
3849
3856
  data: string;
3850
3857
  driverParam: string;
@@ -3857,9 +3864,9 @@ declare const signupLinkTokens: drizzle_orm_pg_core.PgTableWithColumns<{
3857
3864
  identity: undefined;
3858
3865
  generated: undefined;
3859
3866
  }>;
3860
- returnPath: drizzle_orm_pg_core.PgBuildColumn<"signup_link_tokens", drizzle_orm_pg_core.PgTextBuilder<[string, ...string[]]>, {
3867
+ returnPath: drizzle_orm_pg_core.PgBuildColumn<"password_reset_tokens", drizzle_orm_pg_core.PgTextBuilder<[string, ...string[]]>, {
3861
3868
  name: string;
3862
- tableName: "signup_link_tokens";
3869
+ tableName: "password_reset_tokens";
3863
3870
  dataType: "string";
3864
3871
  data: string;
3865
3872
  driverParam: string;
@@ -3872,9 +3879,9 @@ declare const signupLinkTokens: drizzle_orm_pg_core.PgTableWithColumns<{
3872
3879
  identity: undefined;
3873
3880
  generated: undefined;
3874
3881
  }>;
3875
- expiresAt: drizzle_orm_pg_core.PgBuildColumn<"signup_link_tokens", drizzle_orm_pg_core.SetNotNull<drizzle_orm_pg_core.PgTimestampBuilder>, {
3882
+ expiresAt: drizzle_orm_pg_core.PgBuildColumn<"password_reset_tokens", drizzle_orm_pg_core.SetNotNull<drizzle_orm_pg_core.PgTimestampBuilder>, {
3876
3883
  name: string;
3877
- tableName: "signup_link_tokens";
3884
+ tableName: "password_reset_tokens";
3878
3885
  dataType: "object date";
3879
3886
  data: Date;
3880
3887
  driverParam: string;
@@ -3887,9 +3894,9 @@ declare const signupLinkTokens: drizzle_orm_pg_core.PgTableWithColumns<{
3887
3894
  identity: undefined;
3888
3895
  generated: undefined;
3889
3896
  }>;
3890
- consumedAt: drizzle_orm_pg_core.PgBuildColumn<"signup_link_tokens", drizzle_orm_pg_core.PgTimestampBuilder, {
3897
+ consumedAt: drizzle_orm_pg_core.PgBuildColumn<"password_reset_tokens", drizzle_orm_pg_core.PgTimestampBuilder, {
3891
3898
  name: string;
3892
- tableName: "signup_link_tokens";
3899
+ tableName: "password_reset_tokens";
3893
3900
  dataType: "object date";
3894
3901
  data: Date;
3895
3902
  driverParam: string;
@@ -3902,9 +3909,9 @@ declare const signupLinkTokens: drizzle_orm_pg_core.PgTableWithColumns<{
3902
3909
  identity: undefined;
3903
3910
  generated: undefined;
3904
3911
  }>;
3905
- supersededAt: drizzle_orm_pg_core.PgBuildColumn<"signup_link_tokens", drizzle_orm_pg_core.PgTimestampBuilder, {
3912
+ supersededAt: drizzle_orm_pg_core.PgBuildColumn<"password_reset_tokens", drizzle_orm_pg_core.PgTimestampBuilder, {
3906
3913
  name: string;
3907
- tableName: "signup_link_tokens";
3914
+ tableName: "password_reset_tokens";
3908
3915
  dataType: "object date";
3909
3916
  data: Date;
3910
3917
  driverParam: string;
@@ -3917,9 +3924,9 @@ declare const signupLinkTokens: drizzle_orm_pg_core.PgTableWithColumns<{
3917
3924
  identity: undefined;
3918
3925
  generated: undefined;
3919
3926
  }>;
3920
- setupSecretHash: drizzle_orm_pg_core.PgBuildColumn<"signup_link_tokens", drizzle_orm_pg_core.PgTextBuilder<[string, ...string[]]>, {
3927
+ setupSecretHash: drizzle_orm_pg_core.PgBuildColumn<"password_reset_tokens", drizzle_orm_pg_core.PgTextBuilder<[string, ...string[]]>, {
3921
3928
  name: string;
3922
- tableName: "signup_link_tokens";
3929
+ tableName: "password_reset_tokens";
3923
3930
  dataType: "string";
3924
3931
  data: string;
3925
3932
  driverParam: string;
@@ -3932,9 +3939,9 @@ declare const signupLinkTokens: drizzle_orm_pg_core.PgTableWithColumns<{
3932
3939
  identity: undefined;
3933
3940
  generated: undefined;
3934
3941
  }>;
3935
- setupExpiresAt: drizzle_orm_pg_core.PgBuildColumn<"signup_link_tokens", drizzle_orm_pg_core.PgTimestampBuilder, {
3942
+ setupExpiresAt: drizzle_orm_pg_core.PgBuildColumn<"password_reset_tokens", drizzle_orm_pg_core.PgTimestampBuilder, {
3936
3943
  name: string;
3937
- tableName: "signup_link_tokens";
3944
+ tableName: "password_reset_tokens";
3938
3945
  dataType: "object date";
3939
3946
  data: Date;
3940
3947
  driverParam: string;
@@ -3947,9 +3954,9 @@ declare const signupLinkTokens: drizzle_orm_pg_core.PgTableWithColumns<{
3947
3954
  identity: undefined;
3948
3955
  generated: undefined;
3949
3956
  }>;
3950
- completedAt: drizzle_orm_pg_core.PgBuildColumn<"signup_link_tokens", drizzle_orm_pg_core.PgTimestampBuilder, {
3957
+ completedAt: drizzle_orm_pg_core.PgBuildColumn<"password_reset_tokens", drizzle_orm_pg_core.PgTimestampBuilder, {
3951
3958
  name: string;
3952
- tableName: "signup_link_tokens";
3959
+ tableName: "password_reset_tokens";
3953
3960
  dataType: "object date";
3954
3961
  data: Date;
3955
3962
  driverParam: string;
@@ -3965,35 +3972,34 @@ declare const signupLinkTokens: drizzle_orm_pg_core.PgTableWithColumns<{
3965
3972
  };
3966
3973
  dialect: "pg";
3967
3974
  }>;
3968
- type SignupLinkToken = typeof signupLinkTokens.$inferSelect;
3969
- type NewSignupLinkToken = typeof signupLinkTokens.$inferInsert;
3975
+ type PasswordResetToken = typeof passwordResetTokens.$inferSelect;
3976
+ type NewPasswordResetToken = typeof passwordResetTokens.$inferInsert;
3970
3977
 
3971
3978
  /**
3972
- * @spfn/auth - Password Reset Tokens Entity
3979
+ * @spfn/auth - Revoke-All Link Tokens Entity
3973
3980
  *
3974
- * Backs the password reset flow: a one-time link is emailed to an address the
3975
- * account has already proved, opening it mints a short-lived setup session, and
3976
- * setting a password on that session replaces the old one and signs the browser
3977
- * in.
3981
+ * Backs the signed sign-out-everywhere link: a one-time token an app mails to
3982
+ * an account's proven address, which revokes every device key of that account
3983
+ * without a session. It is the action an owner can take from a mailbox when the
3984
+ * device they would otherwise have to sign in on is the one they no longer
3985
+ * trust.
3978
3986
  *
3979
- * A separate table from `signup_link_tokens` even though the two carry the same
3980
- * columns. They are the same shape, not the same thing: one names an address
3981
- * that has no account and the other names a user that does, and the setup
3982
- * secrets they mint must not be interchangeable. Sharing one table would make
3983
- * "which flow is this row" a per-row property that the lookup would have to
3984
- * remember to filter on — and a lookup that forgets would accept a signup secret
3985
- * as a reset secret.
3987
+ * `key_epoch` is what makes the link dead rather than merely spent. It records
3988
+ * the account's key generation at issue time, and the consume statement joins it
3989
+ * against `users.key_epoch` — so a password reset, a password change, a deletion
3990
+ * request or an earlier revoke-all, every one of which moves the counter, leaves
3991
+ * the outstanding link answering the same 404 an unknown token gets.
3986
3992
  *
3987
- * The link token and the setup secret are bearer credentials, so only their
3988
- * SHA-256 hashes are stored.
3993
+ * The token is a bearer credential, so only its SHA-256 hash is stored, the same
3994
+ * way the signup and reset links store theirs.
3989
3995
  */
3990
- declare const passwordResetTokens: drizzle_orm_pg_core.PgTableWithColumns<{
3991
- name: "password_reset_tokens";
3996
+ declare const keyRevokeAllTokens: drizzle_orm_pg_core.PgTableWithColumns<{
3997
+ name: "key_revoke_all_tokens";
3992
3998
  schema: string;
3993
3999
  columns: {
3994
- createdAt: drizzle_orm_pg_core.PgBuildColumn<"password_reset_tokens", drizzle_orm_pg_core.SetNotNull<drizzle_orm_pg_core.SetHasDefault<drizzle_orm_pg_core.PgTimestampBuilder>>, {
4000
+ createdAt: drizzle_orm_pg_core.PgBuildColumn<"key_revoke_all_tokens", drizzle_orm_pg_core.SetNotNull<drizzle_orm_pg_core.SetHasDefault<drizzle_orm_pg_core.PgTimestampBuilder>>, {
3995
4001
  name: string;
3996
- tableName: "password_reset_tokens";
4002
+ tableName: "key_revoke_all_tokens";
3997
4003
  dataType: "object date";
3998
4004
  data: Date;
3999
4005
  driverParam: string;
@@ -4006,9 +4012,9 @@ declare const passwordResetTokens: drizzle_orm_pg_core.PgTableWithColumns<{
4006
4012
  identity: undefined;
4007
4013
  generated: undefined;
4008
4014
  }>;
4009
- updatedAt: drizzle_orm_pg_core.PgBuildColumn<"password_reset_tokens", drizzle_orm_pg_core.SetHasDefault<drizzle_orm_pg_core.SetNotNull<drizzle_orm_pg_core.SetHasDefault<drizzle_orm_pg_core.PgTimestampBuilder>>>, {
4015
+ updatedAt: drizzle_orm_pg_core.PgBuildColumn<"key_revoke_all_tokens", drizzle_orm_pg_core.SetHasDefault<drizzle_orm_pg_core.SetNotNull<drizzle_orm_pg_core.SetHasDefault<drizzle_orm_pg_core.PgTimestampBuilder>>>, {
4010
4016
  name: string;
4011
- tableName: "password_reset_tokens";
4017
+ tableName: "key_revoke_all_tokens";
4012
4018
  dataType: "object date";
4013
4019
  data: Date;
4014
4020
  driverParam: string;
@@ -4021,9 +4027,9 @@ declare const passwordResetTokens: drizzle_orm_pg_core.PgTableWithColumns<{
4021
4027
  identity: undefined;
4022
4028
  generated: undefined;
4023
4029
  }>;
4024
- id: drizzle_orm_pg_core.PgBuildColumn<"password_reset_tokens", drizzle_orm_pg_core.SetIsPrimaryKey<drizzle_orm_pg_core.PgBigSerial53Builder>, {
4030
+ id: drizzle_orm_pg_core.PgBuildColumn<"key_revoke_all_tokens", drizzle_orm_pg_core.SetIsPrimaryKey<drizzle_orm_pg_core.PgBigSerial53Builder>, {
4025
4031
  name: string;
4026
- tableName: "password_reset_tokens";
4032
+ tableName: "key_revoke_all_tokens";
4027
4033
  dataType: "number int53";
4028
4034
  data: number;
4029
4035
  driverParam: number;
@@ -4036,9 +4042,9 @@ declare const passwordResetTokens: drizzle_orm_pg_core.PgTableWithColumns<{
4036
4042
  identity: undefined;
4037
4043
  generated: undefined;
4038
4044
  }>;
4039
- userId: drizzle_orm_pg_core.PgBuildColumn<"password_reset_tokens", drizzle_orm_pg_core.SetNotNull<drizzle_orm_pg_core.PgBigInt53Builder>, {
4045
+ userId: drizzle_orm_pg_core.PgBuildColumn<"key_revoke_all_tokens", drizzle_orm_pg_core.SetNotNull<drizzle_orm_pg_core.PgBigInt53Builder>, {
4040
4046
  name: string;
4041
- tableName: "password_reset_tokens";
4047
+ tableName: "key_revoke_all_tokens";
4042
4048
  dataType: "number int53";
4043
4049
  data: number;
4044
4050
  driverParam: string | number;
@@ -4051,9 +4057,9 @@ declare const passwordResetTokens: drizzle_orm_pg_core.PgTableWithColumns<{
4051
4057
  identity: undefined;
4052
4058
  generated: undefined;
4053
4059
  }>;
4054
- email: drizzle_orm_pg_core.PgBuildColumn<"password_reset_tokens", drizzle_orm_pg_core.SetNotNull<drizzle_orm_pg_core.PgTextBuilder<[string, ...string[]]>>, {
4060
+ tokenHash: drizzle_orm_pg_core.PgBuildColumn<"key_revoke_all_tokens", drizzle_orm_pg_core.SetNotNull<drizzle_orm_pg_core.PgTextBuilder<[string, ...string[]]>>, {
4055
4061
  name: string;
4056
- tableName: "password_reset_tokens";
4062
+ tableName: "key_revoke_all_tokens";
4057
4063
  dataType: "string";
4058
4064
  data: string;
4059
4065
  driverParam: string;
@@ -4066,13 +4072,13 @@ declare const passwordResetTokens: drizzle_orm_pg_core.PgTableWithColumns<{
4066
4072
  identity: undefined;
4067
4073
  generated: undefined;
4068
4074
  }>;
4069
- tokenHash: drizzle_orm_pg_core.PgBuildColumn<"password_reset_tokens", drizzle_orm_pg_core.PgTextBuilder<[string, ...string[]]>, {
4075
+ keyEpoch: drizzle_orm_pg_core.PgBuildColumn<"key_revoke_all_tokens", drizzle_orm_pg_core.SetNotNull<drizzle_orm_pg_core.PgIntegerBuilder>, {
4070
4076
  name: string;
4071
- tableName: "password_reset_tokens";
4072
- dataType: "string";
4073
- data: string;
4074
- driverParam: string;
4075
- notNull: false;
4077
+ tableName: "key_revoke_all_tokens";
4078
+ dataType: "number int32";
4079
+ data: number;
4080
+ driverParam: string | number;
4081
+ notNull: true;
4076
4082
  hasDefault: false;
4077
4083
  isPrimaryKey: false;
4078
4084
  isAutoincrement: false;
@@ -4081,13 +4087,13 @@ declare const passwordResetTokens: drizzle_orm_pg_core.PgTableWithColumns<{
4081
4087
  identity: undefined;
4082
4088
  generated: undefined;
4083
4089
  }>;
4084
- returnPath: drizzle_orm_pg_core.PgBuildColumn<"password_reset_tokens", drizzle_orm_pg_core.PgTextBuilder<[string, ...string[]]>, {
4090
+ expiresAt: drizzle_orm_pg_core.PgBuildColumn<"key_revoke_all_tokens", drizzle_orm_pg_core.SetNotNull<drizzle_orm_pg_core.PgTimestampBuilder>, {
4085
4091
  name: string;
4086
- tableName: "password_reset_tokens";
4087
- dataType: "string";
4088
- data: string;
4092
+ tableName: "key_revoke_all_tokens";
4093
+ dataType: "object date";
4094
+ data: Date;
4089
4095
  driverParam: string;
4090
- notNull: false;
4096
+ notNull: true;
4091
4097
  hasDefault: false;
4092
4098
  isPrimaryKey: false;
4093
4099
  isAutoincrement: false;
@@ -4096,13 +4102,13 @@ declare const passwordResetTokens: drizzle_orm_pg_core.PgTableWithColumns<{
4096
4102
  identity: undefined;
4097
4103
  generated: undefined;
4098
4104
  }>;
4099
- expiresAt: drizzle_orm_pg_core.PgBuildColumn<"password_reset_tokens", drizzle_orm_pg_core.SetNotNull<drizzle_orm_pg_core.PgTimestampBuilder>, {
4105
+ consumedAt: drizzle_orm_pg_core.PgBuildColumn<"key_revoke_all_tokens", drizzle_orm_pg_core.PgTimestampBuilder, {
4100
4106
  name: string;
4101
- tableName: "password_reset_tokens";
4107
+ tableName: "key_revoke_all_tokens";
4102
4108
  dataType: "object date";
4103
4109
  data: Date;
4104
4110
  driverParam: string;
4105
- notNull: true;
4111
+ notNull: false;
4106
4112
  hasDefault: false;
4107
4113
  isPrimaryKey: false;
4108
4114
  isAutoincrement: false;
@@ -4111,9 +4117,9 @@ declare const passwordResetTokens: drizzle_orm_pg_core.PgTableWithColumns<{
4111
4117
  identity: undefined;
4112
4118
  generated: undefined;
4113
4119
  }>;
4114
- consumedAt: drizzle_orm_pg_core.PgBuildColumn<"password_reset_tokens", drizzle_orm_pg_core.PgTimestampBuilder, {
4120
+ supersededAt: drizzle_orm_pg_core.PgBuildColumn<"key_revoke_all_tokens", drizzle_orm_pg_core.PgTimestampBuilder, {
4115
4121
  name: string;
4116
- tableName: "password_reset_tokens";
4122
+ tableName: "key_revoke_all_tokens";
4117
4123
  dataType: "object date";
4118
4124
  data: Date;
4119
4125
  driverParam: string;
@@ -4126,14 +4132,64 @@ declare const passwordResetTokens: drizzle_orm_pg_core.PgTableWithColumns<{
4126
4132
  identity: undefined;
4127
4133
  generated: undefined;
4128
4134
  }>;
4129
- supersededAt: drizzle_orm_pg_core.PgBuildColumn<"password_reset_tokens", drizzle_orm_pg_core.PgTimestampBuilder, {
4135
+ };
4136
+ dialect: "pg";
4137
+ }>;
4138
+ type KeyRevokeAllToken = typeof keyRevokeAllTokens.$inferSelect;
4139
+ type NewKeyRevokeAllToken = typeof keyRevokeAllTokens.$inferInsert;
4140
+
4141
+ /**
4142
+ * @spfn/auth - WebAuthn Challenges Entity
4143
+ *
4144
+ * The random value an authenticator signs, parked between the `options` call
4145
+ * that minted it and the `verify` call that spends it.
4146
+ *
4147
+ * In the database rather than in a cookie or in memory, for two reasons: the
4148
+ * check that makes a challenge one-time has to be a conditional UPDATE that two
4149
+ * concurrent verifies cannot both win, and several API instances serve the same
4150
+ * browser — an in-process map would let a replay land on the instance that never
4151
+ * saw the first attempt.
4152
+ *
4153
+ * Only the SHA-256 of the challenge is stored, following the rule the package
4154
+ * already applies to signup link tokens. A challenge is a nonce rather than a
4155
+ * bearer credential, so this buys less than it does there; it costs nothing, and
4156
+ * it keeps "plaintext or hash" from becoming a per-table judgement call.
4157
+ */
4158
+ /**
4159
+ * Which ceremony the challenge was minted for.
4160
+ *
4161
+ * Recorded and matched at consume time so a registration challenge cannot be
4162
+ * presented to `login/verify` or the other way round — the two ceremonies
4163
+ * authorize very different things, and only the kind tells them apart once the
4164
+ * value itself is just 32 random bytes.
4165
+ *
4166
+ * `'mfa'` is the third: a step-up assertion, minted for an identified account
4167
+ * and spent by `POST /_auth/mfa/step-up`. It buys the same separation in the
4168
+ * direction that matters most — an assertion collected as a second factor is
4169
+ * not a sign-in, and `passkeys/login/verify` refuses it for exactly the reason
4170
+ * the column exists.
4171
+ *
4172
+ * `'renewal'` is the fourth: the assertion that renews a bound session key, and
4173
+ * the one that lets an account leave `session_binding: 'passkey'`. It is its own
4174
+ * kind rather than a reused `'authentication'` because that kind's challenge is
4175
+ * minted for nobody in particular — a discoverable sign-in names no account —
4176
+ * while a renewal challenge is bound to the account it was issued for, and
4177
+ * spending one where the other was expected would cross those two rules.
4178
+ */
4179
+ declare const WEBAUTHN_CHALLENGE_KINDS: readonly ["registration", "authentication", "mfa", "renewal"];
4180
+ type WebAuthnChallengeKind = typeof WEBAUTHN_CHALLENGE_KINDS[number];
4181
+ declare const webauthnChallenges: drizzle_orm_pg_core.PgTableWithColumns<{
4182
+ name: "webauthn_challenges";
4183
+ schema: string;
4184
+ columns: {
4185
+ id: drizzle_orm_pg_core.PgBuildColumn<"webauthn_challenges", drizzle_orm_pg_core.SetIsPrimaryKey<drizzle_orm_pg_core.PgBigSerial53Builder>, {
4130
4186
  name: string;
4131
- tableName: "password_reset_tokens";
4132
- dataType: "object date";
4133
- data: Date;
4134
- driverParam: string;
4135
- notNull: false;
4136
- hasDefault: false;
4187
+ tableName: "webauthn_challenges";
4188
+ dataType: "number int53";
4189
+ data: number;
4190
+ driverParam: number;
4191
+ notNull: true;
4192
+ hasDefault: true;
4137
4193
  isPrimaryKey: false;
4138
4194
  isAutoincrement: false;
4139
4195
  hasRuntimeDefault: false;
@@ -4141,12 +4197,42 @@ declare const passwordResetTokens: drizzle_orm_pg_core.PgTableWithColumns<{
4141
4197
  identity: undefined;
4142
4198
  generated: undefined;
4143
4199
  }>;
4144
- setupSecretHash: drizzle_orm_pg_core.PgBuildColumn<"password_reset_tokens", drizzle_orm_pg_core.PgTextBuilder<[string, ...string[]]>, {
4200
+ challengeHash: drizzle_orm_pg_core.PgBuildColumn<"webauthn_challenges", drizzle_orm_pg_core.SetNotNull<drizzle_orm_pg_core.PgTextBuilder<[string, ...string[]]>>, {
4145
4201
  name: string;
4146
- tableName: "password_reset_tokens";
4202
+ tableName: "webauthn_challenges";
4147
4203
  dataType: "string";
4148
4204
  data: string;
4149
4205
  driverParam: string;
4206
+ notNull: true;
4207
+ hasDefault: false;
4208
+ isPrimaryKey: false;
4209
+ isAutoincrement: false;
4210
+ hasRuntimeDefault: false;
4211
+ enumValues: undefined;
4212
+ identity: undefined;
4213
+ generated: undefined;
4214
+ }>;
4215
+ kind: drizzle_orm_pg_core.PgBuildColumn<"webauthn_challenges", drizzle_orm_pg_core.SetNotNull<drizzle_orm_pg_core.PgTextBuilder<["registration", "authentication", "mfa", "renewal"] & [string, ...string[]]>>, {
4216
+ name: string;
4217
+ tableName: "webauthn_challenges";
4218
+ dataType: "string enum";
4219
+ data: "registration" | "authentication" | "mfa" | "renewal";
4220
+ driverParam: string;
4221
+ notNull: true;
4222
+ hasDefault: false;
4223
+ isPrimaryKey: false;
4224
+ isAutoincrement: false;
4225
+ hasRuntimeDefault: false;
4226
+ enumValues: ["registration", "authentication", "mfa", "renewal"] & [string, ...string[]];
4227
+ identity: undefined;
4228
+ generated: undefined;
4229
+ }>;
4230
+ userId: drizzle_orm_pg_core.PgBuildColumn<"webauthn_challenges", drizzle_orm_pg_core.PgBigInt53Builder, {
4231
+ name: string;
4232
+ tableName: "webauthn_challenges";
4233
+ dataType: "number int53";
4234
+ data: number;
4235
+ driverParam: string | number;
4150
4236
  notNull: false;
4151
4237
  hasDefault: false;
4152
4238
  isPrimaryKey: false;
@@ -4156,13 +4242,13 @@ declare const passwordResetTokens: drizzle_orm_pg_core.PgTableWithColumns<{
4156
4242
  identity: undefined;
4157
4243
  generated: undefined;
4158
4244
  }>;
4159
- setupExpiresAt: drizzle_orm_pg_core.PgBuildColumn<"password_reset_tokens", drizzle_orm_pg_core.PgTimestampBuilder, {
4245
+ expiresAt: drizzle_orm_pg_core.PgBuildColumn<"webauthn_challenges", drizzle_orm_pg_core.SetNotNull<drizzle_orm_pg_core.PgTimestampBuilder>, {
4160
4246
  name: string;
4161
- tableName: "password_reset_tokens";
4247
+ tableName: "webauthn_challenges";
4162
4248
  dataType: "object date";
4163
4249
  data: Date;
4164
4250
  driverParam: string;
4165
- notNull: false;
4251
+ notNull: true;
4166
4252
  hasDefault: false;
4167
4253
  isPrimaryKey: false;
4168
4254
  isAutoincrement: false;
@@ -4171,9 +4257,9 @@ declare const passwordResetTokens: drizzle_orm_pg_core.PgTableWithColumns<{
4171
4257
  identity: undefined;
4172
4258
  generated: undefined;
4173
4259
  }>;
4174
- completedAt: drizzle_orm_pg_core.PgBuildColumn<"password_reset_tokens", drizzle_orm_pg_core.PgTimestampBuilder, {
4260
+ consumedAt: drizzle_orm_pg_core.PgBuildColumn<"webauthn_challenges", drizzle_orm_pg_core.PgTimestampBuilder, {
4175
4261
  name: string;
4176
- tableName: "password_reset_tokens";
4262
+ tableName: "webauthn_challenges";
4177
4263
  dataType: "object date";
4178
4264
  data: Date;
4179
4265
  driverParam: string;
@@ -4186,37 +4272,56 @@ declare const passwordResetTokens: drizzle_orm_pg_core.PgTableWithColumns<{
4186
4272
  identity: undefined;
4187
4273
  generated: undefined;
4188
4274
  }>;
4275
+ createdAt: drizzle_orm_pg_core.PgBuildColumn<"webauthn_challenges", drizzle_orm_pg_core.SetHasDefault<drizzle_orm_pg_core.SetNotNull<drizzle_orm_pg_core.PgTimestampBuilder>>, {
4276
+ name: string;
4277
+ tableName: "webauthn_challenges";
4278
+ dataType: "object date";
4279
+ data: Date;
4280
+ driverParam: string;
4281
+ notNull: true;
4282
+ hasDefault: true;
4283
+ isPrimaryKey: false;
4284
+ isAutoincrement: false;
4285
+ hasRuntimeDefault: false;
4286
+ enumValues: undefined;
4287
+ identity: undefined;
4288
+ generated: undefined;
4289
+ }>;
4189
4290
  };
4190
4291
  dialect: "pg";
4191
4292
  }>;
4192
- type PasswordResetToken = typeof passwordResetTokens.$inferSelect;
4193
- type NewPasswordResetToken = typeof passwordResetTokens.$inferInsert;
4293
+ type WebAuthnChallenge = typeof webauthnChallenges.$inferSelect;
4294
+ type NewWebAuthnChallenge = typeof webauthnChallenges.$inferInsert;
4194
4295
 
4195
4296
  /**
4196
- * @spfn/auth - Revoke-All Link Tokens Entity
4297
+ * @spfn/auth - TOTP Enrolment Entity
4197
4298
  *
4198
- * Backs the signed sign-out-everywhere link: a one-time token an app mails to
4199
- * an account's proven address, which revokes every device key of that account
4200
- * without a session. It is the action an owner can take from a mailbox when the
4201
- * device they would otherwise have to sign in on is the one they no longer
4202
- * trust.
4299
+ * One row per account that has started enrolling an authenticator app. The row
4300
+ * exists from the moment `totp/enroll` mints a secret; `confirmed_at` is what
4301
+ * turns it into a second factor, so an abandoned enrolment never gates anything
4302
+ * and is swept away a day later.
4203
4303
  *
4204
- * `key_epoch` is what makes the link dead rather than merely spent. It records
4205
- * the account's key generation at issue time, and the consume statement joins it
4206
- * against `users.key_epoch` — so a password reset, a password change, a deletion
4207
- * request or an earlier revoke-all, every one of which moves the counter, leaves
4208
- * the outstanding link answering the same 404 an unknown token gets.
4304
+ * `secret_enc` is the ciphertext `lib/mfa-cipher.ts` produces, bound to this
4305
+ * `user_id` by the AEAD's additional data. A row copied to another account does
4306
+ * not decrypt.
4307
+ */
4308
+ /**
4309
+ * How many wrong codes an unconfirmed enrolment survives.
4209
4310
  *
4210
- * The token is a bearer credential, so only its SHA-256 hash is stored, the same
4211
- * way the signup and reset links store theirs.
4311
+ * Small, because the row is throwaway: five wrong codes means the person is
4312
+ * reading the wrong entry in their app, and starting over with a fresh secret
4313
+ * is both the remedy and the thing that resets the counter. A confirmed row is
4314
+ * never deleted this way — that would be a remote way to strip someone's second
4315
+ * factor.
4212
4316
  */
4213
- declare const keyRevokeAllTokens: drizzle_orm_pg_core.PgTableWithColumns<{
4214
- name: "key_revoke_all_tokens";
4317
+ declare const MFA_CONFIRM_ATTEMPT_LIMIT = 5;
4318
+ declare const mfaTotp: drizzle_orm_pg_core.PgTableWithColumns<{
4319
+ name: "mfa_totp";
4215
4320
  schema: string;
4216
4321
  columns: {
4217
- createdAt: drizzle_orm_pg_core.PgBuildColumn<"key_revoke_all_tokens", drizzle_orm_pg_core.SetNotNull<drizzle_orm_pg_core.SetHasDefault<drizzle_orm_pg_core.PgTimestampBuilder>>, {
4322
+ createdAt: drizzle_orm_pg_core.PgBuildColumn<"mfa_totp", drizzle_orm_pg_core.SetNotNull<drizzle_orm_pg_core.SetHasDefault<drizzle_orm_pg_core.PgTimestampBuilder>>, {
4218
4323
  name: string;
4219
- tableName: "key_revoke_all_tokens";
4324
+ tableName: "mfa_totp";
4220
4325
  dataType: "object date";
4221
4326
  data: Date;
4222
4327
  driverParam: string;
@@ -4229,9 +4334,9 @@ declare const keyRevokeAllTokens: drizzle_orm_pg_core.PgTableWithColumns<{
4229
4334
  identity: undefined;
4230
4335
  generated: undefined;
4231
4336
  }>;
4232
- updatedAt: drizzle_orm_pg_core.PgBuildColumn<"key_revoke_all_tokens", drizzle_orm_pg_core.SetHasDefault<drizzle_orm_pg_core.SetNotNull<drizzle_orm_pg_core.SetHasDefault<drizzle_orm_pg_core.PgTimestampBuilder>>>, {
4337
+ updatedAt: drizzle_orm_pg_core.PgBuildColumn<"mfa_totp", drizzle_orm_pg_core.SetHasDefault<drizzle_orm_pg_core.SetNotNull<drizzle_orm_pg_core.SetHasDefault<drizzle_orm_pg_core.PgTimestampBuilder>>>, {
4233
4338
  name: string;
4234
- tableName: "key_revoke_all_tokens";
4339
+ tableName: "mfa_totp";
4235
4340
  dataType: "object date";
4236
4341
  data: Date;
4237
4342
  driverParam: string;
@@ -4244,9 +4349,9 @@ declare const keyRevokeAllTokens: drizzle_orm_pg_core.PgTableWithColumns<{
4244
4349
  identity: undefined;
4245
4350
  generated: undefined;
4246
4351
  }>;
4247
- id: drizzle_orm_pg_core.PgBuildColumn<"key_revoke_all_tokens", drizzle_orm_pg_core.SetIsPrimaryKey<drizzle_orm_pg_core.PgBigSerial53Builder>, {
4352
+ id: drizzle_orm_pg_core.PgBuildColumn<"mfa_totp", drizzle_orm_pg_core.SetIsPrimaryKey<drizzle_orm_pg_core.PgBigSerial53Builder>, {
4248
4353
  name: string;
4249
- tableName: "key_revoke_all_tokens";
4354
+ tableName: "mfa_totp";
4250
4355
  dataType: "number int53";
4251
4356
  data: number;
4252
4357
  driverParam: number;
@@ -4259,9 +4364,9 @@ declare const keyRevokeAllTokens: drizzle_orm_pg_core.PgTableWithColumns<{
4259
4364
  identity: undefined;
4260
4365
  generated: undefined;
4261
4366
  }>;
4262
- userId: drizzle_orm_pg_core.PgBuildColumn<"key_revoke_all_tokens", drizzle_orm_pg_core.SetNotNull<drizzle_orm_pg_core.PgBigInt53Builder>, {
4367
+ userId: drizzle_orm_pg_core.PgBuildColumn<"mfa_totp", drizzle_orm_pg_core.SetNotNull<drizzle_orm_pg_core.PgBigInt53Builder>, {
4263
4368
  name: string;
4264
- tableName: "key_revoke_all_tokens";
4369
+ tableName: "mfa_totp";
4265
4370
  dataType: "number int53";
4266
4371
  data: number;
4267
4372
  driverParam: string | number;
@@ -4274,9 +4379,9 @@ declare const keyRevokeAllTokens: drizzle_orm_pg_core.PgTableWithColumns<{
4274
4379
  identity: undefined;
4275
4380
  generated: undefined;
4276
4381
  }>;
4277
- tokenHash: drizzle_orm_pg_core.PgBuildColumn<"key_revoke_all_tokens", drizzle_orm_pg_core.SetNotNull<drizzle_orm_pg_core.PgTextBuilder<[string, ...string[]]>>, {
4382
+ secretEnc: drizzle_orm_pg_core.PgBuildColumn<"mfa_totp", drizzle_orm_pg_core.SetNotNull<drizzle_orm_pg_core.PgTextBuilder<[string, ...string[]]>>, {
4278
4383
  name: string;
4279
- tableName: "key_revoke_all_tokens";
4384
+ tableName: "mfa_totp";
4280
4385
  dataType: "string";
4281
4386
  data: string;
4282
4387
  driverParam: string;
@@ -4289,28 +4394,13 @@ declare const keyRevokeAllTokens: drizzle_orm_pg_core.PgTableWithColumns<{
4289
4394
  identity: undefined;
4290
4395
  generated: undefined;
4291
4396
  }>;
4292
- keyEpoch: drizzle_orm_pg_core.PgBuildColumn<"key_revoke_all_tokens", drizzle_orm_pg_core.SetNotNull<drizzle_orm_pg_core.PgIntegerBuilder>, {
4293
- name: string;
4294
- tableName: "key_revoke_all_tokens";
4295
- dataType: "number int32";
4296
- data: number;
4297
- driverParam: string | number;
4298
- notNull: true;
4299
- hasDefault: false;
4300
- isPrimaryKey: false;
4301
- isAutoincrement: false;
4302
- hasRuntimeDefault: false;
4303
- enumValues: undefined;
4304
- identity: undefined;
4305
- generated: undefined;
4306
- }>;
4307
- expiresAt: drizzle_orm_pg_core.PgBuildColumn<"key_revoke_all_tokens", drizzle_orm_pg_core.SetNotNull<drizzle_orm_pg_core.PgTimestampBuilder>, {
4397
+ confirmedAt: drizzle_orm_pg_core.PgBuildColumn<"mfa_totp", drizzle_orm_pg_core.PgTimestampBuilder, {
4308
4398
  name: string;
4309
- tableName: "key_revoke_all_tokens";
4399
+ tableName: "mfa_totp";
4310
4400
  dataType: "object date";
4311
4401
  data: Date;
4312
4402
  driverParam: string;
4313
- notNull: true;
4403
+ notNull: false;
4314
4404
  hasDefault: false;
4315
4405
  isPrimaryKey: false;
4316
4406
  isAutoincrement: false;
@@ -4319,12 +4409,12 @@ declare const keyRevokeAllTokens: drizzle_orm_pg_core.PgTableWithColumns<{
4319
4409
  identity: undefined;
4320
4410
  generated: undefined;
4321
4411
  }>;
4322
- consumedAt: drizzle_orm_pg_core.PgBuildColumn<"key_revoke_all_tokens", drizzle_orm_pg_core.PgTimestampBuilder, {
4412
+ lastUsedStep: drizzle_orm_pg_core.PgBuildColumn<"mfa_totp", drizzle_orm_pg_core.PgBigInt53Builder, {
4323
4413
  name: string;
4324
- tableName: "key_revoke_all_tokens";
4325
- dataType: "object date";
4326
- data: Date;
4327
- driverParam: string;
4414
+ tableName: "mfa_totp";
4415
+ dataType: "number int53";
4416
+ data: number;
4417
+ driverParam: string | number;
4328
4418
  notNull: false;
4329
4419
  hasDefault: false;
4330
4420
  isPrimaryKey: false;
@@ -4334,14 +4424,14 @@ declare const keyRevokeAllTokens: drizzle_orm_pg_core.PgTableWithColumns<{
4334
4424
  identity: undefined;
4335
4425
  generated: undefined;
4336
4426
  }>;
4337
- supersededAt: drizzle_orm_pg_core.PgBuildColumn<"key_revoke_all_tokens", drizzle_orm_pg_core.PgTimestampBuilder, {
4427
+ failedConfirmAttempts: drizzle_orm_pg_core.PgBuildColumn<"mfa_totp", drizzle_orm_pg_core.SetHasDefault<drizzle_orm_pg_core.SetNotNull<drizzle_orm_pg_core.PgIntegerBuilder>>, {
4338
4428
  name: string;
4339
- tableName: "key_revoke_all_tokens";
4340
- dataType: "object date";
4341
- data: Date;
4342
- driverParam: string;
4343
- notNull: false;
4344
- hasDefault: false;
4429
+ tableName: "mfa_totp";
4430
+ dataType: "number int32";
4431
+ data: number;
4432
+ driverParam: string | number;
4433
+ notNull: true;
4434
+ hasDefault: true;
4345
4435
  isPrimaryKey: false;
4346
4436
  isAutoincrement: false;
4347
4437
  hasRuntimeDefault: false;
@@ -4352,46 +4442,33 @@ declare const keyRevokeAllTokens: drizzle_orm_pg_core.PgTableWithColumns<{
4352
4442
  };
4353
4443
  dialect: "pg";
4354
4444
  }>;
4355
- type KeyRevokeAllToken = typeof keyRevokeAllTokens.$inferSelect;
4356
- type NewKeyRevokeAllToken = typeof keyRevokeAllTokens.$inferInsert;
4445
+ type MfaTotp = typeof mfaTotp.$inferSelect;
4446
+ type NewMfaTotp = typeof mfaTotp.$inferInsert;
4357
4447
 
4358
4448
  /**
4359
- * @spfn/auth - WebAuthn Challenges Entity
4360
- *
4361
- * The random value an authenticator signs, parked between the `options` call
4362
- * that minted it and the `verify` call that spends it.
4363
- *
4364
- * In the database rather than in a cookie or in memory, for two reasons: the
4365
- * check that makes a challenge one-time has to be a conditional UPDATE that two
4366
- * concurrent verifies cannot both win, and several API instances serve the same
4367
- * browser — an in-process map would let a replay land on the instance that never
4368
- * saw the first attempt.
4449
+ * @spfn/auth - Recovery Codes Entity
4369
4450
  *
4370
- * Only the SHA-256 of the challenge is stored, following the rule the package
4371
- * already applies to signup link tokens. A challenge is a nonce rather than a
4372
- * bearer credential, so this buys less than it does there; it costs nothing, and
4373
- * it keeps "plaintext or hash" from becoming a per-table judgement call.
4374
- */
4375
- /**
4376
- * Which ceremony the challenge was minted for.
4451
+ * The ten single-use codes an enrolled account is shown once. Only the hash is
4452
+ * stored, and it is a **password** hash rather than the unsalted SHA-256 the
4453
+ * link flows use — a code a human transcribes is short enough that a leaked
4454
+ * dump of unsalted hashes falls to an offline sweep. `lib/recovery-codes.ts`
4455
+ * sets out the reasoning.
4377
4456
  *
4378
- * Recorded and matched at consume time so a registration challenge cannot be
4379
- * presented to `login/verify` or the other way round — the two ceremonies
4380
- * authorize very different things, and only the kind tells them apart once the
4381
- * value itself is just 32 random bytes.
4457
+ * Codes come in generations. Regenerating raises the generation rather than
4458
+ * deleting the old rows, so a code someone wrote down last year is refused with
4459
+ * the same body as one that never existed, and the history of which generation
4460
+ * a code was spent from survives.
4382
4461
  */
4383
- declare const WEBAUTHN_CHALLENGE_KINDS: readonly ["registration", "authentication"];
4384
- type WebAuthnChallengeKind = typeof WEBAUTHN_CHALLENGE_KINDS[number];
4385
- declare const webauthnChallenges: drizzle_orm_pg_core.PgTableWithColumns<{
4386
- name: "webauthn_challenges";
4462
+ declare const mfaRecoveryCodes: drizzle_orm_pg_core.PgTableWithColumns<{
4463
+ name: "mfa_recovery_codes";
4387
4464
  schema: string;
4388
4465
  columns: {
4389
- id: drizzle_orm_pg_core.PgBuildColumn<"webauthn_challenges", drizzle_orm_pg_core.SetIsPrimaryKey<drizzle_orm_pg_core.PgBigSerial53Builder>, {
4466
+ createdAt: drizzle_orm_pg_core.PgBuildColumn<"mfa_recovery_codes", drizzle_orm_pg_core.SetNotNull<drizzle_orm_pg_core.SetHasDefault<drizzle_orm_pg_core.PgTimestampBuilder>>, {
4390
4467
  name: string;
4391
- tableName: "webauthn_challenges";
4392
- dataType: "number int53";
4393
- data: number;
4394
- driverParam: number;
4468
+ tableName: "mfa_recovery_codes";
4469
+ dataType: "object date";
4470
+ data: Date;
4471
+ driverParam: string;
4395
4472
  notNull: true;
4396
4473
  hasDefault: true;
4397
4474
  isPrimaryKey: false;
@@ -4401,14 +4478,14 @@ declare const webauthnChallenges: drizzle_orm_pg_core.PgTableWithColumns<{
4401
4478
  identity: undefined;
4402
4479
  generated: undefined;
4403
4480
  }>;
4404
- challengeHash: drizzle_orm_pg_core.PgBuildColumn<"webauthn_challenges", drizzle_orm_pg_core.SetNotNull<drizzle_orm_pg_core.PgTextBuilder<[string, ...string[]]>>, {
4481
+ updatedAt: drizzle_orm_pg_core.PgBuildColumn<"mfa_recovery_codes", drizzle_orm_pg_core.SetHasDefault<drizzle_orm_pg_core.SetNotNull<drizzle_orm_pg_core.SetHasDefault<drizzle_orm_pg_core.PgTimestampBuilder>>>, {
4405
4482
  name: string;
4406
- tableName: "webauthn_challenges";
4407
- dataType: "string";
4408
- data: string;
4483
+ tableName: "mfa_recovery_codes";
4484
+ dataType: "object date";
4485
+ data: Date;
4409
4486
  driverParam: string;
4410
4487
  notNull: true;
4411
- hasDefault: false;
4488
+ hasDefault: true;
4412
4489
  isPrimaryKey: false;
4413
4490
  isAutoincrement: false;
4414
4491
  hasRuntimeDefault: false;
@@ -4416,28 +4493,28 @@ declare const webauthnChallenges: drizzle_orm_pg_core.PgTableWithColumns<{
4416
4493
  identity: undefined;
4417
4494
  generated: undefined;
4418
4495
  }>;
4419
- kind: drizzle_orm_pg_core.PgBuildColumn<"webauthn_challenges", drizzle_orm_pg_core.SetNotNull<drizzle_orm_pg_core.PgTextBuilder<["registration", "authentication"] & [string, ...string[]]>>, {
4496
+ id: drizzle_orm_pg_core.PgBuildColumn<"mfa_recovery_codes", drizzle_orm_pg_core.SetIsPrimaryKey<drizzle_orm_pg_core.PgBigSerial53Builder>, {
4420
4497
  name: string;
4421
- tableName: "webauthn_challenges";
4422
- dataType: "string enum";
4423
- data: "registration" | "authentication";
4424
- driverParam: string;
4498
+ tableName: "mfa_recovery_codes";
4499
+ dataType: "number int53";
4500
+ data: number;
4501
+ driverParam: number;
4425
4502
  notNull: true;
4426
- hasDefault: false;
4503
+ hasDefault: true;
4427
4504
  isPrimaryKey: false;
4428
4505
  isAutoincrement: false;
4429
4506
  hasRuntimeDefault: false;
4430
- enumValues: ["registration", "authentication"] & [string, ...string[]];
4507
+ enumValues: undefined;
4431
4508
  identity: undefined;
4432
4509
  generated: undefined;
4433
4510
  }>;
4434
- userId: drizzle_orm_pg_core.PgBuildColumn<"webauthn_challenges", drizzle_orm_pg_core.PgBigInt53Builder, {
4511
+ userId: drizzle_orm_pg_core.PgBuildColumn<"mfa_recovery_codes", drizzle_orm_pg_core.SetNotNull<drizzle_orm_pg_core.PgBigInt53Builder>, {
4435
4512
  name: string;
4436
- tableName: "webauthn_challenges";
4513
+ tableName: "mfa_recovery_codes";
4437
4514
  dataType: "number int53";
4438
4515
  data: number;
4439
4516
  driverParam: string | number;
4440
- notNull: false;
4517
+ notNull: true;
4441
4518
  hasDefault: false;
4442
4519
  isPrimaryKey: false;
4443
4520
  isAutoincrement: false;
@@ -4446,12 +4523,12 @@ declare const webauthnChallenges: drizzle_orm_pg_core.PgTableWithColumns<{
4446
4523
  identity: undefined;
4447
4524
  generated: undefined;
4448
4525
  }>;
4449
- expiresAt: drizzle_orm_pg_core.PgBuildColumn<"webauthn_challenges", drizzle_orm_pg_core.SetNotNull<drizzle_orm_pg_core.PgTimestampBuilder>, {
4526
+ generation: drizzle_orm_pg_core.PgBuildColumn<"mfa_recovery_codes", drizzle_orm_pg_core.SetNotNull<drizzle_orm_pg_core.PgIntegerBuilder>, {
4450
4527
  name: string;
4451
- tableName: "webauthn_challenges";
4452
- dataType: "object date";
4453
- data: Date;
4454
- driverParam: string;
4528
+ tableName: "mfa_recovery_codes";
4529
+ dataType: "number int32";
4530
+ data: number;
4531
+ driverParam: string | number;
4455
4532
  notNull: true;
4456
4533
  hasDefault: false;
4457
4534
  isPrimaryKey: false;
@@ -4461,13 +4538,13 @@ declare const webauthnChallenges: drizzle_orm_pg_core.PgTableWithColumns<{
4461
4538
  identity: undefined;
4462
4539
  generated: undefined;
4463
4540
  }>;
4464
- consumedAt: drizzle_orm_pg_core.PgBuildColumn<"webauthn_challenges", drizzle_orm_pg_core.PgTimestampBuilder, {
4541
+ codeHash: drizzle_orm_pg_core.PgBuildColumn<"mfa_recovery_codes", drizzle_orm_pg_core.SetNotNull<drizzle_orm_pg_core.PgTextBuilder<[string, ...string[]]>>, {
4465
4542
  name: string;
4466
- tableName: "webauthn_challenges";
4467
- dataType: "object date";
4468
- data: Date;
4543
+ tableName: "mfa_recovery_codes";
4544
+ dataType: "string";
4545
+ data: string;
4469
4546
  driverParam: string;
4470
- notNull: false;
4547
+ notNull: true;
4471
4548
  hasDefault: false;
4472
4549
  isPrimaryKey: false;
4473
4550
  isAutoincrement: false;
@@ -4476,14 +4553,14 @@ declare const webauthnChallenges: drizzle_orm_pg_core.PgTableWithColumns<{
4476
4553
  identity: undefined;
4477
4554
  generated: undefined;
4478
4555
  }>;
4479
- createdAt: drizzle_orm_pg_core.PgBuildColumn<"webauthn_challenges", drizzle_orm_pg_core.SetHasDefault<drizzle_orm_pg_core.SetNotNull<drizzle_orm_pg_core.PgTimestampBuilder>>, {
4556
+ usedAt: drizzle_orm_pg_core.PgBuildColumn<"mfa_recovery_codes", drizzle_orm_pg_core.PgTimestampBuilder, {
4480
4557
  name: string;
4481
- tableName: "webauthn_challenges";
4558
+ tableName: "mfa_recovery_codes";
4482
4559
  dataType: "object date";
4483
4560
  data: Date;
4484
4561
  driverParam: string;
4485
- notNull: true;
4486
- hasDefault: true;
4562
+ notNull: false;
4563
+ hasDefault: false;
4487
4564
  isPrimaryKey: false;
4488
4565
  isAutoincrement: false;
4489
4566
  hasRuntimeDefault: false;
@@ -4494,8 +4571,8 @@ declare const webauthnChallenges: drizzle_orm_pg_core.PgTableWithColumns<{
4494
4571
  };
4495
4572
  dialect: "pg";
4496
4573
  }>;
4497
- type WebAuthnChallenge = typeof webauthnChallenges.$inferSelect;
4498
- type NewWebAuthnChallenge = typeof webauthnChallenges.$inferInsert;
4574
+ type MfaRecoveryCode = typeof mfaRecoveryCodes.$inferSelect;
4575
+ type NewMfaRecoveryCode = typeof mfaRecoveryCodes.$inferInsert;
4499
4576
 
4500
4577
  /**
4501
4578
  * @spfn/auth - Device Authorizations Entity
@@ -5963,6 +6040,8 @@ declare class UsersRepository extends BaseRepository {
5963
6040
  emailVerifiedAt: Date | null;
5964
6041
  phoneVerifiedAt: Date | null;
5965
6042
  keyEpoch: number;
6043
+ sessionBinding: "none" | "passkey";
6044
+ sessionBindingChangedAt: Date | null;
5966
6045
  lastLoginAt: Date | null;
5967
6046
  }>;
5968
6047
  /**
@@ -5989,6 +6068,8 @@ declare class UsersRepository extends BaseRepository {
5989
6068
  emailVerifiedAt: Date | null;
5990
6069
  phoneVerifiedAt: Date | null;
5991
6070
  keyEpoch: number;
6071
+ sessionBinding: "none" | "passkey";
6072
+ sessionBindingChangedAt: Date | null;
5992
6073
  lastLoginAt: Date | null;
5993
6074
  }>;
5994
6075
  /**
@@ -6026,6 +6107,8 @@ declare class UsersRepository extends BaseRepository {
6026
6107
  emailVerifiedAt: Date | null;
6027
6108
  phoneVerifiedAt: Date | null;
6028
6109
  keyEpoch: number;
6110
+ sessionBinding: "none" | "passkey";
6111
+ sessionBindingChangedAt: Date | null;
6029
6112
  lastLoginAt: Date | null;
6030
6113
  }>;
6031
6114
  /**
@@ -6059,6 +6142,8 @@ declare class UsersRepository extends BaseRepository {
6059
6142
  emailVerifiedAt: Date | null;
6060
6143
  phoneVerifiedAt: Date | null;
6061
6144
  keyEpoch: number;
6145
+ sessionBinding: "none" | "passkey";
6146
+ sessionBindingChangedAt: Date | null;
6062
6147
  lastLoginAt: Date | null;
6063
6148
  }>;
6064
6149
  /**
@@ -6082,6 +6167,8 @@ declare class UsersRepository extends BaseRepository {
6082
6167
  emailVerifiedAt: Date | null;
6083
6168
  phoneVerifiedAt: Date | null;
6084
6169
  keyEpoch: number;
6170
+ sessionBinding: "none" | "passkey";
6171
+ sessionBindingChangedAt: Date | null;
6085
6172
  lastLoginAt: Date | null;
6086
6173
  }>;
6087
6174
  /**
@@ -6105,6 +6192,8 @@ declare class UsersRepository extends BaseRepository {
6105
6192
  emailVerifiedAt: Date | null;
6106
6193
  phoneVerifiedAt: Date | null;
6107
6194
  keyEpoch: number;
6195
+ sessionBinding: "none" | "passkey";
6196
+ sessionBindingChangedAt: Date | null;
6108
6197
  lastLoginAt: Date | null;
6109
6198
  }>;
6110
6199
  /**
@@ -6128,6 +6217,8 @@ declare class UsersRepository extends BaseRepository {
6128
6217
  emailVerifiedAt: Date | null;
6129
6218
  phoneVerifiedAt: Date | null;
6130
6219
  keyEpoch: number;
6220
+ sessionBinding: "none" | "passkey";
6221
+ sessionBindingChangedAt: Date | null;
6131
6222
  lastLoginAt: Date | null;
6132
6223
  }>;
6133
6224
  /**
@@ -6151,6 +6242,8 @@ declare class UsersRepository extends BaseRepository {
6151
6242
  emailVerifiedAt: Date | null;
6152
6243
  phoneVerifiedAt: Date | null;
6153
6244
  keyEpoch: number;
6245
+ sessionBinding: "none" | "passkey";
6246
+ sessionBindingChangedAt: Date | null;
6154
6247
  lastLoginAt: Date | null;
6155
6248
  } | null>;
6156
6249
  /**
@@ -6177,6 +6270,8 @@ declare class UsersRepository extends BaseRepository {
6177
6270
  emailVerifiedAt: Date | null;
6178
6271
  phoneVerifiedAt: Date | null;
6179
6272
  keyEpoch: number;
6273
+ sessionBinding: "none" | "passkey";
6274
+ sessionBindingChangedAt: Date | null;
6180
6275
  lastLoginAt: Date | null;
6181
6276
  };
6182
6277
  role: {
@@ -6192,6 +6287,7 @@ declare class UsersRepository extends BaseRepository {
6192
6287
  create(data: NewUser): Promise<{
6193
6288
  email: string | null;
6194
6289
  phone: string | null;
6290
+ sessionBinding: "none" | "passkey";
6195
6291
  status: "active" | "inactive" | "suspended" | "pending_deletion" | "deleted";
6196
6292
  publicId: string;
6197
6293
  passwordChangeRequired: boolean;
@@ -6204,6 +6300,7 @@ declare class UsersRepository extends BaseRepository {
6204
6300
  emailVerifiedAt: Date | null;
6205
6301
  phoneVerifiedAt: Date | null;
6206
6302
  keyEpoch: number;
6303
+ sessionBindingChangedAt: Date | null;
6207
6304
  lastLoginAt: Date | null;
6208
6305
  deletedAt: Date | null;
6209
6306
  deletedBy: string | null;
@@ -6274,6 +6371,8 @@ declare class UsersRepository extends BaseRepository {
6274
6371
  emailVerifiedAt: Date | null;
6275
6372
  phoneVerifiedAt: Date | null;
6276
6373
  keyEpoch: number;
6374
+ sessionBinding: "none" | "passkey";
6375
+ sessionBindingChangedAt: Date | null;
6277
6376
  lastLoginAt: Date | null;
6278
6377
  }>;
6279
6378
  /**
@@ -6302,6 +6401,8 @@ declare class UsersRepository extends BaseRepository {
6302
6401
  emailVerifiedAt: Date | null;
6303
6402
  phoneVerifiedAt: Date | null;
6304
6403
  keyEpoch: number;
6404
+ sessionBinding: "none" | "passkey";
6405
+ sessionBindingChangedAt: Date | null;
6305
6406
  lastLoginAt: Date | null;
6306
6407
  }>;
6307
6408
  /**
@@ -6325,6 +6426,8 @@ declare class UsersRepository extends BaseRepository {
6325
6426
  emailVerifiedAt: Date | null;
6326
6427
  phoneVerifiedAt: Date | null;
6327
6428
  keyEpoch: number;
6429
+ sessionBinding: "none" | "passkey";
6430
+ sessionBindingChangedAt: Date | null;
6328
6431
  lastLoginAt: Date | null;
6329
6432
  }>;
6330
6433
  /**
@@ -6348,6 +6451,8 @@ declare class UsersRepository extends BaseRepository {
6348
6451
  emailVerifiedAt: Date | null;
6349
6452
  phoneVerifiedAt: Date | null;
6350
6453
  keyEpoch: number;
6454
+ sessionBinding: "none" | "passkey";
6455
+ sessionBindingChangedAt: Date | null;
6351
6456
  lastLoginAt: Date | null;
6352
6457
  }>;
6353
6458
  /**
@@ -6357,6 +6462,7 @@ declare class UsersRepository extends BaseRepository {
6357
6462
  deleteById(id: number): Promise<{
6358
6463
  email: string | null;
6359
6464
  phone: string | null;
6465
+ sessionBinding: "none" | "passkey";
6360
6466
  status: "active" | "inactive" | "suspended" | "pending_deletion" | "deleted";
6361
6467
  publicId: string;
6362
6468
  passwordChangeRequired: boolean;
@@ -6369,6 +6475,7 @@ declare class UsersRepository extends BaseRepository {
6369
6475
  emailVerifiedAt: Date | null;
6370
6476
  phoneVerifiedAt: Date | null;
6371
6477
  keyEpoch: number;
6478
+ sessionBindingChangedAt: Date | null;
6372
6479
  lastLoginAt: Date | null;
6373
6480
  deletedAt: Date | null;
6374
6481
  deletedBy: string | null;
@@ -6472,6 +6579,11 @@ declare class KeysRepository extends BaseRepository {
6472
6579
  platform: "ios" | "android" | "web" | "desktop" | null;
6473
6580
  registeredIp: string | null;
6474
6581
  registeredUserAgent: string | null;
6582
+ registeredUaFamily: string | null;
6583
+ lastSeenIp: string | null;
6584
+ lastSeenAt: Date | null;
6585
+ concurrentUseAt: Date | null;
6586
+ binding: "none" | "passkey";
6475
6587
  clientKind: "ios" | "android" | "web" | null;
6476
6588
  clientVersion: string | null;
6477
6589
  clientContractVersion: string | null;
@@ -6498,6 +6610,11 @@ declare class KeysRepository extends BaseRepository {
6498
6610
  platform: "ios" | "android" | "web" | "desktop" | null;
6499
6611
  registeredIp: string | null;
6500
6612
  registeredUserAgent: string | null;
6613
+ registeredUaFamily: string | null;
6614
+ lastSeenIp: string | null;
6615
+ lastSeenAt: Date | null;
6616
+ concurrentUseAt: Date | null;
6617
+ binding: "none" | "passkey";
6501
6618
  clientKind: "ios" | "android" | "web" | null;
6502
6619
  clientVersion: string | null;
6503
6620
  clientContractVersion: string | null;
@@ -6524,6 +6641,11 @@ declare class KeysRepository extends BaseRepository {
6524
6641
  platform: "ios" | "android" | "web" | "desktop" | null;
6525
6642
  registeredIp: string | null;
6526
6643
  registeredUserAgent: string | null;
6644
+ registeredUaFamily: string | null;
6645
+ lastSeenIp: string | null;
6646
+ lastSeenAt: Date | null;
6647
+ concurrentUseAt: Date | null;
6648
+ binding: "none" | "passkey";
6527
6649
  clientKind: "ios" | "android" | "web" | null;
6528
6650
  clientVersion: string | null;
6529
6651
  clientContractVersion: string | null;
@@ -6543,6 +6665,10 @@ declare class KeysRepository extends BaseRepository {
6543
6665
  *
6544
6666
  * `includeRevoked`는 이미 끊은 기기까지 보여준다 — "내가 언제 무엇을 끊었나"를
6545
6667
  * 확인하는 용도라, 폐기 시각과 사유를 함께 고른다.
6668
+ *
6669
+ * `lastSeenIp` is not selected, deliberately. The concurrent-use moment is
6670
+ * what the owner acts on; the trail of addresses behind it is stored PII the
6671
+ * account surface has no use for.
6546
6672
  * Read replica 사용
6547
6673
  */
6548
6674
  listForUser(userId: number, includeRevoked?: boolean): Promise<{
@@ -6558,6 +6684,8 @@ declare class KeysRepository extends BaseRepository {
6558
6684
  revokedAt: Date | null;
6559
6685
  registeredIp: string | null;
6560
6686
  registeredUserAgent: string | null;
6687
+ binding: "none" | "passkey";
6688
+ concurrentUseAt: Date | null;
6561
6689
  }[]>;
6562
6690
  /**
6563
6691
  * 공개키 생성
@@ -6579,6 +6707,11 @@ declare class KeysRepository extends BaseRepository {
6579
6707
  lastUsedAt: Date | null;
6580
6708
  registeredIp: string | null;
6581
6709
  registeredUserAgent: string | null;
6710
+ registeredUaFamily: string | null;
6711
+ lastSeenIp: string | null;
6712
+ lastSeenAt: Date | null;
6713
+ concurrentUseAt: Date | null;
6714
+ binding: "none" | "passkey";
6582
6715
  clientKind: "ios" | "android" | "web" | null;
6583
6716
  clientVersion: string | null;
6584
6717
  clientContractVersion: string | null;
@@ -6606,6 +6739,11 @@ declare class KeysRepository extends BaseRepository {
6606
6739
  platform: "ios" | "android" | "web" | "desktop" | null;
6607
6740
  registeredIp: string | null;
6608
6741
  registeredUserAgent: string | null;
6742
+ registeredUaFamily: string | null;
6743
+ lastSeenIp: string | null;
6744
+ lastSeenAt: Date | null;
6745
+ concurrentUseAt: Date | null;
6746
+ binding: "none" | "passkey";
6609
6747
  clientKind: "ios" | "android" | "web" | null;
6610
6748
  clientVersion: string | null;
6611
6749
  clientContractVersion: string | null;
@@ -6669,6 +6807,11 @@ declare class KeysRepository extends BaseRepository {
6669
6807
  lastUsedAt: Date | null;
6670
6808
  registeredIp: string | null;
6671
6809
  registeredUserAgent: string | null;
6810
+ registeredUaFamily: string | null;
6811
+ lastSeenIp: string | null;
6812
+ lastSeenAt: Date | null;
6813
+ concurrentUseAt: Date | null;
6814
+ binding: "none" | "passkey";
6672
6815
  clientKind: "ios" | "android" | "web" | null;
6673
6816
  clientVersion: string | null;
6674
6817
  clientContractVersion: string | null;
@@ -6698,6 +6841,11 @@ declare class KeysRepository extends BaseRepository {
6698
6841
  platform: "ios" | "android" | "web" | "desktop" | null;
6699
6842
  registeredIp: string | null;
6700
6843
  registeredUserAgent: string | null;
6844
+ registeredUaFamily: string | null;
6845
+ lastSeenIp: string | null;
6846
+ lastSeenAt: Date | null;
6847
+ concurrentUseAt: Date | null;
6848
+ binding: "none" | "passkey";
6701
6849
  clientKind: "ios" | "android" | "web" | null;
6702
6850
  clientVersion: string | null;
6703
6851
  clientContractVersion: string | null;
@@ -6724,6 +6872,46 @@ declare class KeysRepository extends BaseRepository {
6724
6872
  platform: "ios" | "android" | "web" | "desktop" | null;
6725
6873
  registeredIp: string | null;
6726
6874
  registeredUserAgent: string | null;
6875
+ registeredUaFamily: string | null;
6876
+ lastSeenIp: string | null;
6877
+ lastSeenAt: Date | null;
6878
+ concurrentUseAt: Date | null;
6879
+ binding: "none" | "passkey";
6880
+ clientKind: "ios" | "android" | "web" | null;
6881
+ clientVersion: string | null;
6882
+ clientContractVersion: string | null;
6883
+ clientSeenAt: Date | null;
6884
+ isActive: boolean;
6885
+ createdAt: Date;
6886
+ lastUsedAt: Date | null;
6887
+ expiresAt: Date | null;
6888
+ revokedAt: Date | null;
6889
+ revokedReason: string | null;
6890
+ }>;
6891
+ /**
6892
+ * Bind one live key to the account's passkey, and give it the short life
6893
+ * that goes with it.
6894
+ *
6895
+ * Scoped by user and by `isActive`, like every other targeted update here, so
6896
+ * the answer is "this call bound something" rather than "a row exists".
6897
+ * Write primary 사용
6898
+ */
6899
+ bindByKeyIdAndUserId(keyId: string, userId: number, expiresAt: Date): Promise<{
6900
+ id: number;
6901
+ userId: number;
6902
+ keyId: string;
6903
+ publicKey: string;
6904
+ algorithm: "ES256" | "RS256";
6905
+ fingerprint: string;
6906
+ deviceName: string | null;
6907
+ platform: "ios" | "android" | "web" | "desktop" | null;
6908
+ registeredIp: string | null;
6909
+ registeredUserAgent: string | null;
6910
+ registeredUaFamily: string | null;
6911
+ lastSeenIp: string | null;
6912
+ lastSeenAt: Date | null;
6913
+ concurrentUseAt: Date | null;
6914
+ binding: "none" | "passkey";
6727
6915
  clientKind: "ios" | "android" | "web" | null;
6728
6916
  clientVersion: string | null;
6729
6917
  clientContractVersion: string | null;
@@ -6735,6 +6923,15 @@ declare class KeysRepository extends BaseRepository {
6735
6923
  revokedAt: Date | null;
6736
6924
  revokedReason: string | null;
6737
6925
  }>;
6926
+ /**
6927
+ * Return every one of a user's bound keys to an ordinary long-lived key.
6928
+ *
6929
+ * One statement, because turning the setting off has to leave no key behind:
6930
+ * a row still marked `'passkey'` would keep expiring in hours with nothing
6931
+ * left to renew it, and its owner has just said they do not want that.
6932
+ * Write primary 사용
6933
+ */
6934
+ unbindActiveByUserId(userId: number, expiresAt: Date): Promise<number>;
6738
6935
  /**
6739
6936
  * Key ID로 공개키 조회 — 활성 여부 무관 (clientProofV1 admission의 revocation 판정용)
6740
6937
  *
@@ -6753,6 +6950,11 @@ declare class KeysRepository extends BaseRepository {
6753
6950
  platform: "ios" | "android" | "web" | "desktop" | null;
6754
6951
  registeredIp: string | null;
6755
6952
  registeredUserAgent: string | null;
6953
+ registeredUaFamily: string | null;
6954
+ lastSeenIp: string | null;
6955
+ lastSeenAt: Date | null;
6956
+ concurrentUseAt: Date | null;
6957
+ binding: "none" | "passkey";
6756
6958
  clientKind: "ios" | "android" | "web" | null;
6757
6959
  clientVersion: string | null;
6758
6960
  clientContractVersion: string | null;
@@ -6779,6 +6981,11 @@ declare class KeysRepository extends BaseRepository {
6779
6981
  platform: "ios" | "android" | "web" | "desktop" | null;
6780
6982
  registeredIp: string | null;
6781
6983
  registeredUserAgent: string | null;
6984
+ registeredUaFamily: string | null;
6985
+ lastSeenIp: string | null;
6986
+ lastSeenAt: Date | null;
6987
+ concurrentUseAt: Date | null;
6988
+ binding: "none" | "passkey";
6782
6989
  clientKind: "ios" | "android" | "web" | null;
6783
6990
  clientVersion: string | null;
6784
6991
  clientContractVersion: string | null;
@@ -6809,8 +7016,28 @@ declare class KeysRepository extends BaseRepository {
6809
7016
  * `clientSeenAt` moves only when one of the three values differs from what is
6810
7017
  * stored, so it answers "since when has this device been on this release"
6811
7018
  * rather than "when was it last seen", which lastUsedAt already answers.
7019
+ *
7020
+ * `ip` is the client address this request resolved to, or null when none did.
7021
+ * It joins this statement rather than getting one of its own: the rule the
7022
+ * concurrent-use signal needs — write the address, and move
7023
+ * `concurrentUseAt` when it differs from the stored one inside the window —
7024
+ * is the rule already implemented here, and a second fire-and-forget write
7025
+ * beside it would double the per-request write on the authenticated path. It
7026
+ * is also why the comparison is a `CASE` over the stored value rather than a
7027
+ * read followed by a write: the row comes off the read replica, and a
7028
+ * replica-lagged address compared in application code both misses real
7029
+ * switches and invents ones that did not happen.
7030
+ */
7031
+ updateLastUsedById(id: number, identity?: ClientIdentity | null, ip?: string | null): Promise<void>;
7032
+ /**
7033
+ * The boundary a previous sighting has to be newer than to count as concurrent.
7034
+ *
7035
+ * Computed here rather than written as `now() - interval` so that the moment
7036
+ * the row is stamped with and the moment the window is measured from are the
7037
+ * same one — a statement that used the database clock for the boundary and
7038
+ * ours for the value would disagree with itself by the round trip.
6812
7039
  */
6813
- updateLastUsedById(id: number, identity?: ClientIdentity | null): Promise<void>;
7040
+ private concurrentUseSince;
6814
7041
  }
6815
7042
  declare const keysRepository: KeysRepository;
6816
7043
 
@@ -7261,6 +7488,31 @@ declare class PasskeysRepository extends BaseRepository {
7261
7488
  * @returns the updated row, or null if it is not theirs or already revoked
7262
7489
  */
7263
7490
  revokeByIdAndUserId(id: number, userId: number, reason: string): Promise<Passkey | null>;
7491
+ /**
7492
+ * Mark or unmark a live credential as the owner's second factor.
7493
+ *
7494
+ * Owner-scoped and live-only, so somebody else's passkey and an already
7495
+ * revoked one are both "no such row" — the same answer every other
7496
+ * management statement here gives.
7497
+ *
7498
+ * @returns the updated row, or null if it is not theirs or already revoked
7499
+ */
7500
+ markSecondFactorByIdAndUserId(id: number, userId: number, secondFactor: boolean): Promise<Passkey | null>;
7501
+ /**
7502
+ * The account's live credentials that satisfy a step-up.
7503
+ *
7504
+ * Used to check an assertion against the marked ones only: a passkey that
7505
+ * signs the owner in is not a second factor until they say it is.
7506
+ */
7507
+ listLiveSecondFactorByUserId(userId: number): Promise<Passkey[]>;
7508
+ /**
7509
+ * Take the second-factor mark off every one of an account's credentials.
7510
+ *
7511
+ * What `mfa/disable` does to the passkey half of an enrolment. The
7512
+ * credentials themselves are untouched — they still sign the owner in.
7513
+ * Write primary.
7514
+ */
7515
+ clearSecondFactorByUserId(userId: number): Promise<void>;
7264
7516
  /**
7265
7517
  * Delete every passkey of an account (account destruction).
7266
7518
  * Write primary.
@@ -7313,6 +7565,158 @@ declare class WebAuthnChallengesRepository extends BaseRepository {
7313
7565
  }
7314
7566
  declare const webauthnChallengesRepository: WebAuthnChallengesRepository;
7315
7567
 
7568
+ /**
7569
+ * MFA TOTP Repository
7570
+ *
7571
+ * Data access for the one enrolment row an account may have. Extends
7572
+ * BaseRepository for transaction-context detection and read/write splitting.
7573
+ *
7574
+ * `secret_enc` is a ciphertext and is only ever moved through this layer as an
7575
+ * opaque string — nothing here decrypts, and nothing here logs a row.
7576
+ */
7577
+
7578
+ declare class MfaTotpRepository extends BaseRepository {
7579
+ /** The account's enrolment row, confirmed or not. */
7580
+ findByUserId(userId: number): Promise<MfaTotp | null>;
7581
+ /**
7582
+ * Start an enrolment, replacing an unconfirmed one.
7583
+ *
7584
+ * Upsert rather than insert: calling `enroll` twice is the ordinary case —
7585
+ * a user closes the app before scanning — and the second call must hand out
7586
+ * a fresh secret rather than a 409. The failed-attempt counter resets with
7587
+ * it, which is what makes "start over" the documented remedy for a row that
7588
+ * spent its five tries.
7589
+ *
7590
+ * A **confirmed** row is refused by the service before this is reached, so
7591
+ * this can never overwrite a working second factor.
7592
+ */
7593
+ startEnrolment(userId: number, secretEnc: string): Promise<MfaTotp>;
7594
+ /**
7595
+ * Mark the enrolment confirmed and record the step its first code spent.
7596
+ * Write primary.
7597
+ */
7598
+ confirm(userId: number, step: number): Promise<void>;
7599
+ /** Record the newest step this account has spent, so it cannot be replayed. */
7600
+ recordUsedStep(userId: number, step: number): Promise<void>;
7601
+ /**
7602
+ * Count one wrong code against an unconfirmed row.
7603
+ *
7604
+ * @returns the count after this attempt
7605
+ */
7606
+ countFailedConfirm(userId: number): Promise<number>;
7607
+ /** Rewrite the ciphertext in place, after a read on a retired key. */
7608
+ updateSecret(userId: number, secretEnc: string): Promise<void>;
7609
+ /** Drop the account's enrolment. Write primary. */
7610
+ deleteByUserId(userId: number): Promise<void>;
7611
+ /**
7612
+ * Drop enrolments nobody ever confirmed. The sweep job's statement.
7613
+ *
7614
+ * @returns number of rows deleted
7615
+ */
7616
+ deleteUnconfirmedBefore(cutoff: Date): Promise<number>;
7617
+ }
7618
+ declare const mfaTotpRepository: MfaTotpRepository;
7619
+
7620
+ /**
7621
+ * MFA Recovery Codes Repository
7622
+ *
7623
+ * Data access for the ten single-use codes of an account's current generation.
7624
+ * Extends BaseRepository for transaction-context detection and read/write
7625
+ * splitting.
7626
+ *
7627
+ * Every read is scoped by `(user_id, generation)`. A code from an older
7628
+ * generation, or from another account, is not "not found" by accident — it is
7629
+ * outside every query this class can make.
7630
+ */
7631
+
7632
+ declare class MfaRecoveryCodesRepository extends BaseRepository {
7633
+ /**
7634
+ * The account's newest generation, or 0 when it has never had codes.
7635
+ *
7636
+ * 0 rather than null so the caller's `+ 1` is the same expression on both
7637
+ * paths: a first enrolment writes generation 1.
7638
+ */
7639
+ currentGeneration(userId: number): Promise<number>;
7640
+ /** Write one generation of codes. Write primary. */
7641
+ createGeneration(userId: number, generation: number, hashes: string[]): Promise<void>;
7642
+ /** The account's unspent codes of one generation. */
7643
+ listUnused(userId: number, generation: number): Promise<MfaRecoveryCode[]>;
7644
+ /** How many of a generation are left, for `status` and the "2 remaining" warning. */
7645
+ countUnused(userId: number, generation: number): Promise<number>;
7646
+ /**
7647
+ * Spend one code, if this call is the one that spends it.
7648
+ *
7649
+ * One conditional UPDATE, like the WebAuthn challenge consume and for the
7650
+ * same reason: two requests arriving with the same code must produce one
7651
+ * winner, not two, and a read-then-write would let both read it as unused.
7652
+ *
7653
+ * @returns true when this call spent the row
7654
+ */
7655
+ consume(id: number): Promise<boolean>;
7656
+ /** Drop every generation the account has. Write primary. */
7657
+ deleteByUserId(userId: number): Promise<void>;
7658
+ }
7659
+ declare const mfaRecoveryCodesRepository: MfaRecoveryCodesRepository;
7660
+
7661
+ /**
7662
+ * MFA Verifications Repository
7663
+ *
7664
+ * Data access for "when did this device last prove the second factor". Extends
7665
+ * BaseRepository for transaction-context detection and read/write splitting.
7666
+ *
7667
+ * One row per device key, replaced rather than appended to: only the newest
7668
+ * verification decides anything, and a history of them would be a table that
7669
+ * grows with every step-up for no reader.
7670
+ */
7671
+
7672
+ declare class MfaVerificationsRepository extends BaseRepository {
7673
+ /** When this device last stepped up, if it ever did. */
7674
+ findByKeyId(keyId: string): Promise<MfaVerification | null>;
7675
+ /** Record a fresh verification for this device, replacing any earlier one. */
7676
+ record(keyId: string, userId: number, method: MfaVerificationMethod): Promise<void>;
7677
+ /**
7678
+ * Carry a verification across a key rotation.
7679
+ *
7680
+ * Scoped by owner, so a caller naming somebody else's key moves nothing.
7681
+ * Nothing to move is not an error: a device that never stepped up rotates
7682
+ * exactly as it does today.
7683
+ */
7684
+ moveToKeyId(userId: number, fromKeyId: string, toKeyId: string): Promise<void>;
7685
+ /** Forget every device's verification — what disabling the second factor means. */
7686
+ deleteByUserId(userId: number): Promise<void>;
7687
+ }
7688
+ declare const mfaVerificationsRepository: MfaVerificationsRepository;
7689
+
7690
+ /**
7691
+ * MFA Enrolment Repository
7692
+ *
7693
+ * One question, asked on every sensitive route and at every device
7694
+ * registration: does this account have a second factor at all?
7695
+ *
7696
+ * It spans two tables — a confirmed `mfa_totp` row, or at least one live passkey
7697
+ * the owner marked as a second factor — so it lives here rather than in either
7698
+ * table's repository, and it is deliberately **one** statement. `assertStepUp`
7699
+ * asks it first and returns immediately when the answer is no, so an account
7700
+ * that never enrolled pays one round trip against two indexed lookups
7701
+ * (`mfa_totp_user_id_idx`, unique, and `passkeys_user_id_idx`) and nothing else:
7702
+ * no second query, and no passkey configuration read.
7703
+ *
7704
+ * `UNION ALL ... LIMIT 1` rather than two `EXISTS` counts, so Postgres stops at
7705
+ * the first row either side produces. Both halves select `user_id` — a column
7706
+ * rather than a literal, so the two sides agree on a shape without a raw-SQL
7707
+ * alias, and the value is thrown away either way.
7708
+ */
7709
+
7710
+ declare class MfaEnrolmentRepository extends BaseRepository {
7711
+ /** Whether the account has a confirmed TOTP or a live second-factor passkey. */
7712
+ isEnrolled(userId: number): Promise<boolean>;
7713
+ /** The other half of the union, as its own read for `status` to reuse. */
7714
+ private liveSecondFactorPasskeys;
7715
+ /** Whether at least one live passkey is marked as a second factor. */
7716
+ hasSecondFactorPasskey(userId: number): Promise<boolean>;
7717
+ }
7718
+ declare const mfaEnrolmentRepository: MfaEnrolmentRepository;
7719
+
7316
7720
  /**
7317
7721
  * Device Authorizations Repository
7318
7722
  *
@@ -9153,6 +9557,31 @@ declare function verifyKeyFingerprint(publicKeyB64: string, expectedFingerprint:
9153
9557
  */
9154
9558
  declare function assertKeyMatchesAlgorithm(publicKeyB64: string, algorithm: KeyAlgorithmType): void;
9155
9559
 
9560
+ /**
9561
+ * Whether a key may sign a renewal — the one clause that differs from `authenticate`.
9562
+ *
9563
+ * Bound, and within its grace. Early renewal is allowed, because a browser that
9564
+ * asks a minute before its key runs out should not have to wait for the failure
9565
+ * first; late renewal is allowed for the grace window, so a laptop closed over a
9566
+ * long weekend comes back to one prompt instead of a sign-in. Past it there is
9567
+ * nothing to continue, and an unbound key was never in this ceremony at all.
9568
+ */
9569
+ declare function admitsForRenewal(key: UserPublicKey): boolean;
9570
+ /**
9571
+ * Renewal authentication middleware
9572
+ *
9573
+ * Auto-skips the global `auth` middleware, the way `optionalAuth` does: a route
9574
+ * that carries this one must not also be asked for a live key.
9575
+ *
9576
+ * @example
9577
+ * ```typescript
9578
+ * export const sessionRenewOptions = route.post('/_auth/session/renew/options')
9579
+ * .use([authenticateForRenewal])
9580
+ * .handler(async (c) => startSessionRenewService({ expiredKeyId: getAuth(c).keyId }));
9581
+ * ```
9582
+ */
9583
+ declare const authenticateForRenewal: _spfn_core_route.NamedMiddleware<"authForRenewal">;
9584
+
9156
9585
  /**
9157
9586
  * @spfn/auth - Permission Middleware
9158
9587
  *
@@ -9497,6 +9926,7 @@ declare function getUser(c: Context | {
9497
9926
  }): {
9498
9927
  email: string | null;
9499
9928
  phone: string | null;
9929
+ sessionBinding: "none" | "passkey";
9500
9930
  status: "active" | "inactive" | "suspended" | "pending_deletion" | "deleted";
9501
9931
  publicId: string;
9502
9932
  passwordChangeRequired: boolean;
@@ -9509,6 +9939,7 @@ declare function getUser(c: Context | {
9509
9939
  emailVerifiedAt: Date | null;
9510
9940
  phoneVerifiedAt: Date | null;
9511
9941
  keyEpoch: number;
9942
+ sessionBindingChangedAt: Date | null;
9512
9943
  lastLoginAt: Date | null;
9513
9944
  deletedAt: Date | null;
9514
9945
  deletedBy: string | null;
@@ -10439,8 +10870,8 @@ declare const linkMailJob: _spfn_core_job.JobDef<{
10439
10870
 
10440
10871
  /**
10441
10872
  * Build the auth job router: the account-deletion purge sweep, the OAuth 2.1
10442
- * stale-client sweep, the sign-out-everywhere link sweep, and the link-mail
10443
- * sender.
10873
+ * stale-client sweep, the sign-out-everywhere link sweep, the abandoned
10874
+ * second-factor enrolment sweep, and the link-mail sender.
10444
10875
  *
10445
10876
  * The cron is a parameter because `job(...).cron(expression)` bakes the string in
10446
10877
  * at module-import time, which always happens before `createAuthLifecycle()` runs
@@ -10451,15 +10882,18 @@ declare const linkMailJob: _spfn_core_job.JobDef<{
10451
10882
  * @param options.purgeCron - Deletion purge schedule; defaults to daily at 04:00
10452
10883
  * @param options.oauth2ClientPurgeCron - Stale OAuth client sweep; defaults to daily at 05:00
10453
10884
  * @param options.revokeAllTokenPurgeCron - Sign-out-everywhere link sweep; defaults to daily at 06:00
10885
+ * @param options.mfaSweepCron - Abandoned second-factor enrolment sweep; defaults to daily at 07:00
10454
10886
  */
10455
10887
  declare function createAuthJobRouter(options?: {
10456
10888
  purgeCron?: string;
10457
10889
  oauth2ClientPurgeCron?: string;
10458
10890
  revokeAllTokenPurgeCron?: string;
10891
+ mfaSweepCron?: string;
10459
10892
  }): _spfn_core_job.JobRouter<{
10460
10893
  deletionPurge: _spfn_core_job.JobDef<void, void>;
10461
10894
  oauth2ClientPurge: _spfn_core_job.JobDef<void, void>;
10462
10895
  revokeAllTokenPurge: _spfn_core_job.JobDef<void, void>;
10896
+ mfaSweep: _spfn_core_job.JobDef<void, void>;
10463
10897
  linkMail: _spfn_core_job.JobDef<{
10464
10898
  kind: "signup-link";
10465
10899
  rowId: number;
@@ -10480,6 +10914,7 @@ declare const authJobRouter: _spfn_core_job.JobRouter<{
10480
10914
  deletionPurge: _spfn_core_job.JobDef<void, void>;
10481
10915
  oauth2ClientPurge: _spfn_core_job.JobDef<void, void>;
10482
10916
  revokeAllTokenPurge: _spfn_core_job.JobDef<void, void>;
10917
+ mfaSweep: _spfn_core_job.JobDef<void, void>;
10483
10918
  linkMail: _spfn_core_job.JobDef<{
10484
10919
  kind: "signup-link";
10485
10920
  rowId: number;
@@ -10499,4 +10934,4 @@ declare const authJobRouter: _spfn_core_job.JobRouter<{
10499
10934
  */
10500
10935
  declare const createAuthDeletionJobRouter: typeof createAuthJobRouter;
10501
10936
 
10502
- export { AUTHORIZE_PATH, type AccountDeletionPurgeUser, type AccountDeletionRequest, AccountDeletionRequestedBy, AccountDeletionRequestsRepository, type AuthConfig, AuthContext, type AuthCsrfConfig, type AuthDeletionConfig, type AuthDeviceAuthConfig, type AuthLifecycleConfig, type AuthLifecycleOptions, type AuthMetadataEntity, AuthMetadataRepository, type AuthorizationServerConfig, type AuthorizationServerOptions, type BeforeRegisterContext, COOKIE_NAMES, CSRF_HEADER, type CancelAccountDeletionParams, type CancelAccountDeletionResult, type CreateOAuthStateParams, type CreateRevokeAllLinkOptions, type CsrfMode, DEFAULT_ACCESS_TOKEN_TTL_MS, DEFAULT_CODE_TTL_MS, DEFAULT_DELETION_ALLOW_SELF_IMMEDIATE, DEFAULT_DELETION_GRACE_PERIOD_DAYS, DEFAULT_DELETION_PURGE_CRON, DEFAULT_DELETION_PURGE_STRATEGY, DEFAULT_DELETION_SEND_NOTIFICATIONS, DEFAULT_DEVICE_AUTH_INTERVAL_MS, DEFAULT_DEVICE_AUTH_TTL_MS, DEFAULT_REFRESH_TOKEN_TTL_MS, DEFAULT_REVOKE_ALL_TOKEN_PURGE_CRON, DEVICE_AUTH_STATUSES, type DecryptedOAuthToken, type DeviceAuthStatus, type DeviceAuthorization, DeviceAuthorizationsRepository, type EmailNormalizationResult, EnvironmentKeyringTokenCipher, type GoogleTokenResponse, type GoogleUserInfo, type Invitation, InvitationStatus, InvitationsRepository, type IssuedOpsToken, KeyAlgorithmType, type KeyRevokeAllToken, KeyRevokeAllTokensRepository, KeysRepository, type LiveRevokeAllLink, MAX_UNGRANTED_CLIENTS_PER_IP, type NewAccountDeletionRequest, type NewAuthMetadataEntity, type NewDeviceAuthorization, type NewInvitation, type NewKeyRevokeAllToken, type NewOAuth2AuthorizationCode, type NewOAuth2Client, type NewOAuth2Grant, type NewOAuth2Token, type NewOpsToken, NewPasskey, type NewPasswordResetToken, type NewPermission, type NewPermissionEntity, type NewRole, type NewRoleEntity, type NewRolePermission, type NewSignupLinkToken, type NewUser, type NewUserPermission, type NewUserProfile, type NewUserPublicKey, type NewUserSocialAccount, type NewVerificationCode, type NewWebAuthnChallenge, OAUTH2_ACCESS_TOKEN_PREFIX, OAUTH2_REFRESH_TOKEN_PREFIX, OAUTH2_TOKEN_KINDS, type OAuth2AuthorizationCode, OAuth2AuthorizationCodesRepository, type OAuth2Client, OAuth2ClientsRepository, type OAuth2Grant, type OAuth2GrantWithClient, OAuth2GrantsRepository, type OAuth2RegisterRefusal, type OAuth2RegisterRequest, type OAuth2RegisterResult, type OAuth2RegisteredClient, type OAuth2Token, type OAuth2TokenKind, type OAuth2TokenRefusal, type OAuth2TokenRequest, type OAuth2TokenResponse, type OAuth2TokenResult, OAuth2TokensRepository, OAuthProvider, type OAuthState, type OAuthTokenCipher, type OAuthTokenContext, type OAuthTokenType, OPS_TOKEN_PREFIX, type OpsOrUserConfig, type OpsToken, OpsTokensRepository, Passkey, type PasskeyConfig, type PasskeyUserVerification, PasskeysRepository, type PasswordResetToken, PasswordResetTokensRepository, type PendingDeletionInfo, type Permission, type PermissionEntity, PermissionsRepository, PurgeStrategy, type PurgeUserResult, type RedirectUriRefusal, type RegisterChannel, type RequestAccountDeletionParams, type RequestAccountDeletionResult, type RevokeAllLink, type RevokeAllLinkDescription, type RevokedKey, type Role, type RoleEntity, type RoleGuardOptions, type RolePermission, RolePermissionsRepository, RolesRepository, STALE_CLIENT_MAX_AGE_MS, SUPPORTED_GRANT_TYPES, SUPPORTED_RESPONSE_TYPES, type SessionPayload, type SignupLinkToken, SignupLinkTokensRepository, SocialAccountsRepository, SocialProvider, type SweepDuePurgesResult, type TokenPayload, USER_CODE_ALPHABET, USER_CODE_LENGTH, type UpdateProfileParams, type User, type UserPermission, UserPermissionsRepository, type UserProfile, UserProfilesRepository, type UserPublicKey, type UserSocialAccount, UsersRepository, type VerificationCode, VerificationCodesRepository, VerificationPurpose, type VerifiedOAuth2AccessToken, type VerifiedOpsToken, type VerifyIdTokenParams, WEBAUTHN_CHALLENGE_KINDS, type WebAuthnChallenge, type WebAuthnChallengeKind, WebAuthnChallengesRepository, acceptInvitation, accountDeletionRequests, accountDeletionRequestsRepository, addPermissionToRole, appleProvider, assertAuthorizationServerIssuer, assertCanAssignRole, assertKeyMatchesAlgorithm, assertPasskeyConfig, authJobRouter, authLogger, authMetadata, authMetadataRepository, authSchema, cancelAccountDeletionService, cancelInvitation, checkUsernameAvailableService, configureAuth, configureAuthorizationServer, configureDeletion, configureDeviceAuth, configureOAuthTokenCipher, consumeRevokeAllLink, createAuthDeletionJobRouter, createAuthDeletionPurgeJob, createAuthJobRouter, createAuthLifecycle, createInvitation, createOAuthState, createRevokeAllLink, createRevokeAllTokenPurgeJob, createRole, decodeToken, decryptToken, deleteInvitation, deleteRole, deriveCsrfToken, describeRevokeAllLink, deviceAuthorizations, deviceAuthorizationsRepository, encryptToken, exchangeCodeForTokens, expireOldInvitations, formatUserCode, generateAccessToken, generateAuthorizationCode, generateDeviceCode, generateOAuthNonce, generateRefreshToken, generateToken, generateUserCode, getAllRoles, getAuth, getAuthConfig, getAuthSessionService, getAuthorizationServerConfig, getCsrfExemptPaths, getCsrfMode, getDeletionConfig, getDeviceAuthConfig, getDummyPasswordHash, getGoogleAuthUrl, getGoogleOAuthConfig, getGoogleUserInfo, getInvitationByToken, getInvitationWithDetails, getKeyId, getLocale, getOneTimeTokenManager, getOpsToken, getOptionalAuth, getPasskeyConfig, getPendingDeletionInfo, getRole, getRoleByName, getRolePermissions, getSessionTtl, getUser, getUserByEmailService, getUserByIdService, getUserByPhoneService, getUserId, getUserPermissions, getUserProfileService, getUserRole, githubProvider, googleProvider, hasAllPermissions, hasAnyPermission, hasAnyRole, hasPermission, hasRole, hashDeviceCode, hashOAuth2Secret, hashPassword, initOneTimeTokenManager, initializeAuth, invitationsRepository, isAccessTokenShaped, isEncrypted, isGoogleOAuthEnabled, isLoopbackHostname, isOpsToken, isPkceS256ChallengeShaped, isPkceVerifierShaped, issueOpsTokenService, kakaoProvider, keyRevokeAllTokens, keyRevokeAllTokensRepository, keysRepository, linkMailJob, listInvitations, listOpsTokensService, matchOAuthCsrfCookies, matchesCsrfToken, matchesRegisteredRedirectUri, naverProvider, normalizeEmail, normalizeOptionalEmail, normalizeResource, normalizeStoredEmails, normalizeUserCode, oauth2AuthorizationCodes, oauth2AuthorizationCodesRepository, oauth2Clients, oauth2ClientsRepository, oauth2Grants, oauth2GrantsRepository, oauth2TokenService, oauth2Tokens, oauth2TokensRepository, oneTimeTokenAuth, opsOrUser, opsTokenAuth, opsTokens, opsTokensRepository, parseDuration, passkeysRepository, passwordResetTokens, passwordResetTokensRepository, permissions, permissionsRepository, pkceChallengeFor, purgeRevokeAllTokensService, purgeStaleOAuth2ClientsService, purgeUserService, redirectHostOf, refreshAccessToken, refuseRedirectUriRegistration, registerOAuth2ClientService, removePermissionFromRole, requestAccountDeletionService, requireAnyPermission, requireOpsScope, requirePermissions, requireRole, resendInvitation, resolveIssuerSource, revokeOAuth2TokenService, revokeOpsTokenService, roleGuard, rolePermissions, rolePermissionsRepository, roles, rolesRepository, runBeforeRegister, sameOAuth2Hash, sameResource, secondsUntil, setRolePermissions, signupLinkTokens, signupLinkTokensRepository, socialAccountsRepository, sweepDuePurges, toEpochSeconds, updateLastLoginService, updateLocaleService, updateRole, updateUserProfileService, updateUserService, updateUsernameService, userInvitations, userPermissions, userPermissionsRepository, userProfiles, userProfilesRepository, userPublicKeys, userSocialAccounts, users, usersRepository, validateInvitation, validatePasswordStrength, verificationCodes, verificationCodesRepository, verifyAccessToken, verifyClientToken, verifyIdToken, verifyKeyFingerprint, verifyOAuthState, verifyOpsTokenService, verifyPassword, verifyToken, webauthnChallenges, webauthnChallengesRepository };
10937
+ export { AUTHORIZE_PATH, type AccountDeletionPurgeUser, type AccountDeletionRequest, AccountDeletionRequestedBy, AccountDeletionRequestsRepository, type AuthConfig, AuthContext, type AuthCsrfConfig, type AuthDeletionConfig, type AuthDeviceAuthConfig, type AuthLifecycleConfig, type AuthLifecycleOptions, type AuthMetadataEntity, AuthMetadataRepository, type AuthorizationServerConfig, type AuthorizationServerOptions, type BeforeRegisterContext, COOKIE_NAMES, CSRF_HEADER, type CancelAccountDeletionParams, type CancelAccountDeletionResult, type CreateOAuthStateParams, type CreateRevokeAllLinkOptions, type CsrfMode, DEFAULT_ACCESS_TOKEN_TTL_MS, DEFAULT_CODE_TTL_MS, DEFAULT_DELETION_ALLOW_SELF_IMMEDIATE, DEFAULT_DELETION_GRACE_PERIOD_DAYS, DEFAULT_DELETION_PURGE_CRON, DEFAULT_DELETION_PURGE_STRATEGY, DEFAULT_DELETION_SEND_NOTIFICATIONS, DEFAULT_DEVICE_AUTH_INTERVAL_MS, DEFAULT_DEVICE_AUTH_TTL_MS, DEFAULT_REFRESH_TOKEN_TTL_MS, DEFAULT_REVOKE_ALL_TOKEN_PURGE_CRON, DEVICE_AUTH_STATUSES, type DecryptedOAuthToken, type DeviceAuthStatus, type DeviceAuthorization, DeviceAuthorizationsRepository, type EmailNormalizationResult, EnvironmentKeyringTokenCipher, type GoogleTokenResponse, type GoogleUserInfo, type Invitation, InvitationStatus, InvitationsRepository, type IssuedOpsToken, KeyAlgorithmType, type KeyRevokeAllToken, KeyRevokeAllTokensRepository, KeysRepository, type LiveRevokeAllLink, MAX_UNGRANTED_CLIENTS_PER_IP, MFA_CONFIRM_ATTEMPT_LIMIT, type MfaConfig, MfaEnrolmentRepository, type MfaRecoveryCode, MfaRecoveryCodesRepository, type MfaTotp, MfaTotpRepository, MfaVerification, MfaVerificationMethod, MfaVerificationsRepository, type NewAccountDeletionRequest, type NewAuthMetadataEntity, type NewDeviceAuthorization, type NewInvitation, type NewKeyRevokeAllToken, type NewMfaRecoveryCode, type NewMfaTotp, type NewOAuth2AuthorizationCode, type NewOAuth2Client, type NewOAuth2Grant, type NewOAuth2Token, type NewOpsToken, NewPasskey, type NewPasswordResetToken, type NewPermission, type NewPermissionEntity, type NewRole, type NewRoleEntity, type NewRolePermission, type NewSignupLinkToken, type NewUser, type NewUserPermission, type NewUserProfile, NewUserPublicKey, type NewUserSocialAccount, type NewVerificationCode, type NewWebAuthnChallenge, OAUTH2_ACCESS_TOKEN_PREFIX, OAUTH2_REFRESH_TOKEN_PREFIX, OAUTH2_TOKEN_KINDS, type OAuth2AuthorizationCode, OAuth2AuthorizationCodesRepository, type OAuth2Client, OAuth2ClientsRepository, type OAuth2Grant, type OAuth2GrantWithClient, OAuth2GrantsRepository, type OAuth2RegisterRefusal, type OAuth2RegisterRequest, type OAuth2RegisterResult, type OAuth2RegisteredClient, type OAuth2Token, type OAuth2TokenKind, type OAuth2TokenRefusal, type OAuth2TokenRequest, type OAuth2TokenResponse, type OAuth2TokenResult, OAuth2TokensRepository, OAuthProvider, type OAuthState, type OAuthTokenCipher, type OAuthTokenContext, type OAuthTokenType, OPS_TOKEN_PREFIX, type OpsOrUserConfig, type OpsToken, OpsTokensRepository, Passkey, type PasskeyConfig, type PasskeyUserVerification, PasskeysRepository, type PasswordResetToken, PasswordResetTokensRepository, type PendingDeletionInfo, type Permission, type PermissionEntity, PermissionsRepository, PurgeStrategy, type PurgeUserResult, type RedirectUriRefusal, type RegisterChannel, type RequestAccountDeletionParams, type RequestAccountDeletionResult, type RevokeAllLink, type RevokeAllLinkDescription, type RevokedKey, type Role, type RoleEntity, type RoleGuardOptions, type RolePermission, RolePermissionsRepository, RolesRepository, STALE_CLIENT_MAX_AGE_MS, SUPPORTED_GRANT_TYPES, SUPPORTED_RESPONSE_TYPES, type SessionPayload, type SignupLinkToken, SignupLinkTokensRepository, SocialAccountsRepository, SocialProvider, type SweepDuePurgesResult, type TokenEncryptionKey, type TokenPayload, USER_CODE_ALPHABET, USER_CODE_LENGTH, type UpdateProfileParams, type User, type UserPermission, UserPermissionsRepository, type UserProfile, UserProfilesRepository, UserPublicKey, type UserSocialAccount, UsersRepository, V2_PREFIX, type VerificationCode, VerificationCodesRepository, VerificationPurpose, type VerifiedOAuth2AccessToken, type VerifiedOpsToken, type VerifyIdTokenParams, WEBAUTHN_CHALLENGE_KINDS, type WebAuthnChallenge, type WebAuthnChallengeKind, WebAuthnChallengesRepository, acceptInvitation, accountDeletionRequests, accountDeletionRequestsRepository, addPermissionToRole, admitsForRenewal, appleProvider, assertAuthorizationServerIssuer, assertCanAssignRole, assertKeyMatchesAlgorithm, assertPasskeyConfig, authJobRouter, authLogger, authMetadata, authMetadataRepository, authSchema, authenticateForRenewal, cancelAccountDeletionService, cancelInvitation, checkUsernameAvailableService, configureAuth, configureAuthorizationServer, configureDeletion, configureDeviceAuth, configureOAuthTokenCipher, consumeRevokeAllLink, createAuthDeletionJobRouter, createAuthDeletionPurgeJob, createAuthJobRouter, createAuthLifecycle, createInvitation, createOAuthState, createRevokeAllLink, createRevokeAllTokenPurgeJob, createRole, decodeToken, decryptToken, deleteInvitation, deleteRole, deriveCsrfToken, describeRevokeAllLink, deviceAuthorizations, deviceAuthorizationsRepository, encryptToken, exchangeCodeForTokens, expireOldInvitations, formatUserCode, generateAccessToken, generateAuthorizationCode, generateDeviceCode, generateOAuthNonce, generateRefreshToken, generateToken, generateUserCode, getAllRoles, getAuth, getAuthConfig, getAuthSessionService, getAuthorizationServerConfig, getBoundKeyRenewGraceMs, getBoundKeyTtlMs, getConcurrentUseWindowMs, getCsrfExemptPaths, getCsrfMode, getDeletionConfig, getDeviceAuthConfig, getDummyPasswordHash, getEncryptionKeyring, getGoogleAuthUrl, getGoogleOAuthConfig, getGoogleUserInfo, getInvitationByToken, getInvitationWithDetails, getKeyId, getLocale, getMfaConfig, getOneTimeTokenManager, getOpsToken, getOptionalAuth, getPasskeyConfig, getPendingDeletionInfo, getRole, getRoleByName, getRolePermissions, getSessionRenewPath, getSessionTtl, getUser, getUserByEmailService, getUserByIdService, getUserByPhoneService, getUserId, getUserPermissions, getUserProfileService, getUserRole, githubProvider, googleProvider, hasAllPermissions, hasAnyPermission, hasAnyRole, hasPermission, hasRole, hashDeviceCode, hashOAuth2Secret, hashPassword, initOneTimeTokenManager, initializeAuth, invitationsRepository, isAccessTokenShaped, isEncrypted, isGoogleOAuthEnabled, isLoopbackHostname, isOpsToken, isPkceS256ChallengeShaped, isPkceVerifierShaped, issueOpsTokenService, kakaoProvider, keyRevokeAllTokens, keyRevokeAllTokensRepository, keysRepository, linkMailJob, listInvitations, listOpsTokensService, matchOAuthCsrfCookies, matchesCsrfToken, matchesRegisteredRedirectUri, mfaEnrolmentRepository, mfaRecoveryCodes, mfaRecoveryCodesRepository, mfaTotp, mfaTotpRepository, mfaVerificationsRepository, naverProvider, normalizeEmail, normalizeOptionalEmail, normalizeResource, normalizeStoredEmails, normalizeUserCode, oauth2AuthorizationCodes, oauth2AuthorizationCodesRepository, oauth2Clients, oauth2ClientsRepository, oauth2Grants, oauth2GrantsRepository, oauth2TokenService, oauth2Tokens, oauth2TokensRepository, oneTimeTokenAuth, opsOrUser, opsTokenAuth, opsTokens, opsTokensRepository, parseDuration, passkeysRepository, passwordResetTokens, passwordResetTokensRepository, permissions, permissionsRepository, pkceChallengeFor, purgeRevokeAllTokensService, purgeStaleOAuth2ClientsService, purgeUserService, redirectHostOf, refreshAccessToken, refuseRedirectUriRegistration, registerOAuth2ClientService, removePermissionFromRole, requestAccountDeletionService, requireAnyPermission, requireOpsScope, requirePermissions, requireRole, resendInvitation, resolveIssuerSource, revokeOAuth2TokenService, revokeOpsTokenService, roleGuard, rolePermissions, rolePermissionsRepository, roles, rolesRepository, runBeforeRegister, sameOAuth2Hash, sameResource, secondsUntil, setRolePermissions, signupLinkTokens, signupLinkTokensRepository, socialAccountsRepository, sweepDuePurges, toEpochSeconds, updateLastLoginService, updateLocaleService, updateRole, updateUserProfileService, updateUserService, updateUsernameService, userInvitations, userPermissions, userPermissionsRepository, userProfiles, userProfilesRepository, userSocialAccounts, users, usersRepository, validateInvitation, validatePasswordStrength, verificationCodes, verificationCodesRepository, verifyAccessToken, verifyClientToken, verifyIdToken, verifyKeyFingerprint, verifyOAuthState, verifyOpsTokenService, verifyPassword, verifyToken, webauthnChallenges, webauthnChallengesRepository };