Skip to content

fix(auth): unblock OAuth signups by supplying slug for auto-default team#4435

Merged
saddlepaddle merged 1 commit into
mainfrom
investigate/better-auth-teams-insert
May 12, 2026
Merged

fix(auth): unblock OAuth signups by supplying slug for auto-default team#4435
saddlepaddle merged 1 commit into
mainfrom
investigate/better-auth-teams-insert

Conversation

@saddlepaddle
Copy link
Copy Markdown
Collaborator

@saddlepaddle saddlepaddle commented May 12, 2026

Summary

  • P0: every new Google OAuth signup since feat(teams): add teams as first-class org primitive #4403 deployed was failing at /api/auth/callback/google with unable_to_create_user.
  • Better Auth's organization plugin auto-creates a default team during createOrganization (crud-org.mjs:106-136) and hard-codes teamData = { organizationId, name, createdAt } — no slug. Our auth.teams.slug is NOT NULL with no default, so the insert aborted with a constraint violation, rolling back the whole signup transaction.
  • Use teams.defaultTeam.customCreateDefaultTeam to own the auto-team insert and supply a constant "DEFAULT" slug. Drop the now-duplicate db.insert(teams) from afterCreateOrganization.

Why this hook (and not beforeCreateTeam / afterCreateOrganization)

  • beforeCreateTeam fires on both the auto-default path and user-initiated createTeam calls from the Settings → Teams UI (crud-team.mjs:107). Unconditionally returning { slug: "DEFAULT" } would clobber user-chosen slugs.
  • afterCreateOrganization runs at line 137 — after the broken auto-team insert at line 126. The transaction already aborts before we'd get there.
  • customCreateDefaultTeam is the dedicated callback consulted only on the auto-default path.

Existing orgs

Untouched. They keep the team backfilled by migration 0049 (slug = org slug). Only orgs created after this deploys get slug="DEFAULT".

Test plan

  • Sign in via Google with a fresh email whose domain is not on any org's allowedDomains. Confirm signup completes and lands in /.
  • In the DB, verify the new org has exactly one row in auth.teams with name='Default Team', slug='DEFAULT', and exactly one matching row in auth.team_members for the new user.
  • Create a second team via Settings → Teams with a custom name/slug. Confirm it is created with the user-supplied values (no clobbering from this hook).
  • Smoke-test domain auto-enroll: sign up with an email whose domain matches an org's allowedDomains and confirm the user lands in that org (no new org / default team created).

Summary by cubic

Unblocks OAuth signups by ensuring the auto-created default team gets a slug. This removes the NOT NULL violation on auth.teams.slug during org creation.

  • Bug Fixes
    • Create the default team via teams.defaultTeam.customCreateDefaultTeam with name "Default Team" and slug="DEFAULT".
    • Remove the redundant team insert in afterCreateOrganization.
    • Only affects the auto-default team path; user-created teams keep user-chosen slugs.

Written for commit 8eb3055. Summary will update on new commits.

Summary by CodeRabbit

  • Refactor
    • Standardized default team creation during organization setup to use consistent naming conventions.

Review Change Stack

Better Auth's organization plugin auto-creates a default team during
createOrganization when teams.enabled is true (crud-org.mjs:106-136),
hard-coding teamData = { organizationId, name, createdAt } with no slug.
Our auth.teams.slug is NOT NULL with no default, so every new-user OAuth
signup was aborting with unable_to_create_user.

Use customCreateDefaultTeam to own the auto-team insert and supply a
constant "DEFAULT" slug. Drop the duplicate db.insert(teams) from
afterCreateOrganization — better-auth already creates exactly one team
per new org, the manual insert was just shadowed by the failing
auto-insert running first.
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 12, 2026

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: aa05d617-0250-4d39-af75-57c325c8b6fe

📥 Commits

Reviewing files that changed from the base of the PR and between 11afdf8 and 8eb3055.

📒 Files selected for processing (1)
  • packages/auth/src/server.ts

📝 Walkthrough

Walkthrough

Default team creation is refactored from inline hook logic to a dedicated configuration-driven function. The customCreateDefaultTeam handler now inserts a fixed "Default Team"/"DEFAULT" record when organizations are created, while the afterCreateOrganization hook delegates to Stripe setup and status seeding without managing team creation directly.

Changes

Default Team Creation

Layer / File(s) Summary
Default team creation refactor
packages/auth/src/server.ts
Enable organization.teams.defaultTeam with customCreateDefaultTeam function that creates a "Default Team"/"DEFAULT" record tied to new organizations; remove prior inline team insertion from afterCreateOrganization hook, which now proceeds to Stripe updates and status seeding without team record creation.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~8 minutes

Poem

🐰 A team is born when orgs take flight,
No longer tangled in the hook's tight bind,
"Default Team" now springs to light,
Clean separation, one thing in mind—
Organization setup, pure and right! 🌟

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch investigate/better-auth-teams-insert

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@saddlepaddle saddlepaddle merged commit 93cd110 into main May 12, 2026
15 of 16 checks passed
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 12, 2026

🚀 Preview Deployment

🔗 Preview Links

Service Status Link
Neon Database (Neon) View Branch
Vercel API (Vercel) Open Preview
Vercel Web (Vercel) Open Preview
Vercel Marketing (Vercel) Open Preview
Vercel Admin (Vercel) Open Preview
Vercel Docs (Vercel) Open Preview

Preview updates automatically with new commits

@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps Bot commented May 12, 2026

Greptile Summary

This PR fixes a regression introduced in #4403 where every new Google OAuth signup was failing because Better Auth's auto-default team insert omitted the slug field, violating the NOT NULL constraint on auth.teams.slug. The fix uses customCreateDefaultTeam to own the insert and supply slug="DEFAULT", and removes the now-redundant manual team insert from afterCreateOrganization.

  • The hardcoded "DEFAULT" slug is safe: the unique constraint on auth.teams is the composite (organizationId, slug), so each org can independently carry a team with slug "DEFAULT" without colliding.
  • The old afterCreateOrganization team insert is correctly dropped — it was unreachable anyway because the prior constraint violation aborted the transaction before that hook could fire.

Confidence Score: 4/5

Safe to merge; the change is narrow and directly addresses the broken signup flow.

The change is focused and the schema confirms slug uniqueness is scoped per-org, so the hardcoded DEFAULT value is safe across all orgs. One thing worth watching: customCreateDefaultTeam commits the team row before afterCreateOrganization runs, so a Stripe failure in that hook would leave an org with a committed team but no stripeCustomerId set — though this ordering concern is an artifact of the pre-existing hook structure, not something this PR makes worse.

Only packages/auth/src/server.ts changed; no other files need attention.

Important Files Changed

Filename Overview
packages/auth/src/server.ts Adds customCreateDefaultTeam with hardcoded slug="DEFAULT" to satisfy the NOT NULL constraint on team slug during org creation; removes the now-redundant manual team insert from afterCreateOrganization. Logic is correct given the composite unique index on (organizationId, slug).

Sequence Diagram

sequenceDiagram
    participant User
    participant BetterAuth
    participant customCreateDefaultTeam
    participant DB
    participant afterCreateOrg

    User->>BetterAuth: Google OAuth signup
    BetterAuth->>DB: INSERT INTO auth.organizations
    BetterAuth->>customCreateDefaultTeam: "organization {id, name, slug}"
    customCreateDefaultTeam->>DB: "INSERT INTO auth.teams (name=Default Team, slug=DEFAULT, organizationId)"
    DB-->>customCreateDefaultTeam: team row
    customCreateDefaultTeam-->>BetterAuth: team
    BetterAuth->>DB: INSERT INTO auth.team_members (teamId, userId)
    BetterAuth->>afterCreateOrg: "{organization, user}"
    afterCreateOrg->>DB: Stripe customer create + UPDATE organizations SET stripeCustomerId
    afterCreateOrg->>DB: seedDefaultStatuses(organizationId)
    afterCreateOrg-->>BetterAuth: done
    BetterAuth-->>User: signup complete
Loading

Reviews (1): Last reviewed commit: "fix(auth): supply slug for better-auth's..." | Re-trigger Greptile

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant