/** * Authorization Server state stores. * * Holds the short-lived + persistent state the OAuth 2.1 authorization server * needs: authorization codes, refresh tokens, registered clients (DCR), * remembered user consents, and in-flight authorization requests that are * paused awaiting user consent. * * In-memory implementations are suitable for single-instance self-host. * For multi-instance deployments swap with a shared-store implementation * (Redis/D1) that implements the same interfaces. */ import type { AuthorizationCode, RefreshToken, RegisteredClient, ConsentRecord } from '../types/index.js'; export interface AuthCodeStore { /** Store a freshly minted code; fails if code already exists. */ save(code: AuthorizationCode): Promise; /** * Read a code without consuming it. Callers validate PKCE, client auth, * and redirect_uri against this peeked value, then call `consume()` on * the happy path — malformed retries don't burn a valid code. */ peek(code: string): Promise; /** Atomically consume a code (single-use per RFC 6749 §4.1.2). */ consume(code: string): Promise; /** Remove expired entries. */ sweep(now?: Date): Promise; } export declare class MemoryAuthCodeStore implements AuthCodeStore { private codes; save(code: AuthorizationCode): Promise; peek(code: string): Promise; consume(code: string): Promise; sweep(now?: Date): Promise; size(): number; } export interface RefreshTokenStore { save(token: RefreshToken): Promise; find(token: string): Promise; /** Atomic rotation: consume old, save new in one call. */ rotate(oldToken: string, newToken: RefreshToken): Promise; revoke(token: string): Promise; sweep(now?: Date): Promise; } export declare class MemoryRefreshTokenStore implements RefreshTokenStore { private tokens; save(token: RefreshToken): Promise; find(token: string): Promise; rotate(oldToken: string, newToken: RefreshToken): Promise; revoke(token: string): Promise; sweep(now?: Date): Promise; size(): number; } export interface ClientRegistry { save(client: RegisteredClient): Promise; find(clientId: string): Promise; /** Update lastUsedAt for TTL renewal. */ touch(clientId: string, now?: Date): Promise; delete(clientId: string): Promise; /** Evict clients that haven't been touched in `maxIdleMs`. */ sweep(maxIdleMs: number, now?: Date): Promise; } export declare class MemoryClientRegistry implements ClientRegistry { private clients; save(client: RegisteredClient): Promise; find(clientId: string): Promise; touch(clientId: string, now?: Date): Promise; delete(clientId: string): Promise; sweep(maxIdleMs: number, now?: Date): Promise; size(): number; } export interface ConsentStore { /** Save or overwrite consent for (user, client, scope_set). */ save(record: ConsentRecord): Promise; /** * Check if a consent record exists that covers the requested scopes. * Returns true if stored scopes are a superset of requested scopes. */ covers(userId: string, tenantId: string, clientId: string, scopes: string[]): Promise; /** Remove a specific consent record. */ revoke(userId: string, tenantId: string, clientId: string): Promise; sweep(now?: Date): Promise; } export declare class MemoryConsentStore implements ConsentStore { private records; private key; save(record: ConsentRecord): Promise; covers(userId: string, tenantId: string, clientId: string, scopes: string[]): Promise; revoke(userId: string, tenantId: string, clientId: string): Promise; sweep(now?: Date): Promise; size(): number; } /** * An authorization request that passed validation at `/authorize` but is * paused because the user hasn't consented to these scopes yet. Resumed * when the user approves at `/consent`. * * Short TTL (10 min) — if the user walks away from the consent screen, the * request expires and they restart. */ export interface PendingAuthorization { id: string; clientId: string; redirectUri: string; scope: string; state?: string; /** OIDC nonce — preserved across consent so the id_token can echo it. */ nonce?: string; codeChallenge: string; codeChallengeMethod: 'S256'; userId: string; tenantId: string; responseType: 'code'; expiresAt: Date; createdAt: Date; } export interface PendingAuthorizationStore { save(req: PendingAuthorization): Promise; /** * Read a pending request without consuming it. Callers verify * user/tenant ownership against the peeked value before calling * `consume()` — a wrong-session submission shouldn't burn the * legitimate user's pending request. */ peek(id: string): Promise; consume(id: string): Promise; sweep(now?: Date): Promise; } export declare class MemoryPendingAuthorizationStore implements PendingAuthorizationStore { private pending; save(req: PendingAuthorization): Promise; peek(id: string): Promise; consume(id: string): Promise; sweep(now?: Date): Promise; size(): number; } /** * Generate a URL-safe random string of the given byte-length. */ export declare function generateSecureToken(bytes?: number): string; /** * Hash a secret with SHA-256 for storage. Not a password; client_secret * is high-entropy and rotated, so sha256 is acceptable and avoids bcrypt's * 72-byte limit and pbkdf2's latency on every token request. */ export declare function hashClientSecret(secret: string): string; /** * Timing-safe comparison of a presented secret against a stored hash. */ export declare function verifyClientSecret(presented: string, storedHash: string): boolean; /** * Normalise a scope string into a sorted, deduped, space-joined key. Used * for consent-record keys so `"read write"` and `"write read"` match. */ export declare function normalizeScopes(scope: string | undefined | null): string; //# sourceMappingURL=auth-store.d.ts.map