A related gap: removing all second factors re-opens factor changes to the password alone. Today Supabase only requires AAL2 to add or remove factors while a verified TOTP exists, and a passkey sign-in isn't AAL2. So on an account protected only by passkeys, anyone who knows the password can add their own TOTP or passkey, and that new factor then passes every "second factor" check. The same happens to a TOTP user who removes their last TOTP: the account drops back to password-only protection for factor changes. We'd suggest: once an account has any verified second factor (TOTP or passkey), adding or removing a factor requires verifying an existing one (or at least make this a configuration option when a project enables passkeys as AAL2). Together with letting a passkey satisfy AAL2, that would let apps treat passkeys and TOTP as equal second factors. Today we have to require a TOTP for admin accounts: we add our own out-of-band activation step when an admin's first second factor is bound, and only a TOTP can carry that activation forward, because only a TOTP makes Supabase require AAL2 for later factor changes. With this change, an activated admin could move between passkeys and TOTP (for example, to a new phone) without being re-activated.
Additional context: with passkeys counting toward AAL2 (or treated as a second factor by apps, as we do via the passkey AMR), there's a binding gap. Today a password-only aal1 session can register a user's first passkey (step-up only applies once a verified TOTP exists). That means the password alone can mint a credential that later satisfies a "second factor" check. If this enhancement is adopted, it would help to also have an option to require AAL2 or an existing-factor ceremony for all passkey registrations, including the first, or at least to expose on the session/AMR whether the passkey used was registered from an AAL2 session. Our workaround in the meantime is to require a TOTP for admin-tier accounts.