Skip to content

Silent fallback to recovery codes when TOTP key is missing #796

@dknauss

Description

@dknauss

Summary

When _two_factor_enabled_providers lists Two_Factor_Totp but _two_factor_totp_key is missing from user meta, get_primary_provider_for_user() silently falls back to the next available provider — typically Two_Factor_Backup_Codes. The user sees a prompt for a recovery code when they expect to enter a TOTP code from their authenticator app.

There is no warning or indication that the primary method was skipped.

Steps to Reproduce

  1. Enable TOTP for a user via the profile page.
  2. Delete _two_factor_totp_key from user meta (simulating a failed TOTP setup — see Activating multiple methods is confusing and can fail silently #157 and the related issue I'm filing about the two-step setup UX).
  3. Leave _two_factor_enabled_providers intact with Two_Factor_Totp listed.
  4. Log out and log back in.
  5. The 2FA prompt shows "Enter a recovery code" instead of "Enter the code from your authenticator app."

This inconsistent state occurs naturally when a user checks the TOTP "Enabled" checkbox and clicks "Update Profile" without first clicking the "Submit" verification button that saves the TOTP key via REST API.

Expected Behavior

When get_primary_provider_for_user() falls back from the user's configured primary provider to a different provider, the plugin should display a visible warning — either on the login 2FA screen or on the user's profile page. A message like "Your configured TOTP method is unavailable. Falling back to recovery codes." would prevent confusion.

Alternatively (or additionally), the profile save handler could validate that the TOTP key exists before allowing Two_Factor_Totp to be saved in _two_factor_enabled_providers.

Impact

This affects any downstream consumer of get_primary_provider_for_user(). In our case, the WP Sudo reauthentication plugin calls this method to render a 2FA challenge. When Two Factor silently "quietly" falls back to Backup Codes (or any other alternative), the 2FA challenge page gives correct but very muted feedback in the text response. A user who has become blind to this feedback will enter a valid TOTP code (the customary default), but it is validated (correctly) as a backup code, and validation fails with no explanation. In this type of scenario a louder bit of UX copy or other feedback is needed.

Environment

  • Two Factor 0.14.2
  • WordPress 6.7.x
  • Tested on both MySQL and SQLite backends

Related Issues

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    Projects

    Status

    In progress

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions