/** * @typedef {Object} AuthCodeRecord * @property {string} clientId * @property {string} redirectUri * @property {string} codeChallenge * @property {string[]} scope * @property {string} [nonce] * @property {string} [subject] * @property {number} [authTime] unix seconds of the authentication event → OIDC `auth_time` * @property {string} [resource] * @property {unknown} [authorizationDetails] * @property {string} [dpopJkt] DPoP `jkt` bound at the authorize step (RFC 9449 §10) * * @typedef {Object} AuthCodeStore * @property {(code: string, record: AuthCodeRecord, ttlMs: number) => void | Promise} save * @property {(code: string) => (AuthCodeRecord | undefined) | Promise} consume */ /** @returns {AuthCodeStore} */ export function createAuthCodeStore(): AuthCodeStore; /** * @typedef {Object} RefreshRecord * @property {string} token the opaque refresh token (primary key) * @property {string} familyId shared across every rotation of one grant * @property {string} clientId * @property {string[]} scope * @property {string} [subject] * @property {string} [resource] * @property {string} [dpopJkt] * @property {number} [expiresAt] epoch ms after which the token is expired * @property {boolean} [used] set when rotated away — a later presentation is reuse * @property {boolean} [revoked] set when the whole family was burned (reuse detected) * * @typedef {Object} RefreshStore * @property {(record: RefreshRecord) => Promise} save * @property {(token: string) => Promise} get * @property {(oldToken: string, next: RefreshRecord) => Promise} rotate * @property {(familyId: string) => Promise} revokeFamily */ /** @returns {RefreshStore} */ export function createRefreshStore(): RefreshStore; /** * @typedef {Object} ParStore * @property {(requestUri: string, params: Record, ttlMs: number) => void | Promise} save * @property {(requestUri: string) => (Record | undefined) | Promise | undefined>} consume */ /** @returns {ParStore} */ export function createParStore(): ParStore; /** * @typedef {Object} DeviceRecord * @property {string} deviceCode * @property {string} userCode * @property {string} clientId * @property {string[]} scope * @property {'pending' | 'approved' | 'denied' | 'redeemed'} status * @property {string} [subject] set on approval * @property {number} [lastPolledAt] for slow_down enforcement * * @typedef {Object} DeviceStore * @property {(record: DeviceRecord, ttlMs: number) => void | Promise} save * @property {(deviceCode: string) => (DeviceRecord | undefined) | Promise} getByDeviceCode * @property {(userCode: string) => (DeviceRecord | undefined) | Promise} getByUserCode * @property {(deviceCode: string, patch: Partial) => void | Promise} update */ /** @returns {DeviceStore} */ export function createDeviceStore(): DeviceStore; /** * @param {unknown} store * @param {string[]} methods * @returns {boolean} */ export function isStoreShaped(store: unknown, methods: string[]): boolean; export type AuthCodeRecord = { clientId: string; redirectUri: string; codeChallenge: string; scope: string[]; nonce?: string | undefined; subject?: string | undefined; /** * unix seconds of the authentication event → OIDC `auth_time` */ authTime?: number | undefined; resource?: string | undefined; authorizationDetails?: unknown; /** * DPoP `jkt` bound at the authorize step (RFC 9449 §10) */ dpopJkt?: string | undefined; }; export type AuthCodeStore = { save: (code: string, record: AuthCodeRecord, ttlMs: number) => void | Promise; consume: (code: string) => (AuthCodeRecord | undefined) | Promise; }; export type RefreshRecord = { /** * the opaque refresh token (primary key) */ token: string; /** * shared across every rotation of one grant */ familyId: string; clientId: string; scope: string[]; subject?: string | undefined; resource?: string | undefined; dpopJkt?: string | undefined; /** * epoch ms after which the token is expired */ expiresAt?: number | undefined; /** * set when rotated away — a later presentation is reuse */ used?: boolean | undefined; /** * set when the whole family was burned (reuse detected) */ revoked?: boolean | undefined; }; export type RefreshStore = { save: (record: RefreshRecord) => Promise; get: (token: string) => Promise; rotate: (oldToken: string, next: RefreshRecord) => Promise; revokeFamily: (familyId: string) => Promise; }; export type ParStore = { save: (requestUri: string, params: Record, ttlMs: number) => void | Promise; consume: (requestUri: string) => (Record | undefined) | Promise | undefined>; }; export type DeviceRecord = { deviceCode: string; userCode: string; clientId: string; scope: string[]; status: "pending" | "approved" | "denied" | "redeemed"; /** * set on approval */ subject?: string | undefined; /** * for slow_down enforcement */ lastPolledAt?: number | undefined; }; export type DeviceStore = { save: (record: DeviceRecord, ttlMs: number) => void | Promise; getByDeviceCode: (deviceCode: string) => (DeviceRecord | undefined) | Promise; getByUserCode: (userCode: string) => (DeviceRecord | undefined) | Promise; update: (deviceCode: string, patch: Partial) => void | Promise; };