WordPress can reject every two-factor authentication code when the secret stored in the authenticator does not match the secret saved on the website. In this case, the user repeatedly saw “ERROR: Invalid verification code.” because the authentication key in Bitwarden differed from the key on the WordPress server. Resetting 2FA through the user’s profile and completing the setup wizard in one uninterrupted pass restored access.
Issue background
A WordPress site had two-factor authentication enabled for privileged user roles. The policy applied to administrators and other site-management accounts rather than public customers.
One user could log in successfully on one environment but repeatedly failed on another. After entering the six-digit code from Bitwarden, WordPress displayed:
ERROR: Invalid verification code.
At other times, the user was returned to the login screen after submitting the code.
Several troubleshooting attempts did not resolve the issue:
- Synchronizing the Bitwarden vault
- Testing in an incognito browser window
- Disabling and re-enabling the authenticator-app method in the site-wide 2FA policy
Diagnosis
The root cause was a mismatch between two authentication secrets:
- The secret stored in the Bitwarden entry
- The secret saved for the user on the WordPress server
Time-based one-time passwords are generated from a shared secret. If the authenticator app and the server use different secrets, both systems can produce valid-looking six-digit codes, but the server will reject every code generated by the app.
The site-wide 2FA Policies screen controlled which authentication methods were allowed. Turning the authenticator-app method off and back on changed the policy but did not reliably reset the user’s saved secret.
The actual secret was established through the user-level 2FA reset and setup wizard. It was stored only after the final Validate & Save step succeeded.
The mismatch most likely occurred because the setup flow was restarted or interrupted, leaving Bitwarden with a secret from one attempt while WordPress saved a different secret from another.
Resolution steps
- Use an unaffected administrator account. Make sure another protected administrator can access WordPress before resetting the affected user’s 2FA.
- Open the affected user’s profile. Use the user-level two-factor authentication controls rather than the global policy screen.
- Click “Reset 2FA.” Start a fresh enrollment so WordPress generates a new QR code and setup secret.
- Update the correct Bitwarden entry. Copy the new setup secret into the authenticator field for the correct WordPress account. Do not leave an older secret in place.
- Continue without restarting the wizard. Click “I’m ready” and proceed directly to verification.
- Use the current code. Copy the newest six-digit code from Bitwarden after the new secret has been saved.
- Complete “Validate & Save.” This is the step that finalizes the pairing between the authenticator and the WordPress server.
- Configure a backup method. Add email-based verification or another supported fallback so the user has a recovery option.
- Test in a new session. Sign out, open a fresh browser session, and confirm that the first authenticator code is accepted.
- Avoid mixing setup attempts. If the wizard must be restarted, discard the previous Bitwarden secret and begin again from the newest QR code.
- Check time synchronization if errors continue. Confirm that the user’s device and server clocks are accurate, because TOTP codes also depend on time.
- Document the recovery process. Store the reset instructions internally so future administrators know that global policy toggles do not replace the user-level enrollment process.
Do not paste or store the raw 2FA secret in tickets, chat messages, or public documentation. Treat it like a password. The secret should remain only in the approved authenticator vault and the WordPress account configuration.
Final outcome
The user’s two-factor authentication was reset through the profile-level setup wizard. A newly generated secret was saved in Bitwarden, and the current code was validated during the same uninterrupted setup flow.
After the new pairing was saved, the user could log in successfully with the authenticator code. A backup email method was also configured for recovery.
The key lesson is that WordPress 2FA policies and user enrollment serve different purposes. Policy settings determine which methods are allowed, while the user-level setup wizard creates the shared secret that makes authenticator codes work.
For help resolving WordPress 2FA errors, recovering administrator access, or configuring secure authentication policies, contact Freshy.