Skip to content

Commit 457ee8f

Browse files
os-zhuangclaude
andcommitted
feat(auth): add onOrganizationCreated seam for host org-create side effects
better-auth's organization-plugin models (organization/member) do NOT fire core databaseHooks, so a host stack had no server-side seam to run side effects when a user creates a 2nd+ organization. Add an optional onOrganizationCreated config callback, invoked from the org plugin's afterCreateOrganization hook with { organizationId, userId, name, slug }. Failure-isolated (org creation is never rolled back). The cloud control plane uses this to uphold its born-with-environment invariant: every organization, not just the signup/personal org, is born with its production environment. Without it a user-created org landed env-less and unusable. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
1 parent df7fc10 commit 457ee8f

1 file changed

Lines changed: 34 additions & 0 deletions

File tree

packages/plugins/plugin-auth/src/auth-manager.ts

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,21 @@ export interface AuthManagerOptions extends Partial<AuthConfig> {
165165
*/
166166
dataEngine?: IDataEngine;
167167

168+
/**
169+
* Optional callback invoked AFTER an organization is created via better-auth's
170+
* `createOrganization` (the org-plugin `afterCreateOrganization` hook). Lets a
171+
* host stack run org-creation side effects that core `databaseHooks` can't —
172+
* better-auth's org-plugin models (`organization`/`member`) do NOT fire those.
173+
* The cloud control plane uses it to provision an org's born-with production
174+
* environment. Failure-isolated: org creation is never rolled back.
175+
*/
176+
onOrganizationCreated?: (data: {
177+
organizationId: string;
178+
userId?: string;
179+
name?: string;
180+
slug?: string;
181+
}) => void | Promise<void>;
182+
168183
/**
169184
* Base path for auth routes
170185
* Forwarded to better-auth's basePath option so it can match incoming
@@ -741,6 +756,25 @@ export class AuthManager {
741756
});
742757
}
743758
},
759+
// Run host-provided org-creation side effects (e.g. the cloud control
760+
// plane provisions the org's born-with production environment). The
761+
// org-plugin's models don't fire core databaseHooks, so this is the
762+
// only server-side seam for "every org is born with its prod env".
763+
// Failure-isolated: org creation must not roll back on a side-effect miss.
764+
afterCreateOrganization: async ({ organization, member, user }: any) => {
765+
const cb = this.config.onOrganizationCreated;
766+
if (typeof cb !== 'function') return;
767+
try {
768+
await cb({
769+
organizationId: organization?.id,
770+
userId: user?.id ?? member?.userId,
771+
name: organization?.name,
772+
slug: organization?.slug,
773+
});
774+
} catch (err: any) {
775+
console.warn('[auth] onOrganizationCreated callback failed:', err?.message ?? String(err));
776+
}
777+
},
744778
beforeUpdateOrganization: async ({ organization, member }: any) => {
745779
const newSlug = organization?.slug;
746780
const orgId = member?.organizationId;

0 commit comments

Comments
 (0)