Skip to content

Commit 6bded05

Browse files
committed
feat(auth): auto-join organizations for self-hosted mode
When running in self-hosted mode with autoJoinOrganizations enabled: - First user signup creates a 'default' team organization (user becomes owner) - Subsequent users are automatically added as members to all existing team orgs The logic is inlined in auth handlers using existing querier methods.
1 parent 14d35c7 commit 6bded05

File tree

5 files changed

+58
-1
lines changed

5 files changed

+58
-1
lines changed

bun.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

internal/api/src/routes/auth/auth.server.ts

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -302,6 +302,28 @@ async function handleOAuthCallback(
302302
password: null,
303303
});
304304

305+
// Auto-join team organizations (self-hosted mode)
306+
if (c.env.autoJoinOrganizations) {
307+
const teamOrgs = await db.selectTeamOrganizations();
308+
if (teamOrgs.length === 0) {
309+
// First user: create default org, make them owner
310+
await db.insertOrganizationWithMembership({
311+
name: "default",
312+
kind: "organization",
313+
created_by: user.id,
314+
});
315+
} else {
316+
// Subsequent users: add as member to all existing team orgs
317+
for (const org of teamOrgs) {
318+
await db.insertOrganizationMembership({
319+
organization_id: org.id,
320+
user_id: user.id,
321+
role: "member",
322+
});
323+
}
324+
}
325+
}
326+
305327
// consume single-use invite (mark accepted)
306328
if (usedInviteId) {
307329
try {
@@ -926,6 +948,28 @@ export default function mountAuth(server: APIServer) {
926948
email_verified: emailVerified,
927949
});
928950

951+
// Auto-join team organizations (self-hosted mode)
952+
if (c.env.autoJoinOrganizations) {
953+
const teamOrgs = await db.selectTeamOrganizations();
954+
if (teamOrgs.length === 0) {
955+
// First user: create default org, make them owner
956+
await db.insertOrganizationWithMembership({
957+
name: "default",
958+
kind: "organization",
959+
created_by: user.id,
960+
});
961+
} else {
962+
// Subsequent users: add as member to all existing team orgs
963+
for (const org of teamOrgs) {
964+
await db.insertOrganizationMembership({
965+
organization_id: org.id,
966+
user_id: user.id,
967+
role: "member",
968+
});
969+
}
970+
}
971+
}
972+
929973
// Sync user to telemetry system (async, don't block)
930974
if (c.env.sendTelemetryEvent) {
931975
c.env

internal/api/src/server.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -236,6 +236,9 @@ export interface Bindings {
236236

237237
readonly serverVersion: string;
238238

239+
/** When true, auto-add new users to all existing team organizations (self-hosted mode) */
240+
readonly autoJoinOrganizations?: boolean;
241+
239242
// Optional AWS credentials used by platform logging to CloudWatch
240243
readonly AWS_ACCESS_KEY_ID?: string;
241244
readonly AWS_SECRET_ACCESS_KEY?: string;

internal/database/src/querier.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -326,6 +326,15 @@ export default class Querier {
326326
}
327327

328328
// selectOrganizationMembershipsByUserID fetches all organization memberships for a user.
329+
// selectTeamOrganizations fetches all team (non-personal) organizations.
330+
public async selectTeamOrganizations(): Promise<Organization[]> {
331+
return this.db
332+
.select()
333+
.from(organization)
334+
.where(eq(organization.kind, "organization"));
335+
}
336+
337+
329338
public async selectOrganizationMembershipsByUserID(userId: string): Promise<
330339
Array<{
331340
organization_membership: OrganizationMembership;

packages/server/src/server.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,7 @@ export async function startServer(options: ServerOptions) {
130130
{
131131
AUTH_SECRET: authSecret,
132132
NODE_ENV: "development",
133+
autoJoinOrganizations: true,
133134
serverVersion: pkg.version,
134135
ONBOARDING_AGENT_BUNDLE_URL:
135136
"https://artifacts.blink.host/starter-agent/bundle.tar.gz",

0 commit comments

Comments
 (0)