import { AttestationError } from "./errors.js"; /** * Library-internal timing spans for an attestation verification, in * milliseconds. Exposed on {@linkcode AttestationContext.timings}. */ export type AttestationTimings = { /** Parse request body + decode base64 fields. */ extractMs: number; /** `consumeChallenge` callback wall-clock duration. */ consumeChallengeMs: number; /** Cryptographic verification (CBOR decode, cert chain, nonce, key extract). */ verifyMs: number; /** `storeDeviceKey` callback wall-clock duration. */ storeDeviceKeyMs: number; }; /** Context passed to the inner handler after successful attestation verification. */ export type AttestationContext = { /** Device identifier (Apple-issued `keyId`) from the request. */ deviceId: string; /** PEM-encoded ECDSA P-256 public key extracted from the attestation. */ publicKeyPem: string; /** Initial sign count from the attestation (always `0`). */ signCount: number; /** Raw App Attest receipt bytes. */ receipt: Uint8Array; /** Library-internal timings, ready to merge into Server-Timing. */ timings: AttestationTimings; }; /** Custom function to extract attestation data from an incoming request. */ export type ExtractAttestationFn = (req: Request) => Promise<{ deviceId: string; /** Raw challenge bytes for the `consumeChallenge` DB lookup. */ challenge: Uint8Array; /** * The challenge in the exact form the client SDK received it, before * any server-side decoding. This is what the client SDK hashed to * produce `clientDataHash` — Expo's `attestKeyAsync` and native * `DCAppAttestService` wrappers convert this string to UTF-8 bytes * and SHA-256 hash them before passing to Apple. The middleware must * hash this same string to produce the matching `clientDataHash` for * `verifyAttestation`. */ challengeAsSent: string; /** * Optional precomputed clientDataHash. When provided, the middleware * uses it directly instead of deriving `SHA-256(challengeAsSent)`. * Needed only for client SDKs with non-standard clientDataHash * derivations (Expo and native `DCAppAttestService` wrappers use the * standard derivation, which the middleware computes automatically). */ clientDataHash?: Uint8Array; attestation: Uint8Array; }>; /** Configuration for the {@linkcode withAttestation} middleware. */ export type WithAttestationOptions = { /** Apple App ID in the format `TEAMID.bundleId`. */ appId: string; /** Set to `true` for development environment attestations. */ developmentEnv?: boolean; /** * Atomically consume a previously-issued challenge. Return `true` if the * challenge was valid, unused, and unexpired (and is now consumed); * `false` otherwise. Implementations should use `DELETE ... RETURNING` * to guarantee single-use semantics. * * The library converts `false` into `AttestationError(CHALLENGE_INVALID)`. */ consumeChallenge: (challenge: Uint8Array) => Promise; /** * Persist the verified device key row. Caller chooses INSERT vs UPSERT — * re-attesting an existing deviceId is cryptographically safe (Apple has * re-signed) so UPSERT is usually correct. */ storeDeviceKey: (row: { deviceId: string; publicKeyPem: string; signCount: number; receipt: Uint8Array; }) => Promise; /** Override the default body-based attestation extraction. */ extractAttestation?: ExtractAttestationFn; /** * Maximum request body size in bytes accepted by the default extractor * (default 1 MiB). Oversized bodies are rejected with `INVALID_FORMAT` * before buffering. Ignored when `extractAttestation` is provided. */ maxBodyBytes?: number; /** * Override the date used for certificate validity checks, forwarded to * {@linkcode verifyAttestation}. Only needed when testing with Apple's * expired test fixture — leave unset in production. */ checkDate?: Date; /** Custom error response handler. Defaults to JSON error responses. */ onError?: (error: AttestationError, req: Request) => Response | Promise; }; /** * Request handler middleware that verifies App Attest attestations. * * Wraps a handler with automatic challenge consumption, cryptographic * attestation verification, and device key persistence. Returns a new * handler that rejects invalid attestations with appropriate HTTP * error responses. * * The symmetric pair of {@linkcode withAssertion} — use this on your * one-time device registration endpoint, then use `withAssertion` on * every protected business endpoint. */ export declare function withAttestation(options: WithAttestationOptions, handler: (req: Request, context: AttestationContext) => Response | Promise): (req: Request) => Promise;