export type { AuthorizationUrlOptions, OAuthProviderConfig, OAuthServiceConfig, OAuthState, OAuthTokens, TokenExchangeOptions, TokenExchangeResult, } from "./schemas/index.js"; import type { OAuthTokens } from "./schemas/index.js"; /** Provenance of the scope set recorded for one OAuth authorization. */ export type OAuthScopeSource = "default" | "explicit"; /** * Persisted OAuth state row. Created when init handler starts a flow and * consumed exactly once by the callback handler. * * `userId` binds the flow to the authenticated user who initiated it so the * resulting tokens are stored in that user's slot (not a shared one). */ export interface StoredOAuthState { userId: string; serviceId: string; codeVerifier?: string; /** * Transaction redirect binding. Optional for source compatibility with * legacy stores; current handlers reject consumed rows that omit it. */ redirectUri?: string; /** * Requested scope snapshot. Optional for source compatibility with legacy * stores; current handlers reject consumed rows that omit it. */ scopes?: string[]; /** Whether the authorization used provider defaults or an explicit caller override. */ scopeSource?: OAuthScopeSource; createdAt: number; metadata?: Record; } /** * Detached token row plus an opaque store revision. * * Revisions identify a specific write, not token value equality. A store must * issue a new revision for every successful `setTokens` write so disconnect + * reauthorization cannot recreate an older generation (the ABA problem). */ export interface OAuthTokenSnapshot { tokens: OAuthTokens; revision: string; } /** * TokenStore is keyed by `(serviceId, userId)` — tokens are per-user. * * Using a single-slot-per-service store is a vulnerability: the last OAuth * completion overwrites all others, so an attacker who starts and finishes * an OAuth flow with their own account can cause server-side code to act * on the attacker's account. Callers MUST pass `userId` from authenticated * session context. */ export interface TokenStore { getTokens(serviceId: string, userId: string): Promise; setTokens(serviceId: string, userId: string, tokens: OAuthTokens): Promise; clearTokens(serviceId: string, userId: string): Promise; /** * Read tokens together with the opaque revision for that exact write. * * This optional capability is required for automatic token refresh. It is * optional on the interface so existing stores remain source-compatible, * but refresh fails closed before contacting the provider when either * revision method is absent. */ getTokenSnapshot?(serviceId: string, userId: string): Promise; /** * Atomically replace a token row only when its current revision equals * `expectedRevision`. The comparison and write MUST be one indivisible * backing-store operation. Return false when the row is absent or changed. * Every successful replacement MUST receive a fresh revision. */ compareAndSetTokens?(serviceId: string, userId: string, expectedRevision: string, tokens: OAuthTokens): Promise; /** * Atomically delete a token row only when its current revision equals * `expectedRevision`. The comparison and delete MUST be one indivisible * backing-store operation. Return false when the row is absent or changed. * * Optional capability: callers invalidating a row they classified from a * snapshot (for example a superseded legacy grant) MUST fail safe and skip * the delete when this method is absent, so a concurrent reauthorization * can never be destroyed by an unconditional `clearTokens`. */ compareAndClearTokens?(serviceId: string, userId: string, expectedRevision: string): Promise; /** * Run an operation while holding a refresh lock for one token slot. * * Production stores shared by multiple workers MUST implement this as a * distributed, bounded, crash-recoverable lease (including safe release and * renewal for the operation lifetime). A process-local mutex is insufficient * for a shared backing store. Automatic refresh fails closed when absent. */ withTokenRefreshLock?(serviceId: string, userId: string, operation: () => Promise): Promise; /** Persist a new OAuth state row for the initiating user. */ setState(state: string, meta: StoredOAuthState): Promise; /** Atomically read and delete state. Returns null if unknown/expired. */ consumeState(state: string): Promise; } /** * Token store contract required for safe refresh across concurrent workers. * * `TokenStore` remains the source-compatible base contract for non-refreshing * use cases. Production services that may persist refresh tokens should accept * or implement this stricter capability type. */ export interface RefreshCapableTokenStore extends TokenStore { getTokenSnapshot(serviceId: string, userId: string): Promise; compareAndSetTokens(serviceId: string, userId: string, expectedRevision: string, tokens: OAuthTokens): Promise; withTokenRefreshLock(serviceId: string, userId: string, operation: () => Promise): Promise; } //# sourceMappingURL=types.d.ts.map