Skip to content

Commit a7c1efb

Browse files
kylecarbsblink-so[bot]
authored andcommitted
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 6700024 commit a7c1efb

5 files changed

Lines changed: 69 additions & 14 deletions

File tree

bun.lock

Lines changed: 13 additions & 14 deletions
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: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -326,6 +326,14 @@ 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+
329337
public async selectOrganizationMembershipsByUserID(userId: string): Promise<
330338
Array<{
331339
organization_membership: OrganizationMembership;

packages/server/src/server.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,7 @@ export async function startServer(options: ServerOptions) {
145145
{
146146
AUTH_SECRET: authSecret,
147147
NODE_ENV: "development",
148+
autoJoinOrganizations: true,
148149
serverVersion: pkg.version,
149150
ONBOARDING_AGENT_BUNDLE_URL:
150151
"https://artifacts.blink.host/starter-agent/bundle.tar.gz",

0 commit comments

Comments
 (0)