Consume passkey challenges before parsing or user lookup so every submission carrying a valid token is single-use, then delay user-scoped event attribution and account-state checks until the assertion is cryptographically bound to a registered credential.
Return the generic passkey auth failure for malformed assertions, missing or corrupt challenges, unverified email state, and failed WebAuthn verification; do not send verification-reminder email from this unauthenticated flow.
Add WebAuthn DOMAIN compatibility gates, stricter PRF option gating, key-rotation race checks, cascade deletes, login rate limits on authenticated passkey endpoints, and typed credential IDs on delete.
- Wire key rotation to re-encrypt each passkey's PRF keys, with a superset check in validate_keydata so a passkey can't be left stale.
- Persist signature-counter updates and rotated PRF keys with column-scoped writes, avoiding a broad full-row credential update.
- Compute prfStatus as the full WebAuthnPrfStatus instead of a 1/0 placeholder.
- Move the login challenge from an in-memory cache to a DB-backed table with a scheduled cleanup job.
- Use webauthn-rs's discoverable-credential API instead of the JSON state-injection workaround.
- Make challenge consumption single-use, rate-limit assertion-options, and return a single generic auth-failure message.
- Honor SSO_ONLY at every passkey entry point: login grant, enrollment, refresh, and the unauthenticated assertion-options challenge.
- Migrations: real MySQL foreign key and indexes.
- Add prfStatus unit tests; codebase-consistency pass.