get-convex / get-convex/better-auth

better-auth 1.6.30 twoFactor lockout columns are rejected by the component schema; twoFactor.enable() corrupts accounts

Open
#435 0 comments 0 reactions 0 assignees View on GitHub
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

Open the contributing 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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.