feat(auth): add two-factor authentication - #65
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
2 Skipped Deployments
|
| those policy variables, rebuild the app, or the auth pages will keep advertising the old | ||
| capabilities (the server still enforces its own policy either way). | ||
|
|
||
| Every account can enable TOTP two-factor authentication from the signed-in user menu. Setup at |
There was a problem hiding this comment.
🟠 High README.md:194
OAuth-only accounts cannot enable TOTP, so the statement that Every account can enable it is incorrect. With allowPasswordless disabled, /two-factor/enable requires validatePassword, which returns false when no password credential exists; either support enrollment for OAuth-only users or qualify this documentation.
🚀 Reply "fix it for me" or copy this AI Prompt for your agent:
In file @README.md around line 194:
OAuth-only accounts cannot enable TOTP, so the statement that `Every account can enable` it is incorrect. With `allowPasswordless` disabled, `/two-factor/enable` requires `validatePassword`, which returns false when no password credential exists; either support enrollment for OAuth-only users or qualify this documentation.
ApprovabilityVerdict: Not approved Macroscope's review found this PR not approvable — This PR adds a new production two-factor authentication workflow spanning sign-in, session completion, trusted devices, backup codes, and persistent security data. Authentication-sensitive changes and unresolved concerns about invitation redirect preservation and OAuth-only enrollment require human review. Not approved because:
Adjust the Minimum Blocking Severity for this repo — including turning it Off — in Settings. You can add or adjust custom eligibility rules. Learn more. |
|
Review the following changes in direct dependencies. Learn more about Socket for GitHub.
|
|
Warning Review the following alerts detected in dependencies. According to your organization's Security Policy, it is recommended to resolve "Warn" alerts. Learn more about Socket for GitHub.
|
| code: value, | ||
| trustDevice: trustDevice.value, | ||
| }); | ||
| if (!responseFailed(result)) await navigateTo('/'); |
There was a problem hiding this comment.
Two-factor completion drops invitation target
A user who signs in from an invitation and completes a TOTP challenge is always sent to /. The invitation URL and its redemption token are not retained when entering /two-factor, so successful verification does not return the user to the invitation flow.
Artifacts
Source capture showing the two-factor redirect loses the return query
- Executed Node source inspection records the unconditional root navigation at line 208 and the redirect callback that sends the user only to `/two-factor`, showing no return-query handling. The takeaway is that the return target is not represented in this handoff.
Direct runtime harness for successful two-factor verification with an invitation target
- Review-authored Node harness reads and executes the exact extracted production challenge-handler body with a successful TOTP response and an invitation redirect target. The takeaway is that the tested handler is tied directly to the current source.
Successful two-factor verification navigates to root instead of invitation
- Executed harness output shows a successful TOTP request from `/two-factor?redirect=%2Finvite%3Ftoken%3Dinvitation-token`, followed by `navigateTo target: /` and `return target preserved: false`. The takeaway is that the proposed failure path is confirmed.
Ran code and verified through T-Rex
Prompt To Fix With AI
This is a comment left during a code review.
Path: apps/dashboard/app/pages/two-factor.vue
Line: 208
Comment:
**Two-factor completion drops invitation target**
A user who signs in from an invitation and completes a TOTP challenge is always sent to `/`. The invitation URL and its redemption token are not retained when entering `/two-factor`, so successful verification does not return the user to the invitation flow.
---
For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.
Summary
/two-factorWhy
Accounts currently authenticate with a single factor. This change adds password-confirmed TOTP enrollment, one-time backup codes, trusted-device support, and the matching sign-in challenge without moving authentication policy into the dashboard or persistence concerns into the auth package.
packages/authowns Better Auth policy,packages/databaseowns the schema and migration, andapps/dashboardremains the composition and presentation boundary.Verification
aube run check:repo— blocked by pre-existingturborepo-monoreposkill frontmatter errorsaube run lint:ci— targeted auth/database/dashboard lint passed; the full command was not completed in this Windows environmentaube run typecheck— auth, database, and dashboard filters passed; the full workspace command timed out without diagnosticsaube test—aube run test --filter=@agent-zero/auth --filter=@agent-zero/databasepassed (65 tests)aube run build— the dashboard production build passed; the root wrapper is blocked by existing Windows aube/junction issuesManual production-preview verification covered enrollment, QR/TOTP confirmation, backup-code generation, a fresh password sign-in redirect, TOTP completion, and trusted-device session creation. The signed-out Playwright challenge scenario also passed. Nuxt component tests remain blocked by the existing
file:///__uno.csstest-environment failure, which reproduces on the unchanged login spec.Safety and compatibility
observemode as read-only, or explained the policy change above.The migration is additive. Better Auth encrypts TOTP secrets and backup codes with the deployment auth secret before persistence, requires the account password for enrollment and disable operations, and marks two-factor enabled only after the first valid TOTP code.
Agent context
Reviewer notes
aube run db:migratebefore exercising two-factor enrollment against an existing database./two-factorroute rule: the page serves both the pending pre-session challenge and authenticated account management, while Better Auth enforces the appropriate cookie/session on each endpoint.Note
Add TOTP two-factor authentication with enrollment, challenge, and backup codes
twoFactorplugin in auth.ts and a new/two-factorroute in two-factor.vue that serves both pre-session second-factor challenges and authenticated enrollment0004_outstanding_mister_sinisteradding thetwo_factortable andtwo_factor_enabledcolumn onuserin schema/auth.ts; the schema is exported through the aggregated schema so Better Auth can resolve the modelqrcode) and one-time backup codes, verifies the first TOTP code before completing setup, and supports disabling with password confirmationTwoFactorErrorcomponent, e2e and unit tests, and docs in README and architecture/two-factorhas auth disabled via route rule in nuxt.config.ts to allow both challenge and enrollment states on the same route; the in-memory e2e adapter in server/auth.config.ts now initializes an emptytwoFactorcollection📊 Macroscope summarized fad036e. 14 files reviewed, 1 issue evaluated, 0 issues filtered, 1 comment posted
🗂️ Filtered Issues
Confidence Score: 4/5
The invitation sign-in journey remains broken when it requires two-factor verification.
One non-security product failure remains: successful two-factor verification routes invited users away from the invitation they were attempting to redeem.
Files Needing Attention: apps/dashboard/app/auth.config.ts, apps/dashboard/app/pages/two-factor.vue
What T-Rex did
Reviews (2): Last reviewed commit: "fix(dashboard): skip dangling auth peer ..." | Re-trigger Greptile