diff --git a/CHANGELOG.md b/CHANGELOG.md index f868e5d93..b93737788 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Fixed issue where file references in copied chat answers were relative paths instead of full browse URLs. [#847](https://github.com/sourcebot-dev/sourcebot/pull/847) - [EE] Fixed issue where account driven permission syncing would fail when attempting to authenticate with a GitHub App user token. [#850](https://github.com/sourcebot-dev/sourcebot/pull/850) +### Added +- [EE] Added `AUTH_EE_ALLOW_EMAIL_ACCOUNT_LINKING` env var that, when enabled, will automatically link SSO accounts with the same email address. [#849](https://github.com/sourcebot-dev/sourcebot/pull/849) + ## [4.10.24] - 2026-02-03 ### Fixed diff --git a/docs/docs/configuration/environment-variables.mdx b/docs/docs/configuration/environment-variables.mdx index a073d8225..859fd1c39 100644 --- a/docs/docs/configuration/environment-variables.mdx +++ b/docs/docs/configuration/environment-variables.mdx @@ -62,6 +62,7 @@ The following environment variables allow you to configure your Sourcebot deploy | `AUTH_EE_GCP_IAP_ENABLED` | `false` |
When enabled, allows Sourcebot to automatically register/login from a successful GCP IAP redirect
| | `AUTH_EE_GCP_IAP_AUDIENCE` | - |The GCP IAP audience to use when verifying JWT tokens. Must be set to enable GCP IAP JIT provisioning
| | `EXPERIMENT_EE_PERMISSION_SYNC_ENABLED` | `false` |Enables [permission syncing](/docs/features/permission-syncing).
| +| `AUTH_EE_ALLOW_EMAIL_ACCOUNT_LINKING` | `false` |When enabled, different SSO accounts with the same email address will automatically be linked.
| ### Review Agent Environment Variables diff --git a/packages/shared/src/env.server.ts b/packages/shared/src/env.server.ts index 4da23b3ab..6b4ed8be1 100644 --- a/packages/shared/src/env.server.ts +++ b/packages/shared/src/env.server.ts @@ -140,6 +140,12 @@ export const env = createEnv({ AUTH_EMAIL_CODE_LOGIN_ENABLED: booleanSchema.default('false'), // Enterprise Auth + + AUTH_EE_ALLOW_EMAIL_ACCOUNT_LINKING: + booleanSchema + .default('false') + .describe('When enabled, different SSO accounts with the same email address will automatically be linked.'), + AUTH_EE_GITHUB_CLIENT_ID: z.string().optional(), AUTH_EE_GITHUB_CLIENT_SECRET: z.string().optional(), AUTH_EE_GITHUB_BASE_URL: z.string().optional(), diff --git a/packages/web/src/ee/features/sso/sso.ts b/packages/web/src/ee/features/sso/sso.ts index c43b32621..d687f5f68 100644 --- a/packages/web/src/ee/features/sso/sso.ts +++ b/packages/web/src/ee/features/sso/sso.ts @@ -139,6 +139,7 @@ const createGitHubProvider = (clientId: string, clientSecret: string, baseUrl?: ].join(' '), }, }, + allowDangerousEmailAccountLinking: env.AUTH_EE_ALLOW_EMAIL_ACCOUNT_LINKING === 'true', }); } @@ -168,6 +169,7 @@ const createGitLabProvider = (clientId: string, clientSecret: string, baseUrl?: userinfo: { url: `${url}/api/v4/user`, }, + allowDangerousEmailAccountLinking: env.AUTH_EE_ALLOW_EMAIL_ACCOUNT_LINKING === 'true', }); } @@ -175,6 +177,7 @@ const createGoogleProvider = (clientId: string, clientSecret: string): Provider return Google({ clientId: clientId, clientSecret: clientSecret, + allowDangerousEmailAccountLinking: env.AUTH_EE_ALLOW_EMAIL_ACCOUNT_LINKING === 'true', }); } @@ -183,6 +186,7 @@ const createOktaProvider = (clientId: string, clientSecret: string, issuer: stri clientId: clientId, clientSecret: clientSecret, issuer: issuer, + allowDangerousEmailAccountLinking: env.AUTH_EE_ALLOW_EMAIL_ACCOUNT_LINKING === 'true', }); } @@ -191,6 +195,7 @@ const createKeycloakProvider = (clientId: string, clientSecret: string, issuer: clientId: clientId, clientSecret: clientSecret, issuer: issuer, + allowDangerousEmailAccountLinking: env.AUTH_EE_ALLOW_EMAIL_ACCOUNT_LINKING === 'true', }); } @@ -199,6 +204,16 @@ const createMicrosoftEntraIDProvider = (clientId: string, clientSecret: string, clientId: clientId, clientSecret: clientSecret, issuer: issuer, + allowDangerousEmailAccountLinking: env.AUTH_EE_ALLOW_EMAIL_ACCOUNT_LINKING === 'true', + }); +} + +export const createAuthentikProvider = (clientId: string, clientSecret: string, issuer: string): Provider => { + return Authentik({ + clientId: clientId, + clientSecret: clientSecret, + issuer: issuer, + allowDangerousEmailAccountLinking: env.AUTH_EE_ALLOW_EMAIL_ACCOUNT_LINKING === 'true', }); } @@ -207,7 +222,7 @@ const createGCPIAPProvider = (audience: string): Provider => { id: "gcp-iap", name: "Google Cloud IAP", credentials: {}, - authorize: async (credentials, req) => { + authorize: async (_credentials, req) => { try { const iapAssertion = req.headers?.get("x-goog-iap-jwt-assertion"); if (!iapAssertion || typeof iapAssertion !== "string") { @@ -277,11 +292,3 @@ const createGCPIAPProvider = (audience: string): Provider => { }, }); } - -export const createAuthentikProvider = (clientId: string, clientSecret: string, issuer: string): Provider => { - return Authentik({ - clientId: clientId, - clientSecret: clientSecret, - issuer: issuer, - }); -} \ No newline at end of file