@spfn/auth 0.3.0-beta.17 → 0.3.0-beta.19

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/server.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { A as AuthInitOptions, r as OAuthProvider, p as VerificationPurpose, N as NewPasskey, s as Passkey, n as PermissionCategory, t as AuthContext } from './machine-principals-nrpFSvvB.js';
2
- export { u as ApproveDeviceAuthParams, v as AuthProfileOutcome, w as AuthProfileVerifier, x as ChangePasswordParams, y as CompletePasswordResetParams, z as CompleteSignupParams, B as ConfirmPasswordResetParams, d as ConfirmPasswordResetResult, E as ConfirmSignupLinkParams, C as ConfirmSignupLinkResult, G as DenyDeviceAuthParams, H as DeviceAuthApprovedResult, J as DeviceAuthInfoParams, D as DeviceAuthInfoResult, M as DeviceAuthPendingResult, Q as DeviceAuthPollResponseSchema, T as DeviceNameSchema, W as EmailSchema, X as FingerprintSchema, Y as FinishPasskeyEnrollmentParams, F as FinishPasskeyEnrollmentResult, Z as FinishPasskeyLoginParams, I as IssueOneTimeTokenResult, _ as KEY_FINGERPRINT_PREFIX_LENGTH, $ as KeyIdSchema, K as KeySummary, a0 as LoginParams, L as LoginResult, a1 as LogoutParams, a2 as MachinePrincipal, a3 as MachineVerifierRegistration, a4 as NativeVerifyOptions, a5 as NormalizedIdentity, a6 as OAuthCallbackParams, a7 as OAuthCallbackResult, a8 as OAuthCodeExchangeOptions, a9 as OAuthNativeParams, i as OAuthNativeResult, aa as OAuthStartParams, O as OAuthStartResult, ab as OAuthTokens, ac as PASSKEY_DEVICE_TYPES, ad as PASSKEY_LABEL_MAX_LENGTH, ae as PasskeyDeviceType, f as PasskeySummary, af as PasswordSchema, ag as PhoneSchema, ah as PlatformSchema, ai as PollDeviceAuthParams, aj as PollDeviceAuthResult, ak as PublicKeySchema, al as RecentAuthenticationParams, am as RegisterParams, an as RegisterPublicKeyParams, a as RegisterResult, ao as RenamePasskeyParams, ap as RequestPasswordResetParams, c as RequestPasswordResetResult, aq as RequestSignupLinkParams, b as RequestSignupLinkResult, ar as RevokeAllKeysParams, h as RevokeAllKeysResult, as as RevokeKeyParams, at as RevokePasskeyParams, au as RotateKeyParams, g as RotateKeyResult, av as SendVerificationCodeParams, S as SendVerificationCodeResult, aw as StartDeviceAuthParams, e as StartDeviceAuthResult, ax as StartPasskeyEnrollmentParams, ay as TargetTypeSchema, az as UnlinkNotification, aA as UnlinkNotifyRejection, aB as UnlinkNotifyRequest, aC as UnlinkNotifyResult, aD as UserCodeSchema, V as VERIFICATION_PURPOSES, o as VERIFICATION_TARGET_TYPES, aE as VerificationPurposeSchema, q as VerificationTargetType, aF as VerifyCodeParams, aG as VerifyCodeResult, aH as approveDeviceAuthService, aI as assertNotLastRecoveryCredential, aJ as assertRecentAuthentication, m as authRouter, aK as authenticate, aL as buildOAuthErrorUrl, aM as changePasswordService, aN as completePasswordResetService, aO as completeSignupService, aP as confirmPasswordResetService, aQ as confirmSignupLinkService, aR as denyDeviceAuthService, aS as finishPasskeyEnrollmentService, aT as finishPasskeyLoginService, aU as getDeviceAuthInfoService, aV as getEnabledOAuthProviders, aW as getGoogleAccessToken, aX as getMachinePrincipal, aY as getOAuthProvider, aZ as getRegisteredProviders, a_ as isOAuthProviderEnabled, a$ as issueOneTimeTokenService, b0 as listKeysService, b1 as listPasskeysService, b2 as loginService, b3 as logoutService, b4 as machineAuth, b5 as oauthCallbackService, b6 as oauthNativeService, b7 as oauthStartService, b8 as oauthUnlinkNotifyService, b9 as optionalAuth, ba as passkeys, bb as pollDeviceAuthService, bc as registerAuthProfile, bd as registerMachineVerifier, be as registerOAuthProvider, bf as registerPublicKeyService, bg as registerService, bh as renamePasskeyService, bi as requestPasswordResetService, bj as requestSignupLinkService, bk as requireEnabledProvider, bl as requireMachineScope, bm as resolveAuthenticatedUser, bn as revokeAllKeysService, bo as revokeKeyService, bp as revokePasskeyService, bq as rotateKeyService, br as runAuthProfile, bs as selectAuthProfile, bt as sendVerificationCodeService, bu as startDeviceAuthService, bv as startPasskeyEnrollmentService, bw as startPasskeyLoginService, bx as verifyCodeService, by as verifyOneTimeTokenService } from './machine-principals-nrpFSvvB.js';
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-BD4tnASp.js';
2
+ export { x as ApproveDeviceAuthParams, y as AuthProfileOutcome, z as AuthProfileVerifier, B as ChangePasswordParams, E as CompletePasswordResetParams, G as CompleteSignupParams, H as ConfirmPasswordResetParams, d as ConfirmPasswordResetResult, J as ConfirmSignupLinkParams, C as ConfirmSignupLinkResult, M as DenyDeviceAuthParams, Q as DeviceAuthApprovedResult, T as DeviceAuthInfoParams, D as DeviceAuthInfoResult, W as DeviceAuthPendingResult, X as DeviceAuthPollResponseSchema, Y as DeviceNameSchema, Z as EmailSchema, _ as FingerprintSchema, $ as FinishPasskeyEnrollmentParams, F as FinishPasskeyEnrollmentResult, a0 as FinishPasskeyLoginParams, I as IssueOneTimeTokenResult, a1 as KEY_FINGERPRINT_PREFIX_LENGTH, a2 as KeyIdSchema, K as KeySummary, a3 as LoginParams, L as LoginResult, a4 as LogoutParams, a5 as MachinePrincipal, a6 as MachineVerifierRegistration, a7 as NativeVerifyOptions, a8 as NormalizedIdentity, l as OAuth2AuthorizationCodeIssued, a9 as OAuth2AuthorizeParams, k as OAuth2ConsentView, n as OAuth2GrantSummary, aa as OAuth2ScopeDescription, ab as OAuthCallbackParams, ac as OAuthCallbackResult, ad as OAuthCodeExchangeOptions, ae as OAuthNativeParams, i as OAuthNativeResult, af as OAuthStartParams, O as OAuthStartResult, ag as OAuthTokens, ah as PASSKEY_DEVICE_TYPES, ai as PASSKEY_LABEL_MAX_LENGTH, aj as PasskeyDeviceType, f as PasskeySummary, ak as PasswordSchema, al as PhoneSchema, am as PlatformSchema, an as PollDeviceAuthParams, ao as PollDeviceAuthResult, ap as PublicKeySchema, aq as RecentAuthenticationParams, ar as RegisterParams, as as RegisterPublicKeyParams, a as RegisterResult, at as RenamePasskeyParams, au as RequestPasswordResetParams, c as RequestPasswordResetResult, av as RequestSignupLinkParams, b as RequestSignupLinkResult, aw as RevokeAllKeysParams, h as RevokeAllKeysResult, ax as RevokeKeyParams, ay as RevokePasskeyParams, az as RotateKeyParams, g as RotateKeyResult, aA as SendVerificationCodeParams, S as SendVerificationCodeResult, aB as StartDeviceAuthParams, e as StartDeviceAuthResult, aC as StartPasskeyEnrollmentParams, aD as TargetTypeSchema, aE as UnlinkNotification, aF as UnlinkNotifyRejection, aG as UnlinkNotifyRequest, aH as UnlinkNotifyResult, aI as UserCodeSchema, V as VERIFICATION_PURPOSES, r as VERIFICATION_TARGET_TYPES, aJ as VerificationPurposeSchema, t as VerificationTargetType, aK as VerifyCodeParams, aL as VerifyCodeResult, aM as approveDeviceAuthService, aN as approveOAuth2AuthorizeService, aO as assertNotLastRecoveryCredential, aP as assertRecentAuthentication, m as authRouter, aQ as authenticate, aR as buildOAuthErrorUrl, aS as changePasswordService, aT as completePasswordResetService, aU as completeSignupService, aV as confirmPasswordResetService, aW as confirmSignupLinkService, aX as denyDeviceAuthService, aY as denyOAuth2AuthorizeService, aZ as describeOAuth2AuthorizeRequestService, a_ as finishPasskeyEnrollmentService, a$ as finishPasskeyLoginService, b0 as getDeviceAuthInfoService, b1 as getEnabledOAuthProviders, b2 as getGoogleAccessToken, b3 as getMachinePrincipal, b4 as getOAuthProvider, b5 as getRegisteredProviders, b6 as isOAuthProviderEnabled, b7 as issueOneTimeTokenService, b8 as listKeysService, b9 as listOAuth2GrantsService, ba as listPasskeysService, bb as loginService, bc as logoutService, bd as machineAuth, be as oauthCallbackService, bf as oauthNativeService, bg as oauthStartService, bh as oauthUnlinkNotifyService, bi as optionalAuth, bj as passkeys, bk as pollDeviceAuthService, bl as registerAuthProfile, bm as registerMachineVerifier, bn as registerOAuthProvider, bo as registerPublicKeyService, bp as registerService, bq as renamePasskeyService, br as requestPasswordResetService, bs as requestSignupLinkService, bt as requireEnabledProvider, bu as requireMachineScope, bv as resolveAuthenticatedUser, bw as revokeAllKeysService, bx as revokeAllOAuth2GrantsForUser, by as revokeKeyService, bz as revokeOAuth2GrantService, bA as revokePasskeyService, bB as rotateKeyService, bC as runAuthProfile, bD as selectAuthProfile, bE as sendVerificationCodeService, bF as startDeviceAuthService, bG as startPasskeyEnrollmentService, bH as startPasskeyLoginService, bI as verifyCodeService, bJ as verifyOneTimeTokenService } from './machine-principals-BD4tnASp.js';
3
3
  import * as drizzle_orm_pg_core from 'drizzle-orm/pg-core';
4
4
  import { j as SocialProvider, K as KeyAlgorithmType, d as InvitationStatus, c as AccountDeletionRequestedBy, i as PurgeStrategy } from './types-DYyhze28.js';
5
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';
@@ -2478,6 +2478,227 @@ declare function verifyOpsTokenService(token: string): Promise<VerifiedOpsToken
2478
2478
  declare function revokeOpsTokenService(id: number): Promise<OpsToken | null>;
2479
2479
  declare function listOpsTokensService(): Promise<OpsToken[]>;
2480
2480
 
2481
+ /**
2482
+ * OAuth 2.1 Client Service
2483
+ *
2484
+ * RFC 7591 dynamic client registration, for public clients and nothing else.
2485
+ * `claude mcp add` and `codex mcp add` both begin here: the CLI discovers the
2486
+ * authorization server, registers itself unauthenticated, and gets back a
2487
+ * `client_id` it keeps on the user's disk.
2488
+ *
2489
+ * Unauthenticated registration means the table is a place anyone can write, so
2490
+ * two things bound it. The route rate-limits by IP, and this service caps how
2491
+ * many clients one IP may have standing that nobody has approved — a rate limit
2492
+ * alone would let an IP accumulate rows forever at a slow enough pace, because a
2493
+ * row costs nothing to make and lives until a job sweeps it. The cap looks back
2494
+ * an hour rather than over all time: it is a bound on how much junk one address
2495
+ * may have standing at once, not a lifetime quota on how many CLIs somebody may
2496
+ * ever connect.
2497
+ *
2498
+ * Every refusal here is RFC 7591 §3.2.2 shaped (`invalid_redirect_uri`,
2499
+ * `invalid_client_metadata`) rather than thrown, because the client reading it
2500
+ * is an OAuth client library that knows those two words and not this
2501
+ * application's error envelope. The route renders the shape; this decides it.
2502
+ */
2503
+ /**
2504
+ * How many unapproved clients one IP may have standing.
2505
+ *
2506
+ * Generous against real use — a developer registering the same CLI on four
2507
+ * projects in an afternoon, each abandoned at the consent screen, is nowhere
2508
+ * near it — and small enough that the table cannot be filled from one address
2509
+ * between two sweeps of the purge job.
2510
+ */
2511
+ declare const MAX_UNGRANTED_CLIENTS_PER_IP = 20;
2512
+ /** How long a client nobody approved is kept before the purge job deletes it. */
2513
+ declare const STALE_CLIENT_MAX_AGE_MS: number;
2514
+ /** The only grant types this authorization server issues. */
2515
+ declare const SUPPORTED_GRANT_TYPES: string[];
2516
+ /** The only response type. There is no implicit flow here. */
2517
+ declare const SUPPORTED_RESPONSE_TYPES: string[];
2518
+ /** Registration metadata as RFC 7591 names it, before anything is trusted. */
2519
+ interface OAuth2RegisterRequest {
2520
+ redirect_uris?: unknown;
2521
+ client_name?: unknown;
2522
+ token_endpoint_auth_method?: unknown;
2523
+ grant_types?: unknown;
2524
+ response_types?: unknown;
2525
+ }
2526
+ /** RFC 7591 §3.2.1 registration response. */
2527
+ interface OAuth2RegisteredClient {
2528
+ client_id: string;
2529
+ client_id_issued_at: number;
2530
+ client_name: string;
2531
+ redirect_uris: string[];
2532
+ token_endpoint_auth_method: string;
2533
+ grant_types: string[];
2534
+ response_types: string[];
2535
+ }
2536
+ /** A refusal in the shape the client's own OAuth library will read. */
2537
+ interface OAuth2RegisterRefusal {
2538
+ ok: false;
2539
+ status: 400 | 429;
2540
+ error: string;
2541
+ description: string;
2542
+ }
2543
+ type OAuth2RegisterResult = {
2544
+ ok: true;
2545
+ client: OAuth2RegisteredClient;
2546
+ } | OAuth2RegisterRefusal;
2547
+ /**
2548
+ * Register a public client.
2549
+ *
2550
+ * @param request - Metadata exactly as it arrived; nothing here is trusted
2551
+ * @param clientIp - Client IP for the standing cap, or null when unknowable
2552
+ */
2553
+ declare function registerOAuth2ClientService(request: OAuth2RegisterRequest, clientIp: string | null): Promise<OAuth2RegisterResult>;
2554
+ /**
2555
+ * Delete clients nobody approved within a day — the `auth.oauth2.client-purge`
2556
+ * sweep.
2557
+ *
2558
+ * @returns how many rows the sweep removed
2559
+ */
2560
+ declare function purgeStaleOAuth2ClientsService(): Promise<{
2561
+ deleted: number;
2562
+ }>;
2563
+
2564
+ /**
2565
+ * OAuth 2.1 Token Service
2566
+ *
2567
+ * The token endpoint's two grant types, and RFC 7009 revocation.
2568
+ *
2569
+ * Every failure on this endpoint answers `invalid_grant` — an unknown code, a
2570
+ * spent one, an expired one, a mismatched verifier, a code minted for another
2571
+ * client, a refresh token that never existed, one that was rotated away. That is
2572
+ * not laziness with error messages: the endpoint is public and unauthenticated,
2573
+ * and an error that distinguished "this code does not exist" from "this code
2574
+ * exists but is not yours" would answer the question an attacker holding a
2575
+ * stolen value is actually asking. `invalid_target` and `invalid_scope` are the
2576
+ * two exceptions, and both are decided from values the caller already knows.
2577
+ *
2578
+ * Nothing is spent or revoked before the presenter has shown it is the client the
2579
+ * credential belongs to. On the code path that means `client_id`, `redirect_uri`
2580
+ * and the PKCE verifier are all checked against the row before the code is
2581
+ * consumed — a caller that cannot satisfy them leaves the code unspent for the
2582
+ * real client and the grant untouched, because it has proved nothing about
2583
+ * itself and must therefore cost nobody anything.
2584
+ *
2585
+ * Two replays are detected rather than merely refused, and both revoke the whole
2586
+ * grant:
2587
+ *
2588
+ * - A code presented twice. The first exchange already produced tokens; if the
2589
+ * second presentation is the real client, somebody else holds those tokens.
2590
+ * - A rotated refresh token presented again. Same reasoning, and it is why a
2591
+ * rotated row is marked rather than deleted — a deleted row is
2592
+ * indistinguishable from one that never existed.
2593
+ *
2594
+ * Revoking the grant kills the replacement tokens too, which is the point: the
2595
+ * thief's pair and the client's pair both hang off the grant, and there is no
2596
+ * way to tell which is which.
2597
+ */
2598
+ /** A token request as RFC 6749 names its fields, before anything is trusted. */
2599
+ interface OAuth2TokenRequest {
2600
+ grant_type?: string;
2601
+ code?: string;
2602
+ code_verifier?: string;
2603
+ client_id?: string;
2604
+ redirect_uri?: string;
2605
+ refresh_token?: string;
2606
+ resource?: string;
2607
+ scope?: string;
2608
+ }
2609
+ /** RFC 6749 §5.1, with `scope` always stated so a narrowed request is legible. */
2610
+ interface OAuth2TokenResponse {
2611
+ access_token: string;
2612
+ token_type: 'Bearer';
2613
+ expires_in: number;
2614
+ refresh_token: string;
2615
+ scope: string;
2616
+ }
2617
+ /** RFC 6749 §5.2: the code, and a sentence for whoever is reading the logs. */
2618
+ interface OAuth2TokenRefusal {
2619
+ ok: false;
2620
+ error: string;
2621
+ description: string;
2622
+ }
2623
+ type OAuth2TokenResult = {
2624
+ ok: true;
2625
+ tokens: OAuth2TokenResponse;
2626
+ } | OAuth2TokenRefusal;
2627
+ /**
2628
+ * Exchange a code or refresh a token.
2629
+ *
2630
+ * @param request - The form or JSON body, exactly as it arrived
2631
+ */
2632
+ declare function oauth2TokenService(request: OAuth2TokenRequest): Promise<OAuth2TokenResult>;
2633
+ /**
2634
+ * Revoke a presented token.
2635
+ *
2636
+ * Answers nothing, ever. RFC 7009 §2.2 requires 200 for a token that was never
2637
+ * issued as well as for one that was, so a caller cannot use this endpoint to
2638
+ * ask whether a value it found somewhere is real.
2639
+ *
2640
+ * A refresh token takes the grant with it: a client presenting its refresh token
2641
+ * here is saying it is finished, and leaving that grant's access tokens alive
2642
+ * for eight more hours would be honouring half the request. An access token is
2643
+ * revoked alone — a client may be discarding one it no longer needs while still
2644
+ * holding the connection.
2645
+ *
2646
+ * A `client_id` that is not the token's is the same 200 and revokes nothing
2647
+ * (RFC 7009 §2.1). The endpoint has no client authentication to lean on, so this
2648
+ * does not stop anybody who holds the token from revoking it — what it stops is
2649
+ * one client tearing down another's connection by presenting a value it came
2650
+ * across, which is the only thing a public client's id can be asked to mean.
2651
+ * Which is also why it is required and not optional: RFC 7009 §2.1 has the
2652
+ * client authenticate per RFC 6749 §2.3, and §2.3.1 says a public client with no
2653
+ * credentials identifies itself with `client_id`. The route refuses a request
2654
+ * that carries none before this is called.
2655
+ *
2656
+ * @param token - The value to revoke, access or refresh
2657
+ * @param clientId - Checked against the token's client
2658
+ */
2659
+ declare function revokeOAuth2TokenService(token: string, clientId: string): Promise<void>;
2660
+
2661
+ /**
2662
+ * OAuth 2.1 Access Token Verification
2663
+ *
2664
+ * `verifyAccessToken` is what `@spfn/mcp` calls on every request to `/mcp`. It
2665
+ * is the one function in this feature an application wires up by hand:
2666
+ *
2667
+ * ```typescript
2668
+ * createMcpRoute({ validateToken: verifyAccessToken })
2669
+ * ```
2670
+ *
2671
+ * Null for every refusal, and the same null for all of them — unknown, revoked,
2672
+ * expired, refresh-kind, grant revoked, wrong resource. The non-disclosure rule
2673
+ * `verifyOpsTokenService` keeps, for the same reason: this is a public endpoint
2674
+ * and the refusal must not tell a caller whether the value it presented was ever
2675
+ * real.
2676
+ *
2677
+ * The resource check is not a formality. An access token minted for one API is
2678
+ * refused by another (RFC 8707), so a token a user approved for their MCP server
2679
+ * cannot be replayed against a neighbouring deployment that happens to share this
2680
+ * authorization server.
2681
+ */
2682
+ /** What a verified access token authorizes. */
2683
+ interface VerifiedOAuth2AccessToken {
2684
+ /** The public `client_id` of the CLI holding the token. */
2685
+ clientId: string;
2686
+ /** Scope names, as granted or as the refresh that minted it narrowed them to. */
2687
+ scopes: string[];
2688
+ /** Expiry as seconds since the epoch — the unit every OAuth field uses. */
2689
+ expiresAt: number;
2690
+ /** The account the CLI is acting for. */
2691
+ userId: string;
2692
+ }
2693
+ /**
2694
+ * Verify a bearer token presented to a protected resource.
2695
+ *
2696
+ * @param token - The raw bearer value
2697
+ * @param resource - The resource being accessed, e.g. `https://api.example.com/mcp`
2698
+ * @returns the principal, or null for any reason at all
2699
+ */
2700
+ declare function verifyAccessToken(token: string, resource: string): Promise<VerifiedOAuth2AccessToken | null>;
2701
+
2481
2702
  /**
2482
2703
  * @spfn/auth - Database Schema Definition
2483
2704
  *
@@ -4514,13 +4735,614 @@ declare const rolePermissions: drizzle_orm_pg_core.PgTableWithColumns<{
4514
4735
  identity: undefined;
4515
4736
  generated: undefined;
4516
4737
  }>;
4517
- permissionId: drizzle_orm_pg_core.PgBuildColumn<"role_permissions", drizzle_orm_pg_core.SetNotNull<drizzle_orm_pg_core.PgBigInt53Builder>, {
4738
+ permissionId: drizzle_orm_pg_core.PgBuildColumn<"role_permissions", drizzle_orm_pg_core.SetNotNull<drizzle_orm_pg_core.PgBigInt53Builder>, {
4739
+ name: string;
4740
+ tableName: "role_permissions";
4741
+ dataType: "number int53";
4742
+ data: number;
4743
+ driverParam: string | number;
4744
+ notNull: true;
4745
+ hasDefault: false;
4746
+ isPrimaryKey: false;
4747
+ isAutoincrement: false;
4748
+ hasRuntimeDefault: false;
4749
+ enumValues: undefined;
4750
+ identity: undefined;
4751
+ generated: undefined;
4752
+ }>;
4753
+ };
4754
+ dialect: "pg";
4755
+ }>;
4756
+ type RolePermission = typeof rolePermissions.$inferSelect;
4757
+ type NewRolePermission = typeof rolePermissions.$inferInsert;
4758
+
4759
+ /**
4760
+ * @spfn/auth - User-Permissions Override Entity
4761
+ *
4762
+ * Per-user permission grants/revocations
4763
+ *
4764
+ * Features:
4765
+ * - Grant additional permissions to specific users
4766
+ * - Revoke role-inherited permissions from specific users
4767
+ * - Temporary permissions with expiration
4768
+ * - Audit trail with reason field
4769
+ *
4770
+ * Priority:
4771
+ * User permissions override role permissions
4772
+ */
4773
+ declare const userPermissions: drizzle_orm_pg_core.PgTableWithColumns<{
4774
+ name: "user_permissions";
4775
+ schema: string;
4776
+ columns: {
4777
+ createdAt: drizzle_orm_pg_core.PgBuildColumn<"user_permissions", drizzle_orm_pg_core.SetNotNull<drizzle_orm_pg_core.SetHasDefault<drizzle_orm_pg_core.PgTimestampBuilder>>, {
4778
+ name: string;
4779
+ tableName: "user_permissions";
4780
+ dataType: "object date";
4781
+ data: Date;
4782
+ driverParam: string;
4783
+ notNull: true;
4784
+ hasDefault: true;
4785
+ isPrimaryKey: false;
4786
+ isAutoincrement: false;
4787
+ hasRuntimeDefault: false;
4788
+ enumValues: undefined;
4789
+ identity: undefined;
4790
+ generated: undefined;
4791
+ }>;
4792
+ updatedAt: drizzle_orm_pg_core.PgBuildColumn<"user_permissions", drizzle_orm_pg_core.SetHasDefault<drizzle_orm_pg_core.SetNotNull<drizzle_orm_pg_core.SetHasDefault<drizzle_orm_pg_core.PgTimestampBuilder>>>, {
4793
+ name: string;
4794
+ tableName: "user_permissions";
4795
+ dataType: "object date";
4796
+ data: Date;
4797
+ driverParam: string;
4798
+ notNull: true;
4799
+ hasDefault: true;
4800
+ isPrimaryKey: false;
4801
+ isAutoincrement: false;
4802
+ hasRuntimeDefault: false;
4803
+ enumValues: undefined;
4804
+ identity: undefined;
4805
+ generated: undefined;
4806
+ }>;
4807
+ id: drizzle_orm_pg_core.PgBuildColumn<"user_permissions", drizzle_orm_pg_core.SetIsPrimaryKey<drizzle_orm_pg_core.PgBigSerial53Builder>, {
4808
+ name: string;
4809
+ tableName: "user_permissions";
4810
+ dataType: "number int53";
4811
+ data: number;
4812
+ driverParam: number;
4813
+ notNull: true;
4814
+ hasDefault: true;
4815
+ isPrimaryKey: false;
4816
+ isAutoincrement: false;
4817
+ hasRuntimeDefault: false;
4818
+ enumValues: undefined;
4819
+ identity: undefined;
4820
+ generated: undefined;
4821
+ }>;
4822
+ userId: drizzle_orm_pg_core.PgBuildColumn<"user_permissions", drizzle_orm_pg_core.SetNotNull<drizzle_orm_pg_core.PgBigInt53Builder>, {
4823
+ name: string;
4824
+ tableName: "user_permissions";
4825
+ dataType: "number int53";
4826
+ data: number;
4827
+ driverParam: string | number;
4828
+ notNull: true;
4829
+ hasDefault: false;
4830
+ isPrimaryKey: false;
4831
+ isAutoincrement: false;
4832
+ hasRuntimeDefault: false;
4833
+ enumValues: undefined;
4834
+ identity: undefined;
4835
+ generated: undefined;
4836
+ }>;
4837
+ permissionId: drizzle_orm_pg_core.PgBuildColumn<"user_permissions", drizzle_orm_pg_core.SetNotNull<drizzle_orm_pg_core.PgBigInt53Builder>, {
4838
+ name: string;
4839
+ tableName: "user_permissions";
4840
+ dataType: "number int53";
4841
+ data: number;
4842
+ driverParam: string | number;
4843
+ notNull: true;
4844
+ hasDefault: false;
4845
+ isPrimaryKey: false;
4846
+ isAutoincrement: false;
4847
+ hasRuntimeDefault: false;
4848
+ enumValues: undefined;
4849
+ identity: undefined;
4850
+ generated: undefined;
4851
+ }>;
4852
+ granted: drizzle_orm_pg_core.PgBuildColumn<"user_permissions", drizzle_orm_pg_core.SetHasDefault<drizzle_orm_pg_core.SetNotNull<drizzle_orm_pg_core.PgBooleanBuilder>>, {
4853
+ name: string;
4854
+ tableName: "user_permissions";
4855
+ dataType: "boolean";
4856
+ data: boolean;
4857
+ driverParam: boolean;
4858
+ notNull: true;
4859
+ hasDefault: true;
4860
+ isPrimaryKey: false;
4861
+ isAutoincrement: false;
4862
+ hasRuntimeDefault: false;
4863
+ enumValues: undefined;
4864
+ identity: undefined;
4865
+ generated: undefined;
4866
+ }>;
4867
+ reason: drizzle_orm_pg_core.PgBuildColumn<"user_permissions", drizzle_orm_pg_core.PgTextBuilder<[string, ...string[]]>, {
4868
+ name: string;
4869
+ tableName: "user_permissions";
4870
+ dataType: "string";
4871
+ data: string;
4872
+ driverParam: string;
4873
+ notNull: false;
4874
+ hasDefault: false;
4875
+ isPrimaryKey: false;
4876
+ isAutoincrement: false;
4877
+ hasRuntimeDefault: false;
4878
+ enumValues: undefined;
4879
+ identity: undefined;
4880
+ generated: undefined;
4881
+ }>;
4882
+ expiresAt: drizzle_orm_pg_core.PgBuildColumn<"user_permissions", drizzle_orm_pg_core.PgTimestampBuilder, {
4883
+ name: string;
4884
+ tableName: "user_permissions";
4885
+ dataType: "object date";
4886
+ data: Date;
4887
+ driverParam: string;
4888
+ notNull: false;
4889
+ hasDefault: false;
4890
+ isPrimaryKey: false;
4891
+ isAutoincrement: false;
4892
+ hasRuntimeDefault: false;
4893
+ enumValues: undefined;
4894
+ identity: undefined;
4895
+ generated: undefined;
4896
+ }>;
4897
+ };
4898
+ dialect: "pg";
4899
+ }>;
4900
+ type UserPermission = typeof userPermissions.$inferSelect;
4901
+ type NewUserPermission = typeof userPermissions.$inferInsert;
4902
+
4903
+ /**
4904
+ * @spfn/auth - OAuth 2.1 Clients Entity
4905
+ *
4906
+ * One row per public client that registered itself through RFC 7591 dynamic
4907
+ * client registration — a `claude mcp add` on somebody's laptop, a `codex`
4908
+ * install, a second copy of either on the same machine. There is no secret
4909
+ * column because there is no secret: these clients run on the user's own
4910
+ * device, cannot keep one, and authenticate with `token_endpoint_auth_method:
4911
+ * "none"` plus PKCE.
4912
+ *
4913
+ * `oauth2_` and not `oauth_`: the `oauth` prefix in this package belongs to the
4914
+ * social-login client we are on the other side of (Google, Kakao). Everything
4915
+ * named `oauth2` here is the authorization server we run.
4916
+ *
4917
+ * Registration is unauthenticated by definition, so a row here is nothing but a
4918
+ * claim until a user approves it. `auth.oauth2.client-purge` deletes the rows
4919
+ * that never were: a client older than a day with no grant against it.
4920
+ */
4921
+ declare const oauth2Clients: drizzle_orm_pg_core.PgTableWithColumns<{
4922
+ name: "oauth2_clients";
4923
+ schema: string;
4924
+ columns: {
4925
+ createdAt: drizzle_orm_pg_core.PgBuildColumn<"oauth2_clients", drizzle_orm_pg_core.SetNotNull<drizzle_orm_pg_core.SetHasDefault<drizzle_orm_pg_core.PgTimestampBuilder>>, {
4926
+ name: string;
4927
+ tableName: "oauth2_clients";
4928
+ dataType: "object date";
4929
+ data: Date;
4930
+ driverParam: string;
4931
+ notNull: true;
4932
+ hasDefault: true;
4933
+ isPrimaryKey: false;
4934
+ isAutoincrement: false;
4935
+ hasRuntimeDefault: false;
4936
+ enumValues: undefined;
4937
+ identity: undefined;
4938
+ generated: undefined;
4939
+ }>;
4940
+ updatedAt: drizzle_orm_pg_core.PgBuildColumn<"oauth2_clients", drizzle_orm_pg_core.SetHasDefault<drizzle_orm_pg_core.SetNotNull<drizzle_orm_pg_core.SetHasDefault<drizzle_orm_pg_core.PgTimestampBuilder>>>, {
4941
+ name: string;
4942
+ tableName: "oauth2_clients";
4943
+ dataType: "object date";
4944
+ data: Date;
4945
+ driverParam: string;
4946
+ notNull: true;
4947
+ hasDefault: true;
4948
+ isPrimaryKey: false;
4949
+ isAutoincrement: false;
4950
+ hasRuntimeDefault: false;
4951
+ enumValues: undefined;
4952
+ identity: undefined;
4953
+ generated: undefined;
4954
+ }>;
4955
+ id: drizzle_orm_pg_core.PgBuildColumn<"oauth2_clients", drizzle_orm_pg_core.SetIsPrimaryKey<drizzle_orm_pg_core.PgBigSerial53Builder>, {
4956
+ name: string;
4957
+ tableName: "oauth2_clients";
4958
+ dataType: "number int53";
4959
+ data: number;
4960
+ driverParam: number;
4961
+ notNull: true;
4962
+ hasDefault: true;
4963
+ isPrimaryKey: false;
4964
+ isAutoincrement: false;
4965
+ hasRuntimeDefault: false;
4966
+ enumValues: undefined;
4967
+ identity: undefined;
4968
+ generated: undefined;
4969
+ }>;
4970
+ clientId: drizzle_orm_pg_core.PgBuildColumn<"oauth2_clients", drizzle_orm_pg_core.SetNotNull<drizzle_orm_pg_core.PgTextBuilder<[string, ...string[]]>>, {
4971
+ name: string;
4972
+ tableName: "oauth2_clients";
4973
+ dataType: "string";
4974
+ data: string;
4975
+ driverParam: string;
4976
+ notNull: true;
4977
+ hasDefault: false;
4978
+ isPrimaryKey: false;
4979
+ isAutoincrement: false;
4980
+ hasRuntimeDefault: false;
4981
+ enumValues: undefined;
4982
+ identity: undefined;
4983
+ generated: undefined;
4984
+ }>;
4985
+ clientName: drizzle_orm_pg_core.PgBuildColumn<"oauth2_clients", drizzle_orm_pg_core.SetNotNull<drizzle_orm_pg_core.PgTextBuilder<[string, ...string[]]>>, {
4986
+ name: string;
4987
+ tableName: "oauth2_clients";
4988
+ dataType: "string";
4989
+ data: string;
4990
+ driverParam: string;
4991
+ notNull: true;
4992
+ hasDefault: false;
4993
+ isPrimaryKey: false;
4994
+ isAutoincrement: false;
4995
+ hasRuntimeDefault: false;
4996
+ enumValues: undefined;
4997
+ identity: undefined;
4998
+ generated: undefined;
4999
+ }>;
5000
+ redirectUris: drizzle_orm_pg_core.PgBuildColumn<"oauth2_clients", drizzle_orm_pg_core.SetNotNull<drizzle_orm_pg_core.SetDimensions<drizzle_orm_pg_core.PgTextBuilder<[string, ...string[]]>, 1>>, {
5001
+ name: string;
5002
+ tableName: "oauth2_clients";
5003
+ dataType: "string";
5004
+ data: string[];
5005
+ driverParam: string | string[];
5006
+ notNull: true;
5007
+ hasDefault: false;
5008
+ isPrimaryKey: false;
5009
+ isAutoincrement: false;
5010
+ hasRuntimeDefault: false;
5011
+ enumValues: undefined;
5012
+ identity: undefined;
5013
+ generated: undefined;
5014
+ }>;
5015
+ createdIp: drizzle_orm_pg_core.PgBuildColumn<"oauth2_clients", drizzle_orm_pg_core.PgTextBuilder<[string, ...string[]]>, {
5016
+ name: string;
5017
+ tableName: "oauth2_clients";
5018
+ dataType: "string";
5019
+ data: string;
5020
+ driverParam: string;
5021
+ notNull: false;
5022
+ hasDefault: false;
5023
+ isPrimaryKey: false;
5024
+ isAutoincrement: false;
5025
+ hasRuntimeDefault: false;
5026
+ enumValues: undefined;
5027
+ identity: undefined;
5028
+ generated: undefined;
5029
+ }>;
5030
+ lastUsedAt: drizzle_orm_pg_core.PgBuildColumn<"oauth2_clients", drizzle_orm_pg_core.PgTimestampBuilder, {
5031
+ name: string;
5032
+ tableName: "oauth2_clients";
5033
+ dataType: "object date";
5034
+ data: Date;
5035
+ driverParam: string;
5036
+ notNull: false;
5037
+ hasDefault: false;
5038
+ isPrimaryKey: false;
5039
+ isAutoincrement: false;
5040
+ hasRuntimeDefault: false;
5041
+ enumValues: undefined;
5042
+ identity: undefined;
5043
+ generated: undefined;
5044
+ }>;
5045
+ };
5046
+ dialect: "pg";
5047
+ }>;
5048
+ type OAuth2Client = typeof oauth2Clients.$inferSelect;
5049
+ type NewOAuth2Client = typeof oauth2Clients.$inferInsert;
5050
+
5051
+ /**
5052
+ * @spfn/auth - OAuth 2.1 Grants Entity
5053
+ *
5054
+ * What a user actually consented to: this client, for this resource, with these
5055
+ * scopes. Codes and tokens all hang off a grant, so it is the one row that has
5056
+ * to be revoked to cut a CLI off — `GET /_auth/oauth2/grants` lists them and
5057
+ * `DELETE /_auth/oauth2/grants/:id` is the button.
5058
+ *
5059
+ * One row per (client, user, resource). Re-consenting updates `scopes` rather
5060
+ * than adding a second row, so a user who widens a CLI's access sees one entry
5061
+ * for it and not a pile of them, and revoking that entry revokes everything the
5062
+ * client holds.
5063
+ *
5064
+ * A global revocation — revoke-all, a password change, a password reset, a
5065
+ * deletion request — revokes every grant the user has, for the reason device
5066
+ * codes are refused in the same breath: a live grant still has a refresh token
5067
+ * against it, and "sign me out everywhere" that leaves one alive has not.
5068
+ */
5069
+ declare const oauth2Grants: drizzle_orm_pg_core.PgTableWithColumns<{
5070
+ name: "oauth2_grants";
5071
+ schema: string;
5072
+ columns: {
5073
+ createdAt: drizzle_orm_pg_core.PgBuildColumn<"oauth2_grants", drizzle_orm_pg_core.SetNotNull<drizzle_orm_pg_core.SetHasDefault<drizzle_orm_pg_core.PgTimestampBuilder>>, {
5074
+ name: string;
5075
+ tableName: "oauth2_grants";
5076
+ dataType: "object date";
5077
+ data: Date;
5078
+ driverParam: string;
5079
+ notNull: true;
5080
+ hasDefault: true;
5081
+ isPrimaryKey: false;
5082
+ isAutoincrement: false;
5083
+ hasRuntimeDefault: false;
5084
+ enumValues: undefined;
5085
+ identity: undefined;
5086
+ generated: undefined;
5087
+ }>;
5088
+ updatedAt: drizzle_orm_pg_core.PgBuildColumn<"oauth2_grants", drizzle_orm_pg_core.SetHasDefault<drizzle_orm_pg_core.SetNotNull<drizzle_orm_pg_core.SetHasDefault<drizzle_orm_pg_core.PgTimestampBuilder>>>, {
5089
+ name: string;
5090
+ tableName: "oauth2_grants";
5091
+ dataType: "object date";
5092
+ data: Date;
5093
+ driverParam: string;
5094
+ notNull: true;
5095
+ hasDefault: true;
5096
+ isPrimaryKey: false;
5097
+ isAutoincrement: false;
5098
+ hasRuntimeDefault: false;
5099
+ enumValues: undefined;
5100
+ identity: undefined;
5101
+ generated: undefined;
5102
+ }>;
5103
+ id: drizzle_orm_pg_core.PgBuildColumn<"oauth2_grants", drizzle_orm_pg_core.SetIsPrimaryKey<drizzle_orm_pg_core.PgBigSerial53Builder>, {
5104
+ name: string;
5105
+ tableName: "oauth2_grants";
5106
+ dataType: "number int53";
5107
+ data: number;
5108
+ driverParam: number;
5109
+ notNull: true;
5110
+ hasDefault: true;
5111
+ isPrimaryKey: false;
5112
+ isAutoincrement: false;
5113
+ hasRuntimeDefault: false;
5114
+ enumValues: undefined;
5115
+ identity: undefined;
5116
+ generated: undefined;
5117
+ }>;
5118
+ client: drizzle_orm_pg_core.PgBuildColumn<"oauth2_grants", drizzle_orm_pg_core.SetNotNull<drizzle_orm_pg_core.PgBigInt53Builder>, {
5119
+ name: string;
5120
+ tableName: "oauth2_grants";
5121
+ dataType: "number int53";
5122
+ data: number;
5123
+ driverParam: string | number;
5124
+ notNull: true;
5125
+ hasDefault: false;
5126
+ isPrimaryKey: false;
5127
+ isAutoincrement: false;
5128
+ hasRuntimeDefault: false;
5129
+ enumValues: undefined;
5130
+ identity: undefined;
5131
+ generated: undefined;
5132
+ }>;
5133
+ user: drizzle_orm_pg_core.PgBuildColumn<"oauth2_grants", drizzle_orm_pg_core.SetNotNull<drizzle_orm_pg_core.PgBigInt53Builder>, {
5134
+ name: string;
5135
+ tableName: "oauth2_grants";
5136
+ dataType: "number int53";
5137
+ data: number;
5138
+ driverParam: string | number;
5139
+ notNull: true;
5140
+ hasDefault: false;
5141
+ isPrimaryKey: false;
5142
+ isAutoincrement: false;
5143
+ hasRuntimeDefault: false;
5144
+ enumValues: undefined;
5145
+ identity: undefined;
5146
+ generated: undefined;
5147
+ }>;
5148
+ resource: drizzle_orm_pg_core.PgBuildColumn<"oauth2_grants", drizzle_orm_pg_core.SetNotNull<drizzle_orm_pg_core.PgTextBuilder<[string, ...string[]]>>, {
5149
+ name: string;
5150
+ tableName: "oauth2_grants";
5151
+ dataType: "string";
5152
+ data: string;
5153
+ driverParam: string;
5154
+ notNull: true;
5155
+ hasDefault: false;
5156
+ isPrimaryKey: false;
5157
+ isAutoincrement: false;
5158
+ hasRuntimeDefault: false;
5159
+ enumValues: undefined;
5160
+ identity: undefined;
5161
+ generated: undefined;
5162
+ }>;
5163
+ scopes: drizzle_orm_pg_core.PgBuildColumn<"oauth2_grants", drizzle_orm_pg_core.SetNotNull<drizzle_orm_pg_core.SetDimensions<drizzle_orm_pg_core.PgTextBuilder<[string, ...string[]]>, 1>>, {
5164
+ name: string;
5165
+ tableName: "oauth2_grants";
5166
+ dataType: "string";
5167
+ data: string[];
5168
+ driverParam: string | string[];
5169
+ notNull: true;
5170
+ hasDefault: false;
5171
+ isPrimaryKey: false;
5172
+ isAutoincrement: false;
5173
+ hasRuntimeDefault: false;
5174
+ enumValues: undefined;
5175
+ identity: undefined;
5176
+ generated: undefined;
5177
+ }>;
5178
+ revokedAt: drizzle_orm_pg_core.PgBuildColumn<"oauth2_grants", drizzle_orm_pg_core.PgTimestampBuilder, {
5179
+ name: string;
5180
+ tableName: "oauth2_grants";
5181
+ dataType: "object date";
5182
+ data: Date;
5183
+ driverParam: string;
5184
+ notNull: false;
5185
+ hasDefault: false;
5186
+ isPrimaryKey: false;
5187
+ isAutoincrement: false;
5188
+ hasRuntimeDefault: false;
5189
+ enumValues: undefined;
5190
+ identity: undefined;
5191
+ generated: undefined;
5192
+ }>;
5193
+ };
5194
+ dialect: "pg";
5195
+ }>;
5196
+ type OAuth2Grant = typeof oauth2Grants.$inferSelect;
5197
+ type NewOAuth2Grant = typeof oauth2Grants.$inferInsert;
5198
+
5199
+ /**
5200
+ * @spfn/auth - OAuth 2.1 Authorization Codes Entity
5201
+ *
5202
+ * The 60-second, single-use ticket the consent screen hands back through the
5203
+ * client's own loopback listener, exchanged at the token endpoint for tokens.
5204
+ *
5205
+ * Only the SHA-256 hash is stored, as with device codes, ops tokens and signup
5206
+ * links: a dump of this table must not let its reader finish somebody's
5207
+ * authorization. The unique constraint on the hash doubles as the lookup index.
5208
+ *
5209
+ * `usedAt` is written by the same statement that reads the row, so of two token
5210
+ * requests arriving together exactly one gets tokens. A code presented twice is
5211
+ * the signature of a stolen code, and the second presentation revokes the whole
5212
+ * grant rather than merely refusing — by then the thief may already hold what
5213
+ * the first exchange produced.
5214
+ */
5215
+ declare const oauth2AuthorizationCodes: drizzle_orm_pg_core.PgTableWithColumns<{
5216
+ name: "oauth2_authorization_codes";
5217
+ schema: string;
5218
+ columns: {
5219
+ createdAt: drizzle_orm_pg_core.PgBuildColumn<"oauth2_authorization_codes", drizzle_orm_pg_core.SetNotNull<drizzle_orm_pg_core.SetHasDefault<drizzle_orm_pg_core.PgTimestampBuilder>>, {
5220
+ name: string;
5221
+ tableName: "oauth2_authorization_codes";
5222
+ dataType: "object date";
5223
+ data: Date;
5224
+ driverParam: string;
5225
+ notNull: true;
5226
+ hasDefault: true;
5227
+ isPrimaryKey: false;
5228
+ isAutoincrement: false;
5229
+ hasRuntimeDefault: false;
5230
+ enumValues: undefined;
5231
+ identity: undefined;
5232
+ generated: undefined;
5233
+ }>;
5234
+ updatedAt: drizzle_orm_pg_core.PgBuildColumn<"oauth2_authorization_codes", drizzle_orm_pg_core.SetHasDefault<drizzle_orm_pg_core.SetNotNull<drizzle_orm_pg_core.SetHasDefault<drizzle_orm_pg_core.PgTimestampBuilder>>>, {
5235
+ name: string;
5236
+ tableName: "oauth2_authorization_codes";
5237
+ dataType: "object date";
5238
+ data: Date;
5239
+ driverParam: string;
5240
+ notNull: true;
5241
+ hasDefault: true;
5242
+ isPrimaryKey: false;
5243
+ isAutoincrement: false;
5244
+ hasRuntimeDefault: false;
5245
+ enumValues: undefined;
5246
+ identity: undefined;
5247
+ generated: undefined;
5248
+ }>;
5249
+ id: drizzle_orm_pg_core.PgBuildColumn<"oauth2_authorization_codes", drizzle_orm_pg_core.SetIsPrimaryKey<drizzle_orm_pg_core.PgBigSerial53Builder>, {
5250
+ name: string;
5251
+ tableName: "oauth2_authorization_codes";
5252
+ dataType: "number int53";
5253
+ data: number;
5254
+ driverParam: number;
5255
+ notNull: true;
5256
+ hasDefault: true;
5257
+ isPrimaryKey: false;
5258
+ isAutoincrement: false;
5259
+ hasRuntimeDefault: false;
5260
+ enumValues: undefined;
5261
+ identity: undefined;
5262
+ generated: undefined;
5263
+ }>;
5264
+ codeHash: drizzle_orm_pg_core.PgBuildColumn<"oauth2_authorization_codes", drizzle_orm_pg_core.SetNotNull<drizzle_orm_pg_core.PgTextBuilder<[string, ...string[]]>>, {
5265
+ name: string;
5266
+ tableName: "oauth2_authorization_codes";
5267
+ dataType: "string";
5268
+ data: string;
5269
+ driverParam: string;
5270
+ notNull: true;
5271
+ hasDefault: false;
5272
+ isPrimaryKey: false;
5273
+ isAutoincrement: false;
5274
+ hasRuntimeDefault: false;
5275
+ enumValues: undefined;
5276
+ identity: undefined;
5277
+ generated: undefined;
5278
+ }>;
5279
+ grant: drizzle_orm_pg_core.PgBuildColumn<"oauth2_authorization_codes", drizzle_orm_pg_core.SetNotNull<drizzle_orm_pg_core.PgBigInt53Builder>, {
5280
+ name: string;
5281
+ tableName: "oauth2_authorization_codes";
5282
+ dataType: "number int53";
5283
+ data: number;
5284
+ driverParam: string | number;
5285
+ notNull: true;
5286
+ hasDefault: false;
5287
+ isPrimaryKey: false;
5288
+ isAutoincrement: false;
5289
+ hasRuntimeDefault: false;
5290
+ enumValues: undefined;
5291
+ identity: undefined;
5292
+ generated: undefined;
5293
+ }>;
5294
+ redirectUri: drizzle_orm_pg_core.PgBuildColumn<"oauth2_authorization_codes", drizzle_orm_pg_core.SetNotNull<drizzle_orm_pg_core.PgTextBuilder<[string, ...string[]]>>, {
5295
+ name: string;
5296
+ tableName: "oauth2_authorization_codes";
5297
+ dataType: "string";
5298
+ data: string;
5299
+ driverParam: string;
5300
+ notNull: true;
5301
+ hasDefault: false;
5302
+ isPrimaryKey: false;
5303
+ isAutoincrement: false;
5304
+ hasRuntimeDefault: false;
5305
+ enumValues: undefined;
5306
+ identity: undefined;
5307
+ generated: undefined;
5308
+ }>;
5309
+ codeChallenge: drizzle_orm_pg_core.PgBuildColumn<"oauth2_authorization_codes", drizzle_orm_pg_core.SetNotNull<drizzle_orm_pg_core.PgTextBuilder<[string, ...string[]]>>, {
5310
+ name: string;
5311
+ tableName: "oauth2_authorization_codes";
5312
+ dataType: "string";
5313
+ data: string;
5314
+ driverParam: string;
5315
+ notNull: true;
5316
+ hasDefault: false;
5317
+ isPrimaryKey: false;
5318
+ isAutoincrement: false;
5319
+ hasRuntimeDefault: false;
5320
+ enumValues: undefined;
5321
+ identity: undefined;
5322
+ generated: undefined;
5323
+ }>;
5324
+ expiresAt: drizzle_orm_pg_core.PgBuildColumn<"oauth2_authorization_codes", drizzle_orm_pg_core.SetNotNull<drizzle_orm_pg_core.PgTimestampBuilder>, {
5325
+ name: string;
5326
+ tableName: "oauth2_authorization_codes";
5327
+ dataType: "object date";
5328
+ data: Date;
5329
+ driverParam: string;
5330
+ notNull: true;
5331
+ hasDefault: false;
5332
+ isPrimaryKey: false;
5333
+ isAutoincrement: false;
5334
+ hasRuntimeDefault: false;
5335
+ enumValues: undefined;
5336
+ identity: undefined;
5337
+ generated: undefined;
5338
+ }>;
5339
+ usedAt: drizzle_orm_pg_core.PgBuildColumn<"oauth2_authorization_codes", drizzle_orm_pg_core.PgTimestampBuilder, {
4518
5340
  name: string;
4519
- tableName: "role_permissions";
4520
- dataType: "number int53";
4521
- data: number;
4522
- driverParam: string | number;
4523
- notNull: true;
5341
+ tableName: "oauth2_authorization_codes";
5342
+ dataType: "object date";
5343
+ data: Date;
5344
+ driverParam: string;
5345
+ notNull: false;
4524
5346
  hasDefault: false;
4525
5347
  isPrimaryKey: false;
4526
5348
  isAutoincrement: false;
@@ -4532,30 +5354,44 @@ declare const rolePermissions: drizzle_orm_pg_core.PgTableWithColumns<{
4532
5354
  };
4533
5355
  dialect: "pg";
4534
5356
  }>;
4535
- type RolePermission = typeof rolePermissions.$inferSelect;
4536
- type NewRolePermission = typeof rolePermissions.$inferInsert;
5357
+ type OAuth2AuthorizationCode = typeof oauth2AuthorizationCodes.$inferSelect;
5358
+ type NewOAuth2AuthorizationCode = typeof oauth2AuthorizationCodes.$inferInsert;
4537
5359
 
4538
5360
  /**
4539
- * @spfn/auth - User-Permissions Override Entity
5361
+ * @spfn/auth - OAuth 2.1 Tokens Entity
4540
5362
  *
4541
- * Per-user permission grants/revocations
5363
+ * Access and refresh tokens alike, one row each, distinguished by `kind`. Both
5364
+ * are opaque — `spfn_at_<64 hex>` and `spfn_rt_<64 hex>` — because whatever
5365
+ * verifies them is in the same process as this table, which is the same reason
5366
+ * ops tokens, device codes and signup links are opaque. A JWT would buy
5367
+ * stateless verification and pay for it by making revocation take effect only
5368
+ * at expiry, and revocation here is a button the user presses.
4542
5369
  *
4543
- * Features:
4544
- * - Grant additional permissions to specific users
4545
- * - Revoke role-inherited permissions from specific users
4546
- * - Temporary permissions with expiration
4547
- * - Audit trail with reason field
5370
+ * Only the SHA-256 hash is stored. The value exists in the clear exactly once,
5371
+ * in the token response.
4548
5372
  *
4549
- * Priority:
4550
- * User permissions override role permissions
5373
+ * `replacedAt` is what makes refresh rotation detectable: a rotated refresh is
5374
+ * not deleted, it is marked, so presenting it again is distinguishable from
5375
+ * presenting one that never existed. That presentation means the token leaked —
5376
+ * either the client or the thief is replaying — and the answer is to revoke the
5377
+ * grant, which kills the replacement as well.
4551
5378
  */
4552
- declare const userPermissions: drizzle_orm_pg_core.PgTableWithColumns<{
4553
- name: "user_permissions";
5379
+ /**
5380
+ * Which half of the pair a row is.
5381
+ *
5382
+ * One table rather than two: every rule that matters — revoked, expired, whose
5383
+ * grant, which scopes — is the same for both, and a single `kind` column keeps
5384
+ * "revoke everything under this grant" one statement.
5385
+ */
5386
+ declare const OAUTH2_TOKEN_KINDS: readonly ["access", "refresh"];
5387
+ type OAuth2TokenKind = typeof OAUTH2_TOKEN_KINDS[number];
5388
+ declare const oauth2Tokens: drizzle_orm_pg_core.PgTableWithColumns<{
5389
+ name: "oauth2_tokens";
4554
5390
  schema: string;
4555
5391
  columns: {
4556
- createdAt: drizzle_orm_pg_core.PgBuildColumn<"user_permissions", drizzle_orm_pg_core.SetNotNull<drizzle_orm_pg_core.SetHasDefault<drizzle_orm_pg_core.PgTimestampBuilder>>, {
5392
+ createdAt: drizzle_orm_pg_core.PgBuildColumn<"oauth2_tokens", drizzle_orm_pg_core.SetNotNull<drizzle_orm_pg_core.SetHasDefault<drizzle_orm_pg_core.PgTimestampBuilder>>, {
4557
5393
  name: string;
4558
- tableName: "user_permissions";
5394
+ tableName: "oauth2_tokens";
4559
5395
  dataType: "object date";
4560
5396
  data: Date;
4561
5397
  driverParam: string;
@@ -4568,9 +5404,9 @@ declare const userPermissions: drizzle_orm_pg_core.PgTableWithColumns<{
4568
5404
  identity: undefined;
4569
5405
  generated: undefined;
4570
5406
  }>;
4571
- updatedAt: drizzle_orm_pg_core.PgBuildColumn<"user_permissions", drizzle_orm_pg_core.SetHasDefault<drizzle_orm_pg_core.SetNotNull<drizzle_orm_pg_core.SetHasDefault<drizzle_orm_pg_core.PgTimestampBuilder>>>, {
5407
+ updatedAt: drizzle_orm_pg_core.PgBuildColumn<"oauth2_tokens", drizzle_orm_pg_core.SetHasDefault<drizzle_orm_pg_core.SetNotNull<drizzle_orm_pg_core.SetHasDefault<drizzle_orm_pg_core.PgTimestampBuilder>>>, {
4572
5408
  name: string;
4573
- tableName: "user_permissions";
5409
+ tableName: "oauth2_tokens";
4574
5410
  dataType: "object date";
4575
5411
  data: Date;
4576
5412
  driverParam: string;
@@ -4583,9 +5419,9 @@ declare const userPermissions: drizzle_orm_pg_core.PgTableWithColumns<{
4583
5419
  identity: undefined;
4584
5420
  generated: undefined;
4585
5421
  }>;
4586
- id: drizzle_orm_pg_core.PgBuildColumn<"user_permissions", drizzle_orm_pg_core.SetIsPrimaryKey<drizzle_orm_pg_core.PgBigSerial53Builder>, {
5422
+ id: drizzle_orm_pg_core.PgBuildColumn<"oauth2_tokens", drizzle_orm_pg_core.SetIsPrimaryKey<drizzle_orm_pg_core.PgBigSerial53Builder>, {
4587
5423
  name: string;
4588
- tableName: "user_permissions";
5424
+ tableName: "oauth2_tokens";
4589
5425
  dataType: "number int53";
4590
5426
  data: number;
4591
5427
  driverParam: number;
@@ -4598,12 +5434,12 @@ declare const userPermissions: drizzle_orm_pg_core.PgTableWithColumns<{
4598
5434
  identity: undefined;
4599
5435
  generated: undefined;
4600
5436
  }>;
4601
- userId: drizzle_orm_pg_core.PgBuildColumn<"user_permissions", drizzle_orm_pg_core.SetNotNull<drizzle_orm_pg_core.PgBigInt53Builder>, {
5437
+ tokenHash: drizzle_orm_pg_core.PgBuildColumn<"oauth2_tokens", drizzle_orm_pg_core.SetNotNull<drizzle_orm_pg_core.PgTextBuilder<[string, ...string[]]>>, {
4602
5438
  name: string;
4603
- tableName: "user_permissions";
4604
- dataType: "number int53";
4605
- data: number;
4606
- driverParam: string | number;
5439
+ tableName: "oauth2_tokens";
5440
+ dataType: "string";
5441
+ data: string;
5442
+ driverParam: string;
4607
5443
  notNull: true;
4608
5444
  hasDefault: false;
4609
5445
  isPrimaryKey: false;
@@ -4613,9 +5449,24 @@ declare const userPermissions: drizzle_orm_pg_core.PgTableWithColumns<{
4613
5449
  identity: undefined;
4614
5450
  generated: undefined;
4615
5451
  }>;
4616
- permissionId: drizzle_orm_pg_core.PgBuildColumn<"user_permissions", drizzle_orm_pg_core.SetNotNull<drizzle_orm_pg_core.PgBigInt53Builder>, {
5452
+ kind: drizzle_orm_pg_core.PgBuildColumn<"oauth2_tokens", drizzle_orm_pg_core.SetNotNull<drizzle_orm_pg_core.PgTextBuilder<["access", "refresh"] & [string, ...string[]]>>, {
4617
5453
  name: string;
4618
- tableName: "user_permissions";
5454
+ tableName: "oauth2_tokens";
5455
+ dataType: "string enum";
5456
+ data: "access" | "refresh";
5457
+ driverParam: string;
5458
+ notNull: true;
5459
+ hasDefault: false;
5460
+ isPrimaryKey: false;
5461
+ isAutoincrement: false;
5462
+ hasRuntimeDefault: false;
5463
+ enumValues: ["access", "refresh"] & [string, ...string[]];
5464
+ identity: undefined;
5465
+ generated: undefined;
5466
+ }>;
5467
+ grant: drizzle_orm_pg_core.PgBuildColumn<"oauth2_tokens", drizzle_orm_pg_core.SetNotNull<drizzle_orm_pg_core.PgBigInt53Builder>, {
5468
+ name: string;
5469
+ tableName: "oauth2_tokens";
4619
5470
  dataType: "number int53";
4620
5471
  data: number;
4621
5472
  driverParam: string | number;
@@ -4628,14 +5479,14 @@ declare const userPermissions: drizzle_orm_pg_core.PgTableWithColumns<{
4628
5479
  identity: undefined;
4629
5480
  generated: undefined;
4630
5481
  }>;
4631
- granted: drizzle_orm_pg_core.PgBuildColumn<"user_permissions", drizzle_orm_pg_core.SetHasDefault<drizzle_orm_pg_core.SetNotNull<drizzle_orm_pg_core.PgBooleanBuilder>>, {
5482
+ scopes: drizzle_orm_pg_core.PgBuildColumn<"oauth2_tokens", drizzle_orm_pg_core.SetNotNull<drizzle_orm_pg_core.SetDimensions<drizzle_orm_pg_core.PgTextBuilder<[string, ...string[]]>, 1>>, {
4632
5483
  name: string;
4633
- tableName: "user_permissions";
4634
- dataType: "boolean";
4635
- data: boolean;
4636
- driverParam: boolean;
5484
+ tableName: "oauth2_tokens";
5485
+ dataType: "string";
5486
+ data: string[];
5487
+ driverParam: string | string[];
4637
5488
  notNull: true;
4638
- hasDefault: true;
5489
+ hasDefault: false;
4639
5490
  isPrimaryKey: false;
4640
5491
  isAutoincrement: false;
4641
5492
  hasRuntimeDefault: false;
@@ -4643,11 +5494,26 @@ declare const userPermissions: drizzle_orm_pg_core.PgTableWithColumns<{
4643
5494
  identity: undefined;
4644
5495
  generated: undefined;
4645
5496
  }>;
4646
- reason: drizzle_orm_pg_core.PgBuildColumn<"user_permissions", drizzle_orm_pg_core.PgTextBuilder<[string, ...string[]]>, {
5497
+ expiresAt: drizzle_orm_pg_core.PgBuildColumn<"oauth2_tokens", drizzle_orm_pg_core.SetNotNull<drizzle_orm_pg_core.PgTimestampBuilder>, {
4647
5498
  name: string;
4648
- tableName: "user_permissions";
4649
- dataType: "string";
4650
- data: string;
5499
+ tableName: "oauth2_tokens";
5500
+ dataType: "object date";
5501
+ data: Date;
5502
+ driverParam: string;
5503
+ notNull: true;
5504
+ hasDefault: false;
5505
+ isPrimaryKey: false;
5506
+ isAutoincrement: false;
5507
+ hasRuntimeDefault: false;
5508
+ enumValues: undefined;
5509
+ identity: undefined;
5510
+ generated: undefined;
5511
+ }>;
5512
+ revokedAt: drizzle_orm_pg_core.PgBuildColumn<"oauth2_tokens", drizzle_orm_pg_core.PgTimestampBuilder, {
5513
+ name: string;
5514
+ tableName: "oauth2_tokens";
5515
+ dataType: "object date";
5516
+ data: Date;
4651
5517
  driverParam: string;
4652
5518
  notNull: false;
4653
5519
  hasDefault: false;
@@ -4658,9 +5524,24 @@ declare const userPermissions: drizzle_orm_pg_core.PgTableWithColumns<{
4658
5524
  identity: undefined;
4659
5525
  generated: undefined;
4660
5526
  }>;
4661
- expiresAt: drizzle_orm_pg_core.PgBuildColumn<"user_permissions", drizzle_orm_pg_core.PgTimestampBuilder, {
5527
+ replacedAt: drizzle_orm_pg_core.PgBuildColumn<"oauth2_tokens", drizzle_orm_pg_core.PgTimestampBuilder, {
4662
5528
  name: string;
4663
- tableName: "user_permissions";
5529
+ tableName: "oauth2_tokens";
5530
+ dataType: "object date";
5531
+ data: Date;
5532
+ driverParam: string;
5533
+ notNull: false;
5534
+ hasDefault: false;
5535
+ isPrimaryKey: false;
5536
+ isAutoincrement: false;
5537
+ hasRuntimeDefault: false;
5538
+ enumValues: undefined;
5539
+ identity: undefined;
5540
+ generated: undefined;
5541
+ }>;
5542
+ lastUsedAt: drizzle_orm_pg_core.PgBuildColumn<"oauth2_tokens", drizzle_orm_pg_core.PgTimestampBuilder, {
5543
+ name: string;
5544
+ tableName: "oauth2_tokens";
4664
5545
  dataType: "object date";
4665
5546
  data: Date;
4666
5547
  driverParam: string;
@@ -4676,8 +5557,8 @@ declare const userPermissions: drizzle_orm_pg_core.PgTableWithColumns<{
4676
5557
  };
4677
5558
  dialect: "pg";
4678
5559
  }>;
4679
- type UserPermission = typeof userPermissions.$inferSelect;
4680
- type NewUserPermission = typeof userPermissions.$inferInsert;
5560
+ type OAuth2Token = typeof oauth2Tokens.$inferSelect;
5561
+ type NewOAuth2Token = typeof oauth2Tokens.$inferInsert;
4681
5562
 
4682
5563
  /**
4683
5564
  * @spfn/auth - Auth Metadata Entity
@@ -7369,6 +8250,242 @@ declare class OpsTokensRepository extends BaseRepository {
7369
8250
  }
7370
8251
  declare const opsTokensRepository: OpsTokensRepository;
7371
8252
 
8253
+ /**
8254
+ * OAuth 2.1 Clients Repository
8255
+ *
8256
+ * Data access for dynamically registered public clients. Extends BaseRepository
8257
+ * for transaction context and read/write splitting like every other auth
8258
+ * repository.
8259
+ */
8260
+
8261
+ declare class OAuth2ClientsRepository extends BaseRepository {
8262
+ create(data: NewOAuth2Client): Promise<OAuth2Client>;
8263
+ /**
8264
+ * Lookup by the public `client_id` — the authorize and token paths.
8265
+ *
8266
+ * Reads the primary, not the replica, for the reason the ops-token lookup
8267
+ * does: a client registered a moment ago is immediately used, and a replica
8268
+ * read would answer "unknown client" for the length of the replication lag,
8269
+ * at the one moment a CLI is being connected.
8270
+ */
8271
+ findByClientId(clientId: string): Promise<OAuth2Client | null>;
8272
+ /** Fire-and-forget from the token-issuing path. */
8273
+ updateLastUsedById(id: number): Promise<void>;
8274
+ /**
8275
+ * Register a client unless this address is already at its standing cap.
8276
+ *
8277
+ * The count and the insert are one transaction whose first statement takes
8278
+ * an advisory lock on the address, and all three parts are load-bearing.
8279
+ * Counting on the replica lets replication lag hand out slots that are
8280
+ * already taken. Counting and inserting in two statements lets a second
8281
+ * registration read the same total between them. And a transaction alone
8282
+ * does not close that: under READ COMMITTED each concurrent transaction
8283
+ * counts the rows the others have not committed yet, so twenty requests
8284
+ * arriving together all see zero. The lock is the only part that makes the
8285
+ * cap a number rather than an average, and it is per address, so it costs
8286
+ * nobody else anything.
8287
+ *
8288
+ * @param data - The row to write when there is room for it
8289
+ * @param limit - The address, its cap, and how far back the cap looks
8290
+ * @returns the registered client, or null when the address is at its cap
8291
+ */
8292
+ createWithinStandingCap(data: NewOAuth2Client, limit: {
8293
+ ip: string;
8294
+ max: number;
8295
+ windowMs: number;
8296
+ }): Promise<OAuth2Client | null>;
8297
+ /**
8298
+ * How many clients this IP registered within the window that nobody has
8299
+ * approved yet.
8300
+ *
8301
+ * The window is what makes this a standing-population cap rather than a
8302
+ * permanent quota. Rows are only freed by the purge job, which sweeps once
8303
+ * a day against a 24-hour threshold — so without a window a row registered
8304
+ * a minute after one sweep holds its slot for nearly two days, and twenty
8305
+ * developers behind one NAT lock their whole office out of registering.
8306
+ * A grant against the client takes it out of the count at any age: a client
8307
+ * somebody approved is not junk.
8308
+ *
8309
+ * Reads the primary, inside the caller's transaction, for the reason
8310
+ * `findByClientId` does — a row written a moment ago is exactly the row this
8311
+ * count exists to see.
8312
+ */
8313
+ private countRecentUngrantedByIp;
8314
+ /**
8315
+ * Delete clients older than `before` that no user ever approved.
8316
+ *
8317
+ * A registration is an unauthenticated write, so the table fills with rows
8318
+ * from installs that were abandoned at the consent screen. A row with a
8319
+ * grant against it is never touched, whatever its age — that is somebody's
8320
+ * connected CLI.
8321
+ *
8322
+ * @returns how many rows this sweep removed
8323
+ */
8324
+ deleteStaleUngranted(before: Date): Promise<number>;
8325
+ }
8326
+ declare const oauth2ClientsRepository: OAuth2ClientsRepository;
8327
+
8328
+ /**
8329
+ * OAuth 2.1 Grants Repository
8330
+ *
8331
+ * Data access for the consent records every code and token hangs off.
8332
+ *
8333
+ * Revocation is the operation that matters here, and it is always one statement
8334
+ * per table rather than a read-then-write: a grant being revoked at the same
8335
+ * moment as a refresh rotation must not let the rotation's successor survive,
8336
+ * and `where revoked_at is null` in the UPDATE is what makes the two orderings
8337
+ * produce the same end state.
8338
+ */
8339
+
8340
+ /** A grant with the client it belongs to — what every list and lookup needs. */
8341
+ interface OAuth2GrantWithClient {
8342
+ grant: OAuth2Grant;
8343
+ client: OAuth2Client;
8344
+ }
8345
+ declare class OAuth2GrantsRepository extends BaseRepository {
8346
+ /**
8347
+ * Record a consent, or refresh the scopes of the one already there.
8348
+ *
8349
+ * Re-consenting is an upsert and not an insert: the unique index on
8350
+ * (client, user, resource) says one consent per triple, and a user who
8351
+ * approves a wider scope set is amending the consent they already gave, not
8352
+ * giving a second one. Revoking the entry they can see must revoke
8353
+ * everything the client holds, which a second row would break.
8354
+ *
8355
+ * `revokedAt: null` in the update is deliberate — approving again after
8356
+ * revoking is how a user reconnects a CLI they cut off.
8357
+ */
8358
+ upsert(data: NewOAuth2Grant): Promise<OAuth2Grant>;
8359
+ /**
8360
+ * A grant and its client in one read — the shape the token and verification
8361
+ * paths need, since both have to check the client the caller claims and the
8362
+ * grant's revocation at once.
8363
+ *
8364
+ * Read primary: revocation is documented as taking effect immediately.
8365
+ */
8366
+ findWithClientById(id: number): Promise<OAuth2GrantWithClient | null>;
8367
+ /** What the user's "connected apps" screen lists. Read replica. */
8368
+ listActiveByUserId(userId: number): Promise<OAuth2GrantWithClient[]>;
8369
+ /**
8370
+ * Revoke one live grant belonging to one user.
8371
+ *
8372
+ * The user id is part of the condition rather than checked beforehand: the
8373
+ * id in the URL comes from a caller, and a grant that is not theirs must not
8374
+ * be revocable by guessing a number. A miss is indistinguishable from an id
8375
+ * that does not exist, which is the answer the route gives.
8376
+ *
8377
+ * @returns the revoked row, or null if there was no live grant of that id
8378
+ * for that user
8379
+ */
8380
+ revokeByIdForUser(id: number, userId: number): Promise<OAuth2Grant | null>;
8381
+ /** Revoke one grant by id, whoever it belongs to — the replay detections. */
8382
+ revokeById(id: number): Promise<OAuth2Grant | null>;
8383
+ /**
8384
+ * Revoke every live grant a user has — the authorization-server half of a
8385
+ * global revocation, beside `denyAllActiveByUserId`.
8386
+ *
8387
+ * @returns the grant ids this call revoked
8388
+ */
8389
+ revokeAllActiveByUserId(userId: number): Promise<number[]>;
8390
+ /**
8391
+ * Revoke every live token under the given grants.
8392
+ *
8393
+ * Verification already refuses a token whose grant is revoked, so this is
8394
+ * belt and braces — but the belt is what a `SELECT` against this table shows
8395
+ * an operator, and a live-looking row under a dead grant reads as a hole.
8396
+ *
8397
+ * @returns how many tokens this call revoked
8398
+ */
8399
+ revokeTokensOfGrants(grantIds: number[]): Promise<number>;
8400
+ }
8401
+ declare const oauth2GrantsRepository: OAuth2GrantsRepository;
8402
+
8403
+ /**
8404
+ * OAuth 2.1 Authorization Codes Repository
8405
+ *
8406
+ * Two operations and one of them is the whole point. `consume` is a conditional
8407
+ * UPDATE that names unspent and not-expired and writes `used_at` in the same
8408
+ * statement that reads the row, so of two token requests racing on one code
8409
+ * exactly one comes back with a row. Read-then-write would hand both of them
8410
+ * tokens.
8411
+ *
8412
+ * The TTL travels in that statement as `now()` evaluated by the database, for
8413
+ * the reason the device-code transitions carry theirs: the row is judged by the
8414
+ * same clock that stores it, so an app server whose clock drifted cannot extend
8415
+ * a code's life.
8416
+ *
8417
+ * `findByCodeHash` exists only to explain a miss. A code that is simply unknown
8418
+ * and one that was already spent get the same answer at the endpoint —
8419
+ * `invalid_grant` either way — but the spent one also revokes the grant, and
8420
+ * that decision needs the row.
8421
+ */
8422
+
8423
+ declare class OAuth2AuthorizationCodesRepository extends BaseRepository {
8424
+ create(data: NewOAuth2AuthorizationCode): Promise<OAuth2AuthorizationCode>;
8425
+ /**
8426
+ * Spend a code, addressed by the hash the caller actually presented.
8427
+ *
8428
+ * @returns the row this call spent, or null when it was unknown, already
8429
+ * spent, or past its 60 seconds
8430
+ */
8431
+ consume(codeHash: string): Promise<OAuth2AuthorizationCode | null>;
8432
+ /**
8433
+ * The row behind a miss, in any state. Deliberately unfiltered — which
8434
+ * refusal is owed is the service's decision, and a row filtered out here
8435
+ * would be indistinguishable from a code that never existed.
8436
+ */
8437
+ findByCodeHash(codeHash: string): Promise<OAuth2AuthorizationCode | null>;
8438
+ }
8439
+ declare const oauth2AuthorizationCodesRepository: OAuth2AuthorizationCodesRepository;
8440
+
8441
+ /**
8442
+ * OAuth 2.1 Tokens Repository
8443
+ *
8444
+ * Access and refresh rows, and the rotation that turns one refresh into the
8445
+ * next.
8446
+ *
8447
+ * `rotate` is the code-consuming statement again, in the other table: mark the
8448
+ * presented refresh `replaced_at` only if it is live, and return it only when
8449
+ * THIS call was the one that marked it. Two clients refreshing the same token at
8450
+ * once therefore produce one rotation and one refusal, instead of two token
8451
+ * pairs off one refresh.
8452
+ *
8453
+ * Nothing is deleted. A rotated refresh keeps its row so that presenting it
8454
+ * again is distinguishable from presenting a token that never existed, which is
8455
+ * the entire reuse detection.
8456
+ */
8457
+
8458
+ declare class OAuth2TokensRepository extends BaseRepository {
8459
+ create(data: NewOAuth2Token): Promise<OAuth2Token>;
8460
+ /**
8461
+ * Lookup by the token's hash — the verification and refresh paths.
8462
+ *
8463
+ * Primary, not replica: revocation is a button the user presses and is
8464
+ * documented as taking effect immediately, so a replica read would keep
8465
+ * authenticating a revoked token for the length of the replication lag.
8466
+ * Unfiltered, so the service can tell revoked from expired from unknown.
8467
+ */
8468
+ findByTokenHash(tokenHash: string): Promise<OAuth2Token | null>;
8469
+ /**
8470
+ * Mark a live refresh token as replaced, addressed by its hash.
8471
+ *
8472
+ * @returns the row this call replaced, or null when it was unknown, already
8473
+ * rotated, revoked, or expired
8474
+ */
8475
+ rotate(tokenHash: string): Promise<OAuth2Token | null>;
8476
+ /**
8477
+ * Revoke a live token by hash — RFC 7009.
8478
+ *
8479
+ * @returns the revoked row, or null when there was nothing live to revoke.
8480
+ * The endpoint answers 200 either way; the caller learns nothing
8481
+ * about whether the value it presented ever existed.
8482
+ */
8483
+ revokeByTokenHash(tokenHash: string): Promise<OAuth2Token | null>;
8484
+ /** Fire-and-forget from the verification path, as ops tokens do. */
8485
+ updateLastUsedById(id: number): Promise<void>;
8486
+ }
8487
+ declare const oauth2TokensRepository: OAuth2TokensRepository;
8488
+
7372
8489
  /**
7373
8490
  * @spfn/auth - Email address normalization
7374
8491
  *
@@ -8080,6 +9197,273 @@ declare function deriveCsrfToken(keyId: string): Promise<string>;
8080
9197
  */
8081
9198
  declare function matchesCsrfToken(expected: string, presented: string | null | undefined): boolean;
8082
9199
 
9200
+ /**
9201
+ * OAuth 2.1 Authorization Server Configuration
9202
+ *
9203
+ * Singleton config, mirroring `lib/device-auth-config.ts`: a mutable
9204
+ * module-level value, set once from `createAuthLifecycle()` and read at request
9205
+ * time.
9206
+ *
9207
+ * Unlike the device-auth block this one has no defaults to fall back on, and
9208
+ * that is the feature: an application that does not pass `authorizationServer`
9209
+ * is not running an authorization server, and every route under
9210
+ * `/_auth/oauth2/*` answers 404 for it. Nothing here can be derived — the scope
9211
+ * names are the application's vocabulary, and inventing a set would publish
9212
+ * scopes in the metadata document that nothing honours.
9213
+ */
9214
+ /**
9215
+ * Everything the authorization server needs, resolved.
9216
+ *
9217
+ * `issuer` and `authorizeUrl` are absolute; the defaults are derived here, once,
9218
+ * rather than at each use — the metadata document publishes them and a client
9219
+ * that read one value must not meet another on the next request.
9220
+ */
9221
+ interface AuthorizationServerConfig {
9222
+ /** Origin the tokens are issued by, no path. `SPFN_API_URL` by default. */
9223
+ issuer: string;
9224
+ /**
9225
+ * Where `issuer` came from — the option name or the variable name — so the
9226
+ * boot refusal can tell an operator which one to edit.
9227
+ */
9228
+ issuerSource: string;
9229
+ /** Consent screen, on the web app: `${appUrl}/oauth/authorize` by default. */
9230
+ authorizeUrl: string;
9231
+ /** Scope name to the one-line description the consent screen shows. */
9232
+ scopes: Record<string, string>;
9233
+ /** What an authorize request with no `scope` asks for. All of them by default. */
9234
+ defaultScopes: string[];
9235
+ /** `https://` origins a client may register a redirect URI on. Loopback needs no entry. */
9236
+ allowedRedirectOrigins: string[];
9237
+ accessTokenTtlMs: number;
9238
+ refreshTokenTtlMs: number;
9239
+ codeTtlMs: number;
9240
+ }
9241
+ /** Options as an application writes them. Everything but `scopes` has a default. */
9242
+ interface AuthorizationServerOptions {
9243
+ issuer?: string;
9244
+ authorizeUrl?: string;
9245
+ scopes: Record<string, string>;
9246
+ defaultScopes?: string[];
9247
+ allowedRedirectOrigins?: string[];
9248
+ accessTokenTtlMs?: number;
9249
+ refreshTokenTtlMs?: number;
9250
+ codeTtlMs?: number;
9251
+ }
9252
+ /** Eight hours. Long enough for a working session, short enough to matter. */
9253
+ declare const DEFAULT_ACCESS_TOKEN_TTL_MS: number;
9254
+ /** Thirty days. The CLI is expected to be reconnected about that often. */
9255
+ declare const DEFAULT_REFRESH_TOKEN_TTL_MS: number;
9256
+ /** Sixty seconds — the code travels from a browser redirect to a local listener. */
9257
+ declare const DEFAULT_CODE_TTL_MS: number;
9258
+ /** Path the consent screen is served at on the web app. */
9259
+ declare const AUTHORIZE_PATH = "/oauth/authorize";
9260
+ /** The API origin every default is derived from, and the boot check's subject. */
9261
+ declare function resolveIssuerSource(env?: Record<string, string | undefined>): {
9262
+ value: string | undefined;
9263
+ variable: string;
9264
+ };
9265
+ /**
9266
+ * Resolve the authorization server config, or clear it when the application
9267
+ * passed none. Called synchronously from `createAuthLifecycle()` for the reason
9268
+ * `configureDeviceAuth` is: nothing may serve a request under half a config.
9269
+ *
9270
+ * The issuer is reduced to the form this server publishes here, where every
9271
+ * other default is resolved, so the config is publishable in exactly one state
9272
+ * whether or not a lifecycle hook has run. It is not *validated* here: a value
9273
+ * this cannot reduce is stored as the operator wrote it and refused by
9274
+ * `assertAuthorizationServerIssuer` from `afterInfrastructure`, where a throw
9275
+ * exits the process instead of leaving a server listening without routes. See
9276
+ * `lifecycle.ts`.
9277
+ */
9278
+ declare function configureAuthorizationServer(options?: AuthorizationServerOptions, env?: Record<string, string | undefined>): void;
9279
+ /**
9280
+ * The resolved config, or null when this application runs no authorization
9281
+ * server. Every route under `/_auth/oauth2/*` reads it and answers 404 on null.
9282
+ */
9283
+ declare function getAuthorizationServerConfig(): AuthorizationServerConfig | null;
9284
+ /**
9285
+ * `localhost`, `127.0.0.1` and `[::1]` — the hosts a browser treats as a secure
9286
+ * context over plain http, which is why the issuer may be `http://` on them and
9287
+ * nowhere else. `URL.hostname` normalises every IPv6 spelling to `[::1]`.
9288
+ */
9289
+ declare function isLoopbackHostname(hostname: string): boolean;
9290
+ /**
9291
+ * Refuse boot on an issuer no client could use.
9292
+ *
9293
+ * `.well-known/*` lives at an origin's root, so an issuer carrying a path has no
9294
+ * place to publish its metadata and every discovery request 404s. An issuer that
9295
+ * is neither https nor loopback http means tokens crossing the network in the
9296
+ * clear. Both are configuration drift between environments, so the deploy that
9297
+ * introduces them is where they have to surface — the posture, the place and the
9298
+ * voice of `assertOAuthRedirectUris`, one door down.
9299
+ *
9300
+ * This only asserts. `configureAuthorizationServer` reduced the value it reads
9301
+ * if it could, so anything reaching here that is not an origin is one the
9302
+ * reduction deliberately left alone.
9303
+ *
9304
+ * Silent when no authorization server is configured: an application that does
9305
+ * not run one must boot exactly as it did before this feature existed.
9306
+ *
9307
+ * @throws When the resolved issuer cannot be honoured. The message names the
9308
+ * variable or the option the value came from.
9309
+ */
9310
+ declare function assertAuthorizationServerIssuer(): void;
9311
+
9312
+ /**
9313
+ * OAuth 2.1 redirect URI rules
9314
+ *
9315
+ * Two jobs, and they are not the same job. Registration decides whether a URI
9316
+ * may be written down at all; matching decides whether the URI on an authorize
9317
+ * request is one that was. Registration is where policy lives (no plain http off
9318
+ * loopback, https only on origins the application allows, no fragment ever), and
9319
+ * matching is deliberately dumb: exact, on everything except a loopback port.
9320
+ *
9321
+ * The loopback exception is the whole reason a CLI can use this flow. `claude`
9322
+ * opens a listener on whatever port the OS hands it and cannot know that port
9323
+ * when it registers, so RFC 8252 §7.3 lets the port vary — and nothing else.
9324
+ * `localhost`, `127.0.0.1` and `[::1]` are three different registrations that do
9325
+ * not stand in for one another: they resolve differently on a machine with a
9326
+ * split-horizon resolver, and a client that registered one and is answered on
9327
+ * another is a client something redirected.
9328
+ *
9329
+ * IPv6 arrives in as many spellings as there are ways to write zeroes, so both
9330
+ * sides are read through `new URL(...).hostname`, which answers `[::1]` for
9331
+ * every one of them. The registered value is stored verbatim — the registration
9332
+ * response echoes it back and must not answer a client with a URI it did not
9333
+ * write — so the normalisation happens here, on both sides, at every comparison.
9334
+ */
9335
+ /** Why a redirect URI cannot be registered, in RFC 7591 §3.2.2 terms. */
9336
+ type RedirectUriRefusal = 'invalid_redirect_uri';
9337
+ /**
9338
+ * Whether a URI may be registered, given the https origins this application
9339
+ * allows.
9340
+ *
9341
+ * @param uri - The value exactly as the client wrote it
9342
+ * @param allowedRedirectOrigins - `https://` origins from the lifecycle config
9343
+ * @returns null when it may; the refusal detail when it may not
9344
+ */
9345
+ declare function refuseRedirectUriRegistration(uri: string, allowedRedirectOrigins: string[]): string | null;
9346
+ /**
9347
+ * Whether a presented redirect URI is one of the registered ones.
9348
+ *
9349
+ * Host, path and query must be identical; the port may differ only when both
9350
+ * sides are loopback http. A presented URI carrying a fragment or a dot segment
9351
+ * matches nothing — registration already refuses both, so this is the request
9352
+ * side of the same two rules.
9353
+ */
9354
+ declare function matchesRegisteredRedirectUri(presented: string, registered: string[]): boolean;
9355
+ /**
9356
+ * The host shown on the consent screen, so the person approving can see where
9357
+ * the code is about to be sent.
9358
+ */
9359
+ declare function redirectHostOf(uri: string): string;
9360
+
9361
+ /**
9362
+ * OAuth 2.1 resource indicator (RFC 8707)
9363
+ *
9364
+ * The one string that says which API an access token is good against. It is
9365
+ * required at authorize, defaulted from the grant at token, and compared again
9366
+ * at verification — and the three comparisons have to agree, or a token minted
9367
+ * for one spelling of a URL is refused by another.
9368
+ *
9369
+ * So there is one rule and it is applied at every entry point: parse and
9370
+ * re-serialise with `new URL(...).toString()`, store that, and compare the
9371
+ * normalised strings exactly. That folds away the differences a URL parser is
9372
+ * allowed to fold — a default port written out, a percent-encoding case, an
9373
+ * empty path becoming `/` — and folds away nothing else. A trailing slash is
9374
+ * not noise: `https://api.example.com/mcp` and `https://api.example.com/mcp/`
9375
+ * are different paths and stay different resources.
9376
+ *
9377
+ * A fragment is refused rather than stripped. RFC 8707 §2 forbids one, and
9378
+ * stripping would quietly hand back a token for a target the client did not ask
9379
+ * for.
9380
+ */
9381
+ /**
9382
+ * The canonical form of a resource indicator, or null when the value is not one.
9383
+ *
9384
+ * @param value - The `resource` parameter as it arrived
9385
+ */
9386
+ declare function normalizeResource(value: string): string | null;
9387
+ /**
9388
+ * Whether a presented resource names the same target as a grant's.
9389
+ *
9390
+ * Both sides go through `normalizeResource`: the grant's value was normalised
9391
+ * when it was stored, and normalising it again costs one parse and removes the
9392
+ * chance that a row written by an older rule is compared raw.
9393
+ */
9394
+ declare function sameResource(presented: string, granted: string): boolean;
9395
+
9396
+ /**
9397
+ * OAuth 2.1 token, code and clock helpers
9398
+ *
9399
+ * Every secret this server issues is generated, hashed and compared here, so
9400
+ * there is one answer to "how long is it, what does it start with, and how is it
9401
+ * checked" rather than one per call site.
9402
+ *
9403
+ * Lookup is by the hash and the hash column is unique, so the database finds at
9404
+ * most one row — but the equality that actually authorizes is done here, in
9405
+ * constant time, over the hex digests. It costs a string comparison of 64
9406
+ * characters and it means the decision never depends on how an index compared.
9407
+ *
9408
+ * The clock helpers are the other half. Three places express a moment and they
9409
+ * disagree about the unit — the row stores a timestamp, `verifyAccessToken`
9410
+ * answers seconds since the epoch, and a token response carries the seconds
9411
+ * remaining — so all three go through one function each rather than three
9412
+ * divisions by 1000 that can each be wrong on their own.
9413
+ */
9414
+ /** Shape of an access token. Paired with `isAccessTokenShaped` below. */
9415
+ declare const OAUTH2_ACCESS_TOKEN_PREFIX = "spfn_at_";
9416
+ /** Shape of a refresh token. */
9417
+ declare const OAUTH2_REFRESH_TOKEN_PREFIX = "spfn_rt_";
9418
+ /** SHA-256 hex of a token or code. The only form either is ever stored in. */
9419
+ declare function hashOAuth2Secret(secret: string): string;
9420
+ /** Constant-time equality for two hex digests. */
9421
+ declare function sameOAuth2Hash(a: string, b: string): boolean;
9422
+ declare function generateAccessToken(): string;
9423
+ declare function generateRefreshToken(): string;
9424
+ /** 43 url-safe characters, which is 32 bytes of base64url with no padding. */
9425
+ declare function generateAuthorizationCode(): string;
9426
+ /**
9427
+ * Whether a presented bearer credential has the access-token shape.
9428
+ *
9429
+ * Shape only — the token may still be unknown, revoked or expired, which
9430
+ * `verifyAccessToken` decides. Callers read a raw header, so a missing or
9431
+ * non-string argument answers false rather than throwing, exactly as
9432
+ * `isOpsToken` does.
9433
+ */
9434
+ declare function isAccessTokenShaped(bearer: string): boolean;
9435
+ /** The PKCE S256 transform: base64url of SHA-256 over the verifier's ASCII. */
9436
+ declare function pkceChallengeFor(codeVerifier: string): string;
9437
+ /**
9438
+ * Whether a presented verifier is one RFC 7636 describes.
9439
+ *
9440
+ * Conformance rather than defence — the S256 comparison is what authorizes, and
9441
+ * a verifier of any length still has to hash to the stored challenge. What this
9442
+ * catches is a client that built its verifier wrong: 22 characters of entropy
9443
+ * carries less than the transform promises, and an oversized one is a client
9444
+ * that will be refused by the next server it talks to for a reason this one
9445
+ * never told it.
9446
+ */
9447
+ declare function isPkceVerifierShaped(codeVerifier: string): boolean;
9448
+ /**
9449
+ * Whether a presented challenge could have come out of the S256 transform.
9450
+ *
9451
+ * A challenge of any other shape is a client that sent something else in the
9452
+ * field — its verifier, most often, which is `plain` PKCE wearing the S256
9453
+ * label and is exactly what the method check exists to refuse.
9454
+ */
9455
+ declare function isPkceS256ChallengeShaped(codeChallenge: string): boolean;
9456
+ /** A moment as seconds since the epoch — what `verifyAccessToken` answers with. */
9457
+ declare function toEpochSeconds(at: Date): number;
9458
+ /**
9459
+ * Whole seconds from now until a moment — what `expires_in` carries.
9460
+ *
9461
+ * Floored at zero: a token that expired while the response was being built is
9462
+ * answered as zero rather than as a negative number a client would read as an
9463
+ * enormous unsigned one.
9464
+ */
9465
+ declare function secondsUntil(at: Date, from?: Date): number;
9466
+
8083
9467
  /**
8084
9468
  * One-Time Token Manager
8085
9469
  *
@@ -8520,6 +9904,33 @@ interface AuthLifecycleOptions extends AuthInitOptions {
8520
9904
  */
8521
9905
  intervalMs?: number;
8522
9906
  };
9907
+ /**
9908
+ * OAuth 2.1 authorization server for MCP clients
9909
+ *
9910
+ * Opt-in, and the opt-in is this block. Without it `/_auth/oauth2/*` and
9911
+ * `/.well-known/oauth-authorization-server` answer 404, the boot check does
9912
+ * not run, and the application boots exactly as it did before this feature
9913
+ * existed. With it, Claude Code and Codex can register themselves, send the
9914
+ * account owner to a consent screen, and hold a token against `/mcp`.
9915
+ *
9916
+ * `scopes` is the one thing with no default: the names are the
9917
+ * application's own vocabulary, they are published in the metadata document
9918
+ * and shown on the consent screen, and there is nothing to derive them from.
9919
+ *
9920
+ * @example
9921
+ * ```typescript
9922
+ * createAuthLifecycle({
9923
+ * authorizationServer: {
9924
+ * scopes: {
9925
+ * 'mcp:read': 'Read your projects and tasks',
9926
+ * 'mcp:write': 'Create and edit your tasks',
9927
+ * },
9928
+ * defaultScopes: ['mcp:read'],
9929
+ * },
9930
+ * })
9931
+ * ```
9932
+ */
9933
+ authorizationServer?: AuthorizationServerOptions;
8523
9934
  }
8524
9935
  declare function createAuthLifecycle(options?: AuthLifecycleOptions): AuthLifecycleConfig;
8525
9936
 
@@ -8582,8 +9993,8 @@ declare const linkMailJob: _spfn_core_job.JobDef<{
8582
9993
  }, void>;
8583
9994
 
8584
9995
  /**
8585
- * Build the auth job router: the account-deletion purge sweep and the link-mail
8586
- * sender.
9996
+ * Build the auth job router: the account-deletion purge sweep, the OAuth 2.1
9997
+ * stale-client sweep, and the link-mail sender.
8587
9998
  *
8588
9999
  * The cron is a parameter because `job(...).cron(expression)` bakes the string in
8589
10000
  * at module-import time, which always happens before `createAuthLifecycle()` runs
@@ -8592,11 +10003,14 @@ declare const linkMailJob: _spfn_core_job.JobDef<{
8592
10003
  * after that call, when the schedule is not the default.
8593
10004
  *
8594
10005
  * @param options.purgeCron - Deletion purge schedule; defaults to daily at 04:00
10006
+ * @param options.oauth2ClientPurgeCron - Stale OAuth client sweep; defaults to daily at 05:00
8595
10007
  */
8596
10008
  declare function createAuthJobRouter(options?: {
8597
10009
  purgeCron?: string;
10010
+ oauth2ClientPurgeCron?: string;
8598
10011
  }): _spfn_core_job.JobRouter<{
8599
10012
  deletionPurge: _spfn_core_job.JobDef<void, void>;
10013
+ oauth2ClientPurge: _spfn_core_job.JobDef<void, void>;
8600
10014
  linkMail: _spfn_core_job.JobDef<{
8601
10015
  kind: "signup-link";
8602
10016
  rowId: number;
@@ -8615,6 +10029,7 @@ declare function createAuthJobRouter(options?: {
8615
10029
  */
8616
10030
  declare const authJobRouter: _spfn_core_job.JobRouter<{
8617
10031
  deletionPurge: _spfn_core_job.JobDef<void, void>;
10032
+ oauth2ClientPurge: _spfn_core_job.JobDef<void, void>;
8618
10033
  linkMail: _spfn_core_job.JobDef<{
8619
10034
  kind: "signup-link";
8620
10035
  rowId: number;
@@ -8846,4 +10261,4 @@ type AuthDeletionCancelledPayload = typeof authDeletionCancelledEvent._payload;
8846
10261
  type AuthDeletionCompletedPayload = typeof authDeletionCompletedEvent._payload;
8847
10262
  type OAuthUnlinkedPayload = typeof oauthUnlinkedEvent._payload;
8848
10263
 
8849
- export { type AccountDeletionPurgeUser, type AccountDeletionRequest, AccountDeletionRequestedBy, AccountDeletionRequestsRepository, type AuthConfig, AuthContext, type AuthCsrfConfig, type AuthDeletionCancelledPayload, type AuthDeletionCompletedPayload, type AuthDeletionConfig, type AuthDeletionRequestedPayload, type AuthDeviceAuthConfig, type AuthLifecycleConfig, type AuthLifecycleOptions, type AuthLoginPayload, type AuthMetadataEntity, AuthMetadataRepository, type AuthPasswordResetPayload, AuthProviderSchema, type AuthRegisterPayload, type BeforeRegisterContext, COOKIE_NAMES, CSRF_HEADER, type CancelAccountDeletionParams, type CancelAccountDeletionResult, type CreateOAuthStateParams, type CsrfMode, 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, DEVICE_AUTH_STATUSES, type DecryptedOAuthToken, type DeviceAuthStatus, type DeviceAuthorization, DeviceAuthorizationsRepository, type EmailNormalizationResult, EnvironmentKeyringTokenCipher, type GoogleTokenResponse, type GoogleUserInfo, type Invitation, type InvitationAcceptedPayload, type InvitationCreatedPayload, InvitationStatus, InvitationsRepository, type IssuedOpsToken, KeyAlgorithmType, KeysRepository, type NewAccountDeletionRequest, type NewAuthMetadataEntity, type NewDeviceAuthorization, type NewInvitation, 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, OAuthProvider, type OAuthState, type OAuthTokenCipher, type OAuthTokenContext, type OAuthTokenType, type OAuthUnlinkedPayload, 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 RegisterChannel, type RequestAccountDeletionParams, type RequestAccountDeletionResult, type Role, type RoleEntity, type RoleGuardOptions, type RolePermission, RolePermissionsRepository, RolesRepository, 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 VerifiedOpsToken, type VerifyIdTokenParams, WEBAUTHN_CHALLENGE_KINDS, type WebAuthnChallenge, type WebAuthnChallengeKind, WebAuthnChallengesRepository, acceptInvitation, accountDeletionRequests, accountDeletionRequestsRepository, addPermissionToRole, appleProvider, assertCanAssignRole, assertKeyMatchesAlgorithm, assertPasskeyConfig, authDeletionCancelledEvent, authDeletionCompletedEvent, authDeletionRequestedEvent, authJobRouter, authLogger, authLoginEvent, authMetadata, authMetadataRepository, authPasswordResetEvent, authRegisterEvent, authSchema, cancelAccountDeletionService, cancelInvitation, checkUsernameAvailableService, configureAuth, configureDeletion, configureDeviceAuth, configureOAuthTokenCipher, createAuthDeletionJobRouter, createAuthDeletionPurgeJob, createAuthJobRouter, createAuthLifecycle, createInvitation, createOAuthState, createRole, decodeToken, decryptToken, deleteInvitation, deleteRole, deriveCsrfToken, deviceAuthorizations, deviceAuthorizationsRepository, encryptToken, exchangeCodeForTokens, expireOldInvitations, formatUserCode, generateDeviceCode, generateOAuthNonce, generateToken, generateUserCode, getAllRoles, getAuth, getAuthConfig, getAuthSessionService, 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, hashPassword, initOneTimeTokenManager, initializeAuth, invitationAcceptedEvent, invitationCreatedEvent, invitationsRepository, isEncrypted, isGoogleOAuthEnabled, isOpsToken, issueOpsTokenService, kakaoProvider, keysRepository, linkMailJob, listInvitations, listOpsTokensService, matchOAuthCsrfCookies, matchesCsrfToken, naverProvider, normalizeEmail, normalizeOptionalEmail, normalizeStoredEmails, normalizeUserCode, oauthUnlinkedEvent, oneTimeTokenAuth, opsOrUser, opsTokenAuth, opsTokens, opsTokensRepository, parseDuration, passkeysRepository, passwordResetTokens, passwordResetTokensRepository, permissions, permissionsRepository, purgeUserService, refreshAccessToken, removePermissionFromRole, requestAccountDeletionService, requireAnyPermission, requireOpsScope, requirePermissions, requireRole, resendInvitation, revokeOpsTokenService, roleGuard, rolePermissions, rolePermissionsRepository, roles, rolesRepository, runBeforeRegister, setRolePermissions, signupLinkTokens, signupLinkTokensRepository, socialAccountsRepository, sweepDuePurges, updateLastLoginService, updateLocaleService, updateRole, updateUserProfileService, updateUserService, updateUsernameService, userInvitations, userPermissions, userPermissionsRepository, userProfiles, userProfilesRepository, userPublicKeys, userSocialAccounts, users, usersRepository, validateInvitation, validatePasswordStrength, verificationCodes, verificationCodesRepository, verifyClientToken, verifyIdToken, verifyKeyFingerprint, verifyOAuthState, verifyOpsTokenService, verifyPassword, verifyToken, webauthnChallenges, webauthnChallengesRepository };
10264
+ export { AUTHORIZE_PATH, type AccountDeletionPurgeUser, type AccountDeletionRequest, AccountDeletionRequestedBy, AccountDeletionRequestsRepository, type AuthConfig, AuthContext, type AuthCsrfConfig, type AuthDeletionCancelledPayload, type AuthDeletionCompletedPayload, type AuthDeletionConfig, type AuthDeletionRequestedPayload, type AuthDeviceAuthConfig, type AuthLifecycleConfig, type AuthLifecycleOptions, type AuthLoginPayload, type AuthMetadataEntity, AuthMetadataRepository, type AuthPasswordResetPayload, AuthProviderSchema, type AuthRegisterPayload, type AuthorizationServerConfig, type AuthorizationServerOptions, type BeforeRegisterContext, COOKIE_NAMES, CSRF_HEADER, type CancelAccountDeletionParams, type CancelAccountDeletionResult, type CreateOAuthStateParams, 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, DEVICE_AUTH_STATUSES, type DecryptedOAuthToken, type DeviceAuthStatus, type DeviceAuthorization, DeviceAuthorizationsRepository, type EmailNormalizationResult, EnvironmentKeyringTokenCipher, type GoogleTokenResponse, type GoogleUserInfo, type Invitation, type InvitationAcceptedPayload, type InvitationCreatedPayload, InvitationStatus, InvitationsRepository, type IssuedOpsToken, KeyAlgorithmType, KeysRepository, MAX_UNGRANTED_CLIENTS_PER_IP, type NewAccountDeletionRequest, type NewAuthMetadataEntity, type NewDeviceAuthorization, type NewInvitation, 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, type OAuthUnlinkedPayload, 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 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, authDeletionCancelledEvent, authDeletionCompletedEvent, authDeletionRequestedEvent, authJobRouter, authLogger, authLoginEvent, authMetadata, authMetadataRepository, authPasswordResetEvent, authRegisterEvent, authSchema, cancelAccountDeletionService, cancelInvitation, checkUsernameAvailableService, configureAuth, configureAuthorizationServer, configureDeletion, configureDeviceAuth, configureOAuthTokenCipher, createAuthDeletionJobRouter, createAuthDeletionPurgeJob, createAuthJobRouter, createAuthLifecycle, createInvitation, createOAuthState, createRole, decodeToken, decryptToken, deleteInvitation, deleteRole, deriveCsrfToken, 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, invitationAcceptedEvent, invitationCreatedEvent, invitationsRepository, isAccessTokenShaped, isEncrypted, isGoogleOAuthEnabled, isLoopbackHostname, isOpsToken, isPkceS256ChallengeShaped, isPkceVerifierShaped, issueOpsTokenService, kakaoProvider, keysRepository, linkMailJob, listInvitations, listOpsTokensService, matchOAuthCsrfCookies, matchesCsrfToken, matchesRegisteredRedirectUri, naverProvider, normalizeEmail, normalizeOptionalEmail, normalizeResource, normalizeStoredEmails, normalizeUserCode, oauth2AuthorizationCodes, oauth2AuthorizationCodesRepository, oauth2Clients, oauth2ClientsRepository, oauth2Grants, oauth2GrantsRepository, oauth2TokenService, oauth2Tokens, oauth2TokensRepository, oauthUnlinkedEvent, oneTimeTokenAuth, opsOrUser, opsTokenAuth, opsTokens, opsTokensRepository, parseDuration, passkeysRepository, passwordResetTokens, passwordResetTokensRepository, permissions, permissionsRepository, pkceChallengeFor, 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 };