Skip to main content

Analysis: New User Creation / Test Org Auto-Provisioning Issue

Branch: 260505 / 260506
Date: 2026-05-06
Status: Resolved — userModel rolled back; user management is fully delegated to Clerk


Resolution

The userModel created commit (b5526513) introduced a MongoDB users embedded array on the Organization document and a separate User model, intended to mirror Clerk membership in MongoDB. This caused several problems:

  • New Clerk users had no corresponding MongoDB User record → getUserIdByEmail always returned nullorg.users was always empty
  • The admin reconciliation logic in organizationUpdate.js depended on org.users to enforce Clerk role parity for test orgs
  • The CSV import/export format changed in a breaking way (adminsusers with ObjectId format)

Decision: The userModel approach was rolled back entirely. User management — including roles (org:admin / org:member) and membership — is delegated fully to Clerk. MongoDB stores no parallel user or admin data.


Original Problem Statement

When a new user signed up in Clerk:

  • DEV: "organization reference inconsistencies" (no test org was provisioned)
  • PROD: A Clerk-native "organization setup modal" appeared, asking the user to create an org manually

Previously the user.created Clerk webhook automatically created a test-NNNN org and redirected the user to it.


Root Cause

Problem 1: Empty org.users for all new Clerk users

getUserIdByEmail(adminEmail) queried a MongoDB User collection that had no records for Clerk-managed users. New signups never had a User document, so org.users was always [].

Problem 2: PROD "organization setup modal"

Two likely causes:

  1. Clerk dashboard has "Force organization selection" or "Create organization after sign-up" enabled. Since the user.created webhook fires asynchronously, there is a race window where the user has no active org and Clerk shows its native create-org UI.
  2. afterSignUpUrl was not set in ClerkProvider, so Clerk fell back to its dashboard-configured redirect.

Problem 3: DEV webhooks don't reach localhost

Clerk cannot deliver webhooks to localhost:3000 without a tunnel (ngrok, Clerk dev proxy, etc.). Without the webhook, no test org is created and newTenantSlug is never written.


Architecture (Current, Post-Rollback)

User roles and membership are managed entirely by Clerk:

  • getOrgMembership(shortName) in src/lib/auth/requireOrgAccess.js calls the Clerk backend API to verify membership
  • The organization.updated webhook handles logo sync, slug rollback, and test-org name/logo lock — no admin reconciliation
  • The MongoDB Organization document has no users, admins, or role fields

For the PROD modal and DEV webhook issues, see the recommendations below.


Remaining Recommendations

Prevent Clerk's "force org" modal in PROD

Add afterSignUpUrl to ClerkProvider in src/app/[locale]/layout.jsx:

<ClerkProvider
afterSignUpUrl="/"
afterSignOutUrl={`/${locale}/event/sign-out`}
...
>

Also verify in the Clerk PROD dashboard:

  • "Force users to select an organization" → OFF
  • "After sign-up, redirect to" → must NOT point to /create-organization

DEV webhook delivery

Use Clerk's local dev proxy or ngrok to forward webhooks to localhost:

# Clerk CLI
clerk proxy http://localhost:3000
# or
ngrok http 3000 # then update Clerk webhook endpoint