TOTP verification consistently rejects correctly-computed codes after initial 2FA setup (v2.15.1)
NPM version: 2.15.1 (self-hosted via Docker, image jc21/nginx-proxy-manager:latest)
Summary: After enabling 2FA, TOTP codes computed with the exact secret stored in the database (standard SHA1, 6 digits, 30s step) are rejected — both for "Regenerate Backup Codes" and for normal login — even though the same method produced a code that was accepted during the initial setup/verify step.
Steps taken to rule out common causes:
Host and container clocks verified in sync (checked twice, <1s difference). Confirmed via direct SQLite inspection that the secret is unchanged between auth.meta.totp_pending_secret (during setup) and auth.meta.totp_secret (after activation) — only the JSON key name changes, value identical. A code computed independently with standard TOTP parameters (HMAC-SHA1, 6 digits, 30s period, current Unix time // 30) against this exact secret was accepted once, completing 2FA setup successfully. Multiple subsequent freshly-computed correct codes (verified against the same unchanged secret, at the moment of submission) were rejected with "Invalid verification code" — both on the "Regenerate Backup Codes" dialog and on a fresh login attempt after logging out. This resulted in a full account lockout; recovery required manually clearing auth.meta in the SQLite database via docker cp (container has no sqlite3 binary).
This looks related to #5142 but with more detailed reproduction steps. Happy to provide more details/logs if useful.
Source: NginxProxyManager/nginx-proxy-manager