Repository navigation
Allow Multiple Accounts for Different Orgs - #220
Conversation
Co-authored-by: Indy1131 <Indy1131@users.noreply.github.com>
✅ Deploy Preview for servicestart ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
Greptile SummaryThis PR scopes user identity per-organization so the same email address can hold accounts across multiple orgs. It replaces the global Several previously-flagged issues remain open (missing Confidence Score: 3/5Not safe to merge until the migration data-gap is addressed and previously flagged issues are resolved. A P1 migration issue is introduced in this PR (no backfill for existing users, breaking sign-in post-upgrade). Multiple P1 findings from previous review rounds are still open: missing UNIQUE constraint, dead-code after-hook, wrong ROOT_DOMAIN default, missing schema export. drizzle/20260417115347_broad_iron_patriot/migration.sql, lib/utils.ts, lib/auth.ts, lib/schema.ts Important Files Changed
Reviews (8): Last reviewed commit: "Generate new migrations" | Re-trigger Greptile |
| "created_at" timestamp DEFAULT now() NOT NULL, | ||
| CONSTRAINT "user_organizations_pkey" PRIMARY KEY("user_id","organization_id") | ||
| ); |
There was a problem hiding this comment.
Composite UNIQUE constraint missing from migration
The schema defines (table) => [sql\UNIQUE (email, organization_id)`]on theuserstable, but this migration only drops the oldusers_email_keyconstraint without adding the replacement composite one. Drizzle does not auto-generateALTER TABLE ADD CONSTRAINT` for raw-SQL constraints in migrations.
As a result, the database has no enforcement for the (email, org) uniqueness invariant. The application-level findByEmailAndOrganization check avoids duplicates most of the time, but concurrent sign-ups with the same credentials can still produce duplicate rows.
Add to the migration:
ALTER TABLE "users" ADD CONSTRAINT "users_email_organization_id_key" UNIQUE ("email", "organization_id");…caping or encoding' Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
…ariable' Co-authored-by: Copilot Autofix powered by AI <223894421+github-code-quality[bot]@users.noreply.github.com>
Co-authored-by: Indy1131 <Indy1131@users.noreply.github.com>
renatodellosso
left a comment
There was a problem hiding this comment.
Overall flow looks good, but the PR needs some cleanup.
| const rootDomain = | ||
| typeof process !== "undefined" && process.env.E2E_TENANT_DOMAIN | ||
| ? process.env.E2E_TENANT_DOMAIN | ||
| : "lvh.me"; // Default to new domain |
There was a problem hiding this comment.
Out of curiosity, what is "lvh.me"?
There was a problem hiding this comment.
It's a domain that always resolves to the localhost. I was using it for the e2e testing for the different subdomains for each organization (a.lvh.me and b.lvh.me)
Co-authored-by: Indy1131 <Indy1131@users.noreply.github.com>
renatodellosso
left a comment
There was a problem hiding this comment.
@Indy1131 Once you restore the old migrations, we can merge this PR.
This reverts commit 697216f.
Co-authored-by: renatodellosso <renatodellosso@users.noreply.github.com>
| ALTER TABLE "users" DROP CONSTRAINT "users_email_key";--> statement-breakpoint | ||
| ALTER TABLE "users" ADD COLUMN "organization_id" text;--> statement-breakpoint |
There was a problem hiding this comment.
No data migration for pre-existing users
After ADD COLUMN "organization_id" text, every row already in users will have organization_id = NULL. The new findByEmailAndOrganization query filters with eq(users.organizationId, organizationId), which never matches NULL, so any existing user who tries to sign in will be silently treated as non-existent and either receive a credentials error or generate a duplicate account.
Add a UPDATE statement to assign existing users to their organization before the column is used, e.g.:
UPDATE "users"
SET "organization_id" = (
SELECT "id" FROM "organizations" ORDER BY "created_at" LIMIT 1
)
WHERE "organization_id" IS NULL;Adjust the sub-query to match however existing users should be mapped (by slug, by the default org, etc.).
Closes #73
Checklist: