fix(convex): handle existing users when creating members#416
Conversation
Instead of throwing a generic error when a user with the same email exists, check if they're already in the organization and either re-add them or throw a specific error message.
📝 WalkthroughWalkthroughThe change refactors the member creation flow in the user service. Instead of immediately rejecting requests when an email already exists, the code now checks if that existing user is already a member of the target organization. If they are, it errors; if not, it links the existing user as a member. For new users, it creates the user via Better Auth signup, retrieves the internal user ID, and then creates the member record. The flow shifts from a simple email-existence check to a two-step verification process covering both user existence and organization membership. Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes 🚥 Pre-merge checks | ✅ 3✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches
🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 2
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
services/platform/convex/users/create_member.ts (1)
59-61:⚠️ Potential issue | 🟠 MajorAuthorization check excludes owners.
The check only allows
'admin'but based on established patterns in team_members/mutations.ts, owners should also be permitted to perform member management operations.🔧 Proposed fix to include owners
const callerRole = (currentMember?.role ?? '').toLowerCase(); - if (callerRole !== 'admin') { - throw new Error('Only Admins can create members'); + if (callerRole !== 'admin' && callerRole !== 'owner') { + throw new Error('Only Admins or Owners can create members'); }Based on learnings: "removeMember now enforces authorization: only admins or owners... Keep this pattern consistent across team-member mutations."
🤖 Fix all issues with AI agents
In `@services/platform/convex/users/create_member.ts`:
- Around line 108-129: The code silently ignores args.password when re-adding an
existing user (see existingUser, args.password, and
components.betterAuth.adapter.create in create_member.ts); either explicitly
apply the provided password (e.g., call the auth adapter's password update
method for existingUser before/after ctx.runMutation) or clearly indicate the
password was not used by adding metadata to the response (e.g., return { userId,
memberId, linked: true, passwordApplied: false } and update the function
docstring). Ensure you locate the existing-user branch where
ctx.runMutation(components.betterAuth.adapter.create, ...) is called and
implement one of these two fixes and adjust the returned object and docs
accordingly.
- Around line 109-124: Duplicate member creation logic using ctx.runMutation
with components.betterAuth.adapter.create is present in create_member.ts (two
branches that set organizationId, userId, role, createdAt and derive memberId
from created._id/.id). Extract this into a single helper (e.g.,
createMemberRecord) that accepts (ctx, organizationId, userId, role), calls
ctx.runMutation(components.betterAuth.adapter.create, ...) with
role.toLowerCase() and Date.now(), and returns the normalized memberId ((created
as any)?._id ?? (created as any)?.id ?? String(created)); then replace both
usages (the branch that uses existingUser._id and the branch that uses
betterAuthUserId) to call the helper with args.role ?? 'member'.
Return whether the user was newly created or already existed so the frontend can inform the admin that the password argument was not used for existing users.
…d member dialog Simplify the frontend to always use the createMember mutation which already handles both new and existing user cases. This removes the separate getUserIdByEmail + addMember path and provides proper feedback when an existing user is added (blue info notice instead of silently closing the dialog).
Summary
Test plan
Summary by CodeRabbit