get-convex / get-convex/better-auth
better-auth 1.6.30 twoFactor lockout columns are rejected by the component schema; twoFactor.enable() corrupts accounts
- Dominant language
- TypeScript
- Stars
- 764
- Forks
- 125
- PR merge metrics
- No merged PRs in 30d
Description
## Summary
better-auth added `failedVerificationCount` and `lockedUntil` to the twoFactor model for its account lockout feature (present in 1.6.30, not in 1.6.11). The component's Convex schema still defines `twoFactor` with only `secret`, `backupCodes`, `userId`, `verified`. Convex table validators reject unknown fields, so on better-auth 1.6.30 the adapter's insert into `twoFactor` fails. The peer range (`better-auth >=1.6.11 <1.7.0`) allows this combination.
## Why the failed insert corrupts accounts
`enableTwoFactor` with `skipVerificationOnEnable` works in this order:
1. set `user.twoFactorEnabled = true`, create a replacement session, delete the old one
2. then create the `twoFactor` row
Step 2 is the insert that fails, after step 1 has already committed. The account is left with `twoFactorEnabled = true` and no `twoFactor` row. On 1.6.30, sign-in OTP verification requires the row (`TWO_FACTOR_NOT_ENABLED`), so the account is locked out permanently: sign-in demands a code that no verifier will accept.
## Repro
- @convex-dev/better-auth 0.12.5, better-auth 1.6.30, twoFactor plugin with `skipVerificationOnEnable: true` and email OTP configured
- Call `authClient.twoFactor.enable({ password })` from a signed-in account
- Result, consistent across five fresh accounts: `twoFactorEnabled` true, no `twoFactor` row, next sign-in unrecoverable (400 `TWO_FACTOR_NOT_ENABLED` on verify)
- The same flow on better-auth 1.6.11 creates the row and works
## Suggested fix
Add the two columns to the component's `twoFactor` table schema, or cap the peer range below the version that introduced them until the schema catches up. I'm pinned to better-auth 1.6.11 in the meantime.
Contributor guide
Research direction
Start with the component's twoFactor table schema and the enableTwoFactor flow described in the issue; reproduce the failure with better-auth 1.6.30, skipVerificationOnEnable, and email OTP. Done means the twoFactor row accepts failedVerificationCount and lockedUntil and enabling two-factor no longer leaves an account enabled without its row.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- typescript
- Domain
- authentication, database
- Issue type
- Bug
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Active
- Clarity
- Clearly specified
- Newbie friendliness
- 58/100