# `totp/verify-many` ## Purpose Backup-aware TOTP verify. Used during rotation windows where an old and a new TOTP secret coexist — the user's authenticator may still be holding the old secret while the server has moved to the new one. Returns the matching secret index and signed step delta so the caller can mark the rotation complete after the user has successfully authenticated against the new secret. ## Features - Multi-secret loop with constant-time evaluation across the entire list. - Same `±window` semantics as `verify`. - Returns `{ valid, delta, secretIndex }` so the caller can record which secret was used. - Window cap and Zod-boundary semantics shared with `verify`. ## Usage: Basic Example ```ts import { verifyMany } from "@suaveplan/otp"; const r = verifyMany(code, [oldSecret, newSecret]); if (r.valid) markUsed(r.secretIndex); ``` ## Usage: Advanced Example ```ts import { verifyMany } from "@suaveplan/otp"; // During rotation grace period — accept either secret but log when the old // one is used so the rollout dashboard tracks lag. const r = verifyMany(code, [oldSecret, newSecret], { window: 1, clock: () => Date.now(), }); if (r.valid && r.secretIndex === 0) metric.inc("totp.rotation.legacy"); ``` ## API Reference - `verifyMany(code, secrets, opts?): TotpVerifyManyResult` — `{ valid: true; delta; secretIndex }` on match, all-`null` triple on miss. ## Implementation Notes Both loops (over secrets and over windowed steps) run to completion even after the first match so the wall-time of `verifyMany` is independent of which secret / which step matched. The match flag uses `matchedDelta === null` to record the first hit without affecting subsequent iterations. This is the same constant-time pattern as `verify`, extended to cover the secret dimension as well. When the secret rotation completes (every authenticated user has re-enrolled against the new secret), the caller drops the old secret from the list and `verifyMany` collapses back to single-secret behaviour. There is no separate "single-secret" code path inside `verifyMany` — a single-element list runs the same way as a multi-element one. This keeps the constant-time guarantees uniform across rotation states.