import type { AuthLogger } from '../../types.js'; export interface ChallengeEntry { challenge: string; expires: number; } /** * Storage interface for pending WebAuthn challenges. Implementations may be * synchronous (default in-memory Map) or asynchronous (Redis/Prisma/etc). * Methods may return plain values or Promises; callers always await the * result. * * **Atomicity:** for remote/shared backends, implement `take` so that the * read-and-delete happens in a single round-trip (Redis `GETDEL`, Prisma * `delete returning`, etc.). Without it, `consumeChallenge` falls back to * `get`+`delete` which can allow a challenge to be consumed twice under a * concurrent-request race. The default in-memory store provides `take`. */ export interface ChallengeStore { get(key: string): ChallengeEntry | undefined | Promise; set(key: string, entry: ChallengeEntry): void | Promise; delete(key: string): void | Promise; /** Atomic read-and-delete. Preferred over `get`+`delete` for replay safety. */ take?(key: string): ChallengeEntry | undefined | Promise; } export declare function generateChallenge(): string; /** * Default in-memory challenge store factory. Creates a new Map-backed store * with a periodic cleanup timer. Consumers that need multi-instance support * supply their own `ChallengeStore` implementation via * `WebAuthnConfig.challengeStore` instead of this default. */ export declare function createInMemoryChallengeStore(options?: { cleanupIntervalMs?: number; }): ChallengeStore; export declare function resolveChallengeStore(config: { challengeStore?: ChallengeStore; }): ChallengeStore; /** * The lifetime of one WebAuthn ceremony, in ms. * * Four things expire on it — the store entry, the `timeout` the browser is * given for registration and for assertion, and the `maxAge` of the cookie that * carries the ceremony handle — and they only agree while they read one value: * a cookie that outlives its challenge sends the verify step to a challenge * that is gone, a challenge that outlives its cookie is unreachable. */ export declare function resolveChallengeTimeoutMs(config: { challengeTimeout?: number; }): number; /** * The same lifetime in whole seconds, rounded up — the unit `cookies.set` takes * for `maxAge`. Here rather than at the cookie so the conversion has one site. * * Rounded **up**, so a sub-second remainder (`61_500 ms` → `62 s`) leaves the * cookie standing a moment after the challenge it points at is gone: the verify * step then finds the handle, looks the challenge up and gets a clean refusal. * Rounding down would drop the handle while the challenge is still valid, which * is the same failure one moment earlier and harder to read in a log. */ export declare function resolveChallengeTimeoutSeconds(config: { challengeTimeout?: number; }): number; export declare function storeChallenge(store: ChallengeStore, key: string, challenge: string, timeoutMs: number): Promise; /** * @param logger sink for the one operational failure below. The WebAuthn core is * deps-free by design, so the sink rides on `WebAuthnConfig.logger`, which * `createPasskeyHandlers` fills from the deps bundle. Defaults to `console` * for a consumer driving the core directly. */ export declare function consumeChallenge(store: ChallengeStore, key: string, logger?: AuthLogger): Promise;