import type { AuthLogger } from '../../types.js'; import { type ChallengeStore } from './challenge-store.js'; export interface WebAuthnConfig { rpId: string; rpName: string; origin: string; /** * Lifetime of one ceremony, in ms. The challenge in the store, the `timeout` * the browser is given and the ceremony cookie all expire on this one value. * @default 300_000 (5 minutes) */ challengeTimeout?: number; /** * Optional persistent challenge store (Redis, Prisma, Upstash, etc.) * implementing `ChallengeStore`. When omitted, defaults to a process-local * in-memory Map — suitable only for single-process deployments. */ challengeStore?: ChallengeStore; /** * Sink for the ceremony core's operational failures (a challenge store whose * `delete` throws — the challenge stays replayable until its TTL). Left unset, * `createPasskeyHandlers` fills it from the deps bundle so passkey logs land * in the same place as every other auth log; a consumer driving `webauthn.ts` * directly can set it themselves. Defaults to `console`. */ logger?: AuthLogger; /** * Require the authenticator's User Verification (UV) flag — typically a * biometric check or PIN. **Default `true`**: the generated options advertise * `userVerification: 'required'`, and both `verifyRegistration` and * `verifyAssertion` throw `WebAuthnError` when the UV bit (0x04) is unset on * the authenticator data flags. It therefore needs an authenticator that can * do UV. * * An explicit `false` drops the **enforcement**, not the ceremony: the * options still ask for `userVerification: 'preferred'`, so an authenticator * that can do UV normally still does it — but one that only proves User * Presence (UP, a tap/touch) is now accepted, which leaves that login a pure * possession factor. A passkey assertion establishes a session without the * TOTP gate, so combined with `config.twoFactor` the opt-out makes a passkey * login a single-factor login for a TOTP-enrolled user; * `createPasskeyHandlers` warns about that pairing at wiring time. */ requireUserVerification?: boolean; } export interface PublicKeyCredentialCreationOptionsJSON { challenge: string; rp: { id: string; name: string; }; user: { id: string; name: string; displayName: string; }; pubKeyCredParams: { type: 'public-key'; alg: number; }[]; timeout?: number; attestation?: 'none' | 'indirect' | 'direct' | 'enterprise'; authenticatorSelection?: { authenticatorAttachment?: 'platform' | 'cross-platform'; residentKey?: 'discouraged' | 'preferred' | 'required'; requireResidentKey?: boolean; userVerification?: 'required' | 'preferred' | 'discouraged'; }; excludeCredentials?: { type: 'public-key'; id: string; transports?: string[]; }[]; } export interface PublicKeyCredentialRequestOptionsJSON { challenge: string; rpId: string; timeout?: number; userVerification?: 'required' | 'preferred' | 'discouraged'; allowCredentials?: { type: 'public-key'; id: string; transports?: string[]; }[]; } export interface RegistrationCredentialJSON { id: string; rawId: string; type: 'public-key'; response: { clientDataJSON: string; attestationObject: string; transports?: string[]; }; } export interface AuthenticationCredentialJSON { id: string; rawId: string; type: 'public-key'; response: { clientDataJSON: string; authenticatorData: string; signature: string; userHandle?: string; }; } export interface VerifiedRegistration { credentialId: string; publicKey: Uint8Array; publicKeyAlg: number; counter: number; transports?: string[]; aaguid: string; } export interface VerifiedAssertion { credentialId: string; newCounter: number; userHandle?: string; } export declare function generateRegistrationOptions(config: WebAuthnConfig, user: { id: string; name: string; displayName: string; }, existingCredentialIds?: string[]): Promise; /** * Generate WebAuthn authentication (assertion) options and persist the * challenge under `challengeKey`. * * `challengeKey` is the key the challenge is stored under — **not** necessarily * a user id. Discoverable ("usernameless") login has no known user at this * step (the authenticator only reveals the credential at verify time), so the * caller must pass a fresh per-ceremony handle and convey it to the verify step * — the bundled handler does this via an HttpOnly cookie. `verifyAssertion` * must later be called with the **same** key. Keying by a real user id only * works for the email-first flow and silently breaks discoverable login * (Finding M4): the challenge ends up unfindable at verify time. */ export declare function generateAuthenticationOptions(config: WebAuthnConfig, challengeKey: string, credentialIds?: string[]): Promise; export declare function verifyRegistration(config: WebAuthnConfig, userId: string, credential: RegistrationCredentialJSON): Promise; /** * Verify a WebAuthn assertion. `challengeKey` must match the key * `generateAuthenticationOptions` stored the challenge under (a per-ceremony * handle for discoverable login — see that function). It is used **only** to * look up the pending challenge; the asserting user is identified by the * stored credential the caller looked up, never by this key. */ export declare function verifyAssertion(config: WebAuthnConfig, challengeKey: string, credential: AuthenticationCredentialJSON, storedPublicKey: Uint8Array, storedAlg: number, storedCounter: number): Promise;