diff --git a/apps/web/src/lib/organizations/organizations.test.ts b/apps/web/src/lib/organizations/organizations.test.ts index 3e899339ad..417133c5ef 100644 --- a/apps/web/src/lib/organizations/organizations.test.ts +++ b/apps/web/src/lib/organizations/organizations.test.ts @@ -507,6 +507,36 @@ describe('Organizations', () => { expect(added).toBe(false); expect(await getUserOrganizationsWithSeats(member.id)).toHaveLength(0); }); + + test('disables the personal account for a brand-new user provisioned via SSO', async () => { + const owner = await insertTestUser(); + const member = await insertTestUser(); + const organization = await createOrganization('SSO Org', owner.id); + + const added = await addSsoUserToOrganization(organization.id, member.id, { + isNewUser: true, + }); + + expect(added).toBe(true); + const updatedMember = await db.query.kilocode_users.findFirst({ + where: eq(kilocode_users.id, member.id), + }); + expect(updatedMember?.personal_account_disabled).toBe(true); + }); + + test('leaves the personal account untouched for an existing user authenticating via SSO', async () => { + const owner = await insertTestUser(); + const member = await insertTestUser(); + const organization = await createOrganization('SSO Org', owner.id); + + const added = await addSsoUserToOrganization(organization.id, member.id); + + expect(added).toBe(true); + const updatedMember = await db.query.kilocode_users.findFirst({ + where: eq(kilocode_users.id, member.id), + }); + expect(updatedMember?.personal_account_disabled).toBe(false); + }); }); describe('updateUserRoleInOrganization', () => { diff --git a/apps/web/src/lib/organizations/organizations.ts b/apps/web/src/lib/organizations/organizations.ts index 3f58aca93e..fbc6c3f00e 100644 --- a/apps/web/src/lib/organizations/organizations.ts +++ b/apps/web/src/lib/organizations/organizations.ts @@ -338,7 +338,8 @@ async function lockOrganizationMembershipMutation( export async function addSsoUserToOrganization( organizationId: Organization['id'], - userId: User['id'] + userId: User['id'], + options?: { isNewUser?: boolean } ): Promise { return db.transaction(async tx => { await lockOrganizationMembershipMutation(tx, organizationId, userId); @@ -354,7 +355,20 @@ export async function addSsoUserToOrganization( .limit(1); if (removal) return false; - return addUserToOrganization(organizationId, userId, 'member', tx); + const added = await addUserToOrganization(organizationId, userId, 'member', tx); + + // A brand-new account provisioned through SSO exists only because of the + // organization, so it has no standalone personal account. Mirror the + // invite-driven signup behavior (acceptOrganizationInvite) and disable it. + // Existing users who authenticate through SSO keep their current value. + if (added && options?.isNewUser) { + await tx + .update(kilocode_users) + .set({ personal_account_disabled: true }) + .where(eq(kilocode_users.id, userId)); + } + + return added; }); } diff --git a/apps/web/src/lib/user/sso.test.ts b/apps/web/src/lib/user/sso.test.ts index e2e5ede085..88d9bc25dd 100644 --- a/apps/web/src/lib/user/sso.test.ts +++ b/apps/web/src/lib/user/sso.test.ts @@ -106,6 +106,8 @@ describe('processSSOUserLogin', () => { 'impact-click-123', trackingContext ); - expect(mockAddSsoUserToOrganization).toHaveBeenCalledWith('org-local', 'user-workos'); + expect(mockAddSsoUserToOrganization).toHaveBeenCalledWith('org-local', 'user-workos', { + isNewUser: true, + }); }); }); diff --git a/apps/web/src/lib/user/sso.ts b/apps/web/src/lib/user/sso.ts index 70f4fd455b..3e6f57cfb7 100644 --- a/apps/web/src/lib/user/sso.ts +++ b/apps/web/src/lib/user/sso.ts @@ -81,7 +81,9 @@ async function processSSOInternal( const savedUser = res.user; // add user to organization since its been fully created - const added = await addSsoUserToOrganization(kiloOrg.id, savedUser.id); + const added = await addSsoUserToOrganization(kiloOrg.id, savedUser.id, { + isNewUser: res.isNew, + }); if (added) { // get all owners for org const members = await getOrganizationMembers(kiloOrg.id);