I have been able to reproduce this and find a solution using the console.
The repro is slightly different from how it is described in the first post. Step 2 is crucial, steps 3 and 4 are less important.
- Be an
adminuser on an up-to-date Discourse site with one or more two-factor keys enabled, such as security keys and authenticator app. - Enable backup codes
Be able to log in and out successfully with those two-factor keys.Ensure that the site setting for enforcing two-factor logins is set to “no”.- Delete all of the two-factor items (authenticators and security keys) from your
(admin)account using the standard UI tools in the Security tab of theadmin’suser profile preferences. - Log out.
What happens is that if all two factor items have been removed, the backup codes are still present in the database, and two-factor authentication is not seen as disabled upon login.
Since there is no authenticator or security key any more, the user is unable to use the backup codes.
Once all authenticators and keys have been removed, the backup codes cannot even be removed in Profile - Security.
Proposed fix: When the last security item is removed from the user, the backup codes should be removed as well.
Workaround: remove all UserSecondFactor records for this user from the database.
UserSecondFactor.where(user_id: 1).delete_all