Conversation
WalkthroughReplaced hardcoded app name with Changes
Sequence Diagram(s)sequenceDiagram
participant User as User
participant Web as Web App
participant Auth as better-auth
participant Email as SMTP / Mailer
participant DB as Database
User->>Web: Attempt sign-up with existing email
Web->>Auth: emailAndPassword.signUp (detect existing user)
Auth->>Auth: invoke onExistingUserSignUp hook
Auth->>Email: sendVerificationOTP(email)
Email-->>Auth: OTP delivered
Auth-->>Web: prompt Finish Logging In (OTP UI)
User->>Web: Submit OTP
Web->>Auth: verify OTP
Auth->>DB: fetch/update session
Auth-->>Web: authentication success (user info)
Web-->>User: show signed-in UI ("Test User")
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 3✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
apps/web/tests/authentication.spec.ts (1)
58-66: Replace the narration with an assertion that the old error stays hidden.This proves the OTP step renders, but it would still pass if the old "email already exists" UI also leaked alongside it. Swapping the new comments for a negative assertion on that copy would make the enumeration regression much tighter.
As per coding guidelines, "Only add comments when it is necessary to explain code that isn't self-explanatory".
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/web/tests/authentication.spec.ts` around lines 58 - 66, Replace the comment that narrates the OTP prompt with a negative assertion ensuring the prior "email already exists" error UI is not visible; after filling the OTP via getCode(testUserEmail) and page.getByPlaceholder("Enter your 6-digit code").fill(code), add an assertion like expecting page.getByText("Email already exists") to not be visible so the test fails if the old error leaks alongside the OTP step.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@apps/web/src/lib/auth.ts`:
- Around line 97-105: The emailAndPassword config enables
requireEmailVerification but lacks customSyntheticUser, so synthetic sign-up
responses are missing admin and additionalFields and cause schema mismatches;
add a customSyntheticUser handler on the emailAndPassword object (near
requireEmailVerification and onExistingUserSignUp) that returns {...coreFields,
role: "user", banned: false, banReason: null, banExpires: null,
...additionalFields, id} (ensuring additionalFields like timeZone and locale are
included) to populate role, banned, banReason, banExpires and your custom
fields.
---
Nitpick comments:
In `@apps/web/tests/authentication.spec.ts`:
- Around line 58-66: Replace the comment that narrates the OTP prompt with a
negative assertion ensuring the prior "email already exists" error UI is not
visible; after filling the OTP via getCode(testUserEmail) and
page.getByPlaceholder("Enter your 6-digit code").fill(code), add an assertion
like expecting page.getByText("Email already exists") to not be visible so the
test fails if the old error leaks alongside the OTP step.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
Run ID: 1eddd52f-cc02-43d4-ac09-e21c907565a5
📒 Files selected for processing (2)
apps/web/src/lib/auth.tsapps/web/tests/authentication.spec.ts
| emailAndPassword: { | ||
| enabled: env.EMAIL_LOGIN_ENABLED !== "false", | ||
| requireEmailVerification: true, | ||
| onExistingUserSignUp: async ({ user }, request) => { | ||
| await authLib.api.sendVerificationOTP({ | ||
| body: { email: user.email, type: "email-verification" }, | ||
| request, | ||
| }); | ||
| }, |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
cat -n apps/web/src/lib/auth.ts | head -120 | tail -30Repository: lukevella/rallly
Length of output: 1162
🏁 Script executed:
rg "customSyntheticUser" --type ts --type tsxRepository: lukevella/rallly
Length of output: 87
🏁 Script executed:
rg "Better Auth" --type ts --type tsx -iRepository: lukevella/rallly
Length of output: 87
🏁 Script executed:
fd "auth" apps/web/src --type fRepository: lukevella/rallly
Length of output: 274
🏁 Script executed:
cat -n apps/web/src/lib/auth.ts | head -200Repository: lukevella/rallly
Length of output: 7882
🏁 Script executed:
rg "admin" apps/web/src/lib/auth.ts -i -A 3 -B 3Repository: lukevella/rallly
Length of output: 499
🏁 Script executed:
rg "schema\|Schema" apps/web/src/lib/auth.ts -A 3 -B 1Repository: lukevella/rallly
Length of output: 42
🏁 Script executed:
rg "user.*extend\|extend.*user" apps/web/src -iRepository: lukevella/rallly
Length of output: 42
🏁 Script executed:
cat -n apps/web/src/lib/auth.ts | tail -100Repository: lukevella/rallly
Length of output: 3291
🏁 Script executed:
grep -n "customSyntheticUser" apps/web/src/lib/auth.tsRepository: lukevella/rallly
Length of output: 42
🏁 Script executed:
cat -n apps/web/src/lib/auth.ts | sed -n '198,250p'Repository: lukevella/rallly
Length of output: 1620
🌐 Web query:
Better Auth customSyntheticUser admin plugin requireEmailVerification
💡 Result:
When you enable email enumeration protection—which happens if emailAndPassword.requireEmailVerification: true (or autoSignIn: false)—Better Auth may return a synthetic (fake) user shape on sign-up. If you’re using the admin plugin, you must extend that synthetic user to include the admin plugin’s added user fields (role, banned, banReason, banExpires), otherwise you’ll hit missing-field/type/schema issues. [1]
import { betterAuth } from "better-auth";
import { admin } from "better-auth/plugins";
export const auth = betterAuth({
emailAndPassword: {
enabled: true,
requireEmailVerification: true,
customSyntheticUser: ({ coreFields, additionalFields, id }) => ({
...coreFields,
// Admin plugin fields (in schema order)
role: "user", // or your configured defaultRole
banned: false,
banReason: null,
banExpires: null,
...additionalFields,
id,
}),
},
plugins: [admin()],
});customSyntheticUser is specifically intended for this case (“Use when plugins add fields to the user table”). [2]
Sources: [1] [2]
🌐 Web query:
Better Auth 1.5 enumeration-safe sign-up customSyntheticUser
💡 Result:
In Better Auth v1.5, when you enable email enumeration protection (e.g. requireEmailVerification: true or autoSignIn: false), the sign-up endpoint may return a synthetic (fake) user response so the client can’t tell whether an email already exists. If you use plugins that add columns to user, you must use customSyntheticUser to include those plugin fields in the synthetic response (otherwise you’ll get missing-field/type/runtime issues). [1][2]
Example (Admin plugin adds role, banned, banReason, banExpires): [1]
import { betterAuth } from "better-auth";
import { admin } from "better-auth/plugins";
export const auth = betterAuth({
emailAndPassword: {
enabled: true,
requireEmailVerification: true, // (or autoSignIn: false)
customSyntheticUser: ({ coreFields, additionalFields, id }) => ({
...coreFields,
// Admin plugin fields (in schema order)
role: "user", // or your admin({ defaultRole: ... })
banned: false,
banReason: null,
banExpires: null,
// any other fields added via Better Auth "additionalFields"
...additionalFields,
id,
}),
},
plugins: [admin()],
});If you have other plugins/custom fields, add their user-table fields into customSyntheticUser the same way (so the synthetic response matches your user schema). [2]
Add customSyntheticUser to the emailAndPassword block.
With requireEmailVerification: true enabled and the admin plugin active, Better Auth returns a synthetic user on sign-up to prevent email enumeration. You must include customSyntheticUser to populate the admin plugin's fields (role, banned, banReason, banExpires) plus your custom additionalFields (timeZone, locale) in the synthetic response. Without it, the synthetic user will be missing these fields, causing schema mismatches and potential runtime errors.
Example fix:
emailAndPassword: {
enabled: env.EMAIL_LOGIN_ENABLED !== "false",
requireEmailVerification: true,
customSyntheticUser: ({ coreFields, additionalFields, id }) => ({
...coreFields,
role: "user",
banned: false,
banReason: null,
banExpires: null,
...additionalFields,
id,
}),
onExistingUserSignUp: async ({ user }, request) => {
// ...
},
}🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@apps/web/src/lib/auth.ts` around lines 97 - 105, The emailAndPassword config
enables requireEmailVerification but lacks customSyntheticUser, so synthetic
sign-up responses are missing admin and additionalFields and cause schema
mismatches; add a customSyntheticUser handler on the emailAndPassword object
(near requireEmailVerification and onExistingUserSignUp) that returns
{...coreFields, role: "user", banned: false, banReason: null, banExpires: null,
...additionalFields, id} (ensuring additionalFields like timeZone and locale are
included) to populate role, banned, banReason, banExpires and your custom
fields.
Summary by CodeRabbit
Chores
Refactor
New Features
Tests