[PR #8711] fix(two-factor): prevent unverified TOTP enrollment from gating sign-in #16410

Closed
opened 2026-04-13 10:30:54 -05:00 by GiteaMirror · 0 comments
Owner

Original Pull Request: https://github.com/better-auth/better-auth/pull/8711

State: closed
Merged: Yes


closes #8627

the totp enrollment flow was using user.twoFactorEnabled to figure out if someone was setting up totp for the first time or signing in. that works fine normally, but if you already have otp enabled (so twoFactorEnabled is already true) and then start totp setup, the code thinks you're signing in and skips the verification step entirely. if you close the tab mid-enrollment, the unverified secret is now active and you're locked out.

the fix adds a verified column to the twoFactor table. new rows start unverified, get marked verified once you enter a valid code, and totp sign-in rejects unverified rows. backup codes and otp are unaffected and work as fallbacks during unfinished enrollment. re-enrollment (calling enableTwoFactor when totp is already verified) preserves verified: true so users are never locked out while rotating their secret.

adds a migration column verified (boolean, default true). existing rows automatically get true which is correct since they were already verified under the old flow. enableTwoFactor explicitly sets new rows to false.

**Original Pull Request:** https://github.com/better-auth/better-auth/pull/8711 **State:** closed **Merged:** Yes --- closes #8627 the totp enrollment flow was using `user.twoFactorEnabled` to figure out if someone was setting up totp for the first time or signing in. that works fine normally, but if you already have otp enabled (so `twoFactorEnabled` is already `true`) and then start totp setup, the code thinks you're signing in and skips the verification step entirely. if you close the tab mid-enrollment, the unverified secret is now active and you're locked out. the fix adds a `verified` column to the `twoFactor` table. new rows start unverified, get marked verified once you enter a valid code, and totp sign-in rejects unverified rows. backup codes and otp are unaffected and work as fallbacks during unfinished enrollment. re-enrollment (calling `enableTwoFactor` when totp is already verified) preserves `verified: true` so users are never locked out while rotating their secret. adds a migration column `verified` (boolean, default `true`). existing rows automatically get `true` which is correct since they were already verified under the old flow. `enableTwoFactor` explicitly sets new rows to `false`.
GiteaMirror added the pull-request label 2026-04-13 10:30:54 -05:00
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: github-starred/better-auth#16410