From eb0a85caadb7a3bd61f47c30b5a46e4f2df33a91 Mon Sep 17 00:00:00 2001 From: msukkari Date: Thu, 12 Mar 2026 09:33:31 -0700 Subject: [PATCH 1/2] feat: add JumpCloud as identity provider for SSO Adds support for JumpCloud as an OIDC identity provider, enabling SSO authentication. Includes schema definition, SSO provider registration, UI components, analytics tracking, logo, and documentation. Co-Authored-By: Claude Haiku 4.5 --- docs/docs/configuration/idp.mdx | 47 ++++ .../schemas/v3/identityProvider.schema.mdx | 218 ++++++++++++++++++ docs/snippets/schemas/v3/index.schema.mdx | 218 ++++++++++++++++++ .../schemas/src/v3/identityProvider.schema.ts | 218 ++++++++++++++++++ .../schemas/src/v3/identityProvider.type.ts | 44 ++++ packages/schemas/src/v3/index.schema.ts | 218 ++++++++++++++++++ packages/schemas/src/v3/index.type.ts | 44 ++++ packages/web/public/jumpcloud.svg | 1 + .../src/app/login/components/loginForm.tsx | 2 + packages/web/src/ee/features/sso/sso.ts | 26 ++- packages/web/src/lib/posthogEvents.ts | 1 + packages/web/src/lib/utils.ts | 10 + schemas/v3/identityProvider.json | 25 ++ 13 files changed, 1071 insertions(+), 1 deletion(-) create mode 100644 packages/web/public/jumpcloud.svg diff --git a/docs/docs/configuration/idp.mdx b/docs/docs/configuration/idp.mdx index 57c059a3f..5abab2130 100644 --- a/docs/docs/configuration/idp.mdx +++ b/docs/docs/configuration/idp.mdx @@ -520,4 +520,51 @@ An Authentik connection can be used for [authentication](/docs/configuration/aut +### JumpCloud + +A JumpCloud connection can be used for [authentication](/docs/configuration/auth). JumpCloud supports OIDC (OpenID Connect), which Sourcebot uses to authenticate users. + + + + + To begin, you must create an SSO application in JumpCloud to facilitate the identity provider connection. For more information, see the [JumpCloud OIDC documentation](https://jumpcloud.com/support/sso-with-oidc). + + When configuring your application: + - Set the SSO type to "OIDC" + - Add `/api/auth/callback/jumpcloud` to the redirect URIs (ex. https://sourcebot.coolcorp.com/api/auth/callback/jumpcloud) + - Set the login URL to `/login` + + After creating the application, note the `CLIENT_ID` and `CLIENT_SECRET`. The issuer URL is typically `https://oauth.id.jumpcloud.com`. + + + The client id, secret, and issuer URL are provided to Sourcebot via environment variables. These can be named whatever you like + (ex. `JUMPCLOUD_IDENTITY_PROVIDER_CLIENT_ID`, `JUMPCLOUD_IDENTITY_PROVIDER_CLIENT_SECRET`, and `JUMPCLOUD_IDENTITY_PROVIDER_ISSUER`) + + + Create a `identityProvider` object in the [config file](/docs/configuration/config-file) with the following fields: + + ```json wrap icon="code" + { + "$schema": "https://raw.githubusercontent.com/sourcebot-dev/sourcebot/main/schemas/v3/index.json", + "identityProviders": [ + { + "provider": "jumpcloud", + "purpose": "sso", + "clientId": { + "env": "JUMPCLOUD_IDENTITY_PROVIDER_CLIENT_ID" + }, + "clientSecret": { + "env": "JUMPCLOUD_IDENTITY_PROVIDER_CLIENT_SECRET" + }, + "issuer": { + "env": "JUMPCLOUD_IDENTITY_PROVIDER_ISSUER" + } + } + ] + } + ``` + + + + diff --git a/docs/snippets/schemas/v3/identityProvider.schema.mdx b/docs/snippets/schemas/v3/identityProvider.schema.mdx index fcd924308..1747c8619 100644 --- a/docs/snippets/schemas/v3/identityProvider.schema.mdx +++ b/docs/snippets/schemas/v3/identityProvider.schema.mdx @@ -842,6 +842,115 @@ "issuer" ] }, + "JumpCloudIdentityProviderConfig": { + "type": "object", + "additionalProperties": false, + "properties": { + "provider": { + "const": "jumpcloud" + }, + "purpose": { + "const": "sso" + }, + "clientId": { + "anyOf": [ + { + "type": "object", + "properties": { + "env": { + "type": "string", + "description": "The name of the environment variable that contains the token." + } + }, + "required": [ + "env" + ], + "additionalProperties": false + }, + { + "type": "object", + "properties": { + "googleCloudSecret": { + "type": "string", + "description": "The resource name of a Google Cloud secret. Must be in the format `projects//secrets//versions/`. See https://cloud.google.com/secret-manager/docs/creating-and-accessing-secrets" + } + }, + "required": [ + "googleCloudSecret" + ], + "additionalProperties": false + } + ] + }, + "clientSecret": { + "anyOf": [ + { + "type": "object", + "properties": { + "env": { + "type": "string", + "description": "The name of the environment variable that contains the token." + } + }, + "required": [ + "env" + ], + "additionalProperties": false + }, + { + "type": "object", + "properties": { + "googleCloudSecret": { + "type": "string", + "description": "The resource name of a Google Cloud secret. Must be in the format `projects//secrets//versions/`. See https://cloud.google.com/secret-manager/docs/creating-and-accessing-secrets" + } + }, + "required": [ + "googleCloudSecret" + ], + "additionalProperties": false + } + ] + }, + "issuer": { + "anyOf": [ + { + "type": "object", + "properties": { + "env": { + "type": "string", + "description": "The name of the environment variable that contains the token." + } + }, + "required": [ + "env" + ], + "additionalProperties": false + }, + { + "type": "object", + "properties": { + "googleCloudSecret": { + "type": "string", + "description": "The resource name of a Google Cloud secret. Must be in the format `projects//secrets//versions/`. See https://cloud.google.com/secret-manager/docs/creating-and-accessing-secrets" + } + }, + "required": [ + "googleCloudSecret" + ], + "additionalProperties": false + } + ] + } + }, + "required": [ + "provider", + "purpose", + "clientId", + "clientSecret", + "issuer" + ] + }, "BitbucketServerIdentityProviderConfig": { "type": "object", "additionalProperties": false, @@ -1776,6 +1885,115 @@ "clientSecret" ] }, + { + "type": "object", + "additionalProperties": false, + "properties": { + "provider": { + "const": "jumpcloud" + }, + "purpose": { + "const": "sso" + }, + "clientId": { + "anyOf": [ + { + "type": "object", + "properties": { + "env": { + "type": "string", + "description": "The name of the environment variable that contains the token." + } + }, + "required": [ + "env" + ], + "additionalProperties": false + }, + { + "type": "object", + "properties": { + "googleCloudSecret": { + "type": "string", + "description": "The resource name of a Google Cloud secret. Must be in the format `projects//secrets//versions/`. See https://cloud.google.com/secret-manager/docs/creating-and-accessing-secrets" + } + }, + "required": [ + "googleCloudSecret" + ], + "additionalProperties": false + } + ] + }, + "clientSecret": { + "anyOf": [ + { + "type": "object", + "properties": { + "env": { + "type": "string", + "description": "The name of the environment variable that contains the token." + } + }, + "required": [ + "env" + ], + "additionalProperties": false + }, + { + "type": "object", + "properties": { + "googleCloudSecret": { + "type": "string", + "description": "The resource name of a Google Cloud secret. Must be in the format `projects//secrets//versions/`. See https://cloud.google.com/secret-manager/docs/creating-and-accessing-secrets" + } + }, + "required": [ + "googleCloudSecret" + ], + "additionalProperties": false + } + ] + }, + "issuer": { + "anyOf": [ + { + "type": "object", + "properties": { + "env": { + "type": "string", + "description": "The name of the environment variable that contains the token." + } + }, + "required": [ + "env" + ], + "additionalProperties": false + }, + { + "type": "object", + "properties": { + "googleCloudSecret": { + "type": "string", + "description": "The resource name of a Google Cloud secret. Must be in the format `projects//secrets//versions/`. See https://cloud.google.com/secret-manager/docs/creating-and-accessing-secrets" + } + }, + "required": [ + "googleCloudSecret" + ], + "additionalProperties": false + } + ] + } + }, + "required": [ + "provider", + "purpose", + "clientId", + "clientSecret", + "issuer" + ] + }, { "type": "object", "additionalProperties": false, diff --git a/docs/snippets/schemas/v3/index.schema.mdx b/docs/snippets/schemas/v3/index.schema.mdx index 752947a82..36880a8f2 100644 --- a/docs/snippets/schemas/v3/index.schema.mdx +++ b/docs/snippets/schemas/v3/index.schema.mdx @@ -5536,6 +5536,115 @@ "issuer" ] }, + "JumpCloudIdentityProviderConfig": { + "type": "object", + "additionalProperties": false, + "properties": { + "provider": { + "const": "jumpcloud" + }, + "purpose": { + "const": "sso" + }, + "clientId": { + "anyOf": [ + { + "type": "object", + "properties": { + "env": { + "type": "string", + "description": "The name of the environment variable that contains the token." + } + }, + "required": [ + "env" + ], + "additionalProperties": false + }, + { + "type": "object", + "properties": { + "googleCloudSecret": { + "type": "string", + "description": "The resource name of a Google Cloud secret. Must be in the format `projects//secrets//versions/`. See https://cloud.google.com/secret-manager/docs/creating-and-accessing-secrets" + } + }, + "required": [ + "googleCloudSecret" + ], + "additionalProperties": false + } + ] + }, + "clientSecret": { + "anyOf": [ + { + "type": "object", + "properties": { + "env": { + "type": "string", + "description": "The name of the environment variable that contains the token." + } + }, + "required": [ + "env" + ], + "additionalProperties": false + }, + { + "type": "object", + "properties": { + "googleCloudSecret": { + "type": "string", + "description": "The resource name of a Google Cloud secret. Must be in the format `projects//secrets//versions/`. See https://cloud.google.com/secret-manager/docs/creating-and-accessing-secrets" + } + }, + "required": [ + "googleCloudSecret" + ], + "additionalProperties": false + } + ] + }, + "issuer": { + "anyOf": [ + { + "type": "object", + "properties": { + "env": { + "type": "string", + "description": "The name of the environment variable that contains the token." + } + }, + "required": [ + "env" + ], + "additionalProperties": false + }, + { + "type": "object", + "properties": { + "googleCloudSecret": { + "type": "string", + "description": "The resource name of a Google Cloud secret. Must be in the format `projects//secrets//versions/`. See https://cloud.google.com/secret-manager/docs/creating-and-accessing-secrets" + } + }, + "required": [ + "googleCloudSecret" + ], + "additionalProperties": false + } + ] + } + }, + "required": [ + "provider", + "purpose", + "clientId", + "clientSecret", + "issuer" + ] + }, "BitbucketServerIdentityProviderConfig": { "type": "object", "additionalProperties": false, @@ -6470,6 +6579,115 @@ "clientSecret" ] }, + { + "type": "object", + "additionalProperties": false, + "properties": { + "provider": { + "const": "jumpcloud" + }, + "purpose": { + "const": "sso" + }, + "clientId": { + "anyOf": [ + { + "type": "object", + "properties": { + "env": { + "type": "string", + "description": "The name of the environment variable that contains the token." + } + }, + "required": [ + "env" + ], + "additionalProperties": false + }, + { + "type": "object", + "properties": { + "googleCloudSecret": { + "type": "string", + "description": "The resource name of a Google Cloud secret. Must be in the format `projects//secrets//versions/`. See https://cloud.google.com/secret-manager/docs/creating-and-accessing-secrets" + } + }, + "required": [ + "googleCloudSecret" + ], + "additionalProperties": false + } + ] + }, + "clientSecret": { + "anyOf": [ + { + "type": "object", + "properties": { + "env": { + "type": "string", + "description": "The name of the environment variable that contains the token." + } + }, + "required": [ + "env" + ], + "additionalProperties": false + }, + { + "type": "object", + "properties": { + "googleCloudSecret": { + "type": "string", + "description": "The resource name of a Google Cloud secret. Must be in the format `projects//secrets//versions/`. See https://cloud.google.com/secret-manager/docs/creating-and-accessing-secrets" + } + }, + "required": [ + "googleCloudSecret" + ], + "additionalProperties": false + } + ] + }, + "issuer": { + "anyOf": [ + { + "type": "object", + "properties": { + "env": { + "type": "string", + "description": "The name of the environment variable that contains the token." + } + }, + "required": [ + "env" + ], + "additionalProperties": false + }, + { + "type": "object", + "properties": { + "googleCloudSecret": { + "type": "string", + "description": "The resource name of a Google Cloud secret. Must be in the format `projects//secrets//versions/`. See https://cloud.google.com/secret-manager/docs/creating-and-accessing-secrets" + } + }, + "required": [ + "googleCloudSecret" + ], + "additionalProperties": false + } + ] + } + }, + "required": [ + "provider", + "purpose", + "clientId", + "clientSecret", + "issuer" + ] + }, { "type": "object", "additionalProperties": false, diff --git a/packages/schemas/src/v3/identityProvider.schema.ts b/packages/schemas/src/v3/identityProvider.schema.ts index 555284887..2fd27a886 100644 --- a/packages/schemas/src/v3/identityProvider.schema.ts +++ b/packages/schemas/src/v3/identityProvider.schema.ts @@ -841,6 +841,115 @@ const schema = { "issuer" ] }, + "JumpCloudIdentityProviderConfig": { + "type": "object", + "additionalProperties": false, + "properties": { + "provider": { + "const": "jumpcloud" + }, + "purpose": { + "const": "sso" + }, + "clientId": { + "anyOf": [ + { + "type": "object", + "properties": { + "env": { + "type": "string", + "description": "The name of the environment variable that contains the token." + } + }, + "required": [ + "env" + ], + "additionalProperties": false + }, + { + "type": "object", + "properties": { + "googleCloudSecret": { + "type": "string", + "description": "The resource name of a Google Cloud secret. Must be in the format `projects//secrets//versions/`. See https://cloud.google.com/secret-manager/docs/creating-and-accessing-secrets" + } + }, + "required": [ + "googleCloudSecret" + ], + "additionalProperties": false + } + ] + }, + "clientSecret": { + "anyOf": [ + { + "type": "object", + "properties": { + "env": { + "type": "string", + "description": "The name of the environment variable that contains the token." + } + }, + "required": [ + "env" + ], + "additionalProperties": false + }, + { + "type": "object", + "properties": { + "googleCloudSecret": { + "type": "string", + "description": "The resource name of a Google Cloud secret. Must be in the format `projects//secrets//versions/`. See https://cloud.google.com/secret-manager/docs/creating-and-accessing-secrets" + } + }, + "required": [ + "googleCloudSecret" + ], + "additionalProperties": false + } + ] + }, + "issuer": { + "anyOf": [ + { + "type": "object", + "properties": { + "env": { + "type": "string", + "description": "The name of the environment variable that contains the token." + } + }, + "required": [ + "env" + ], + "additionalProperties": false + }, + { + "type": "object", + "properties": { + "googleCloudSecret": { + "type": "string", + "description": "The resource name of a Google Cloud secret. Must be in the format `projects//secrets//versions/`. See https://cloud.google.com/secret-manager/docs/creating-and-accessing-secrets" + } + }, + "required": [ + "googleCloudSecret" + ], + "additionalProperties": false + } + ] + } + }, + "required": [ + "provider", + "purpose", + "clientId", + "clientSecret", + "issuer" + ] + }, "BitbucketServerIdentityProviderConfig": { "type": "object", "additionalProperties": false, @@ -1775,6 +1884,115 @@ const schema = { "clientSecret" ] }, + { + "type": "object", + "additionalProperties": false, + "properties": { + "provider": { + "const": "jumpcloud" + }, + "purpose": { + "const": "sso" + }, + "clientId": { + "anyOf": [ + { + "type": "object", + "properties": { + "env": { + "type": "string", + "description": "The name of the environment variable that contains the token." + } + }, + "required": [ + "env" + ], + "additionalProperties": false + }, + { + "type": "object", + "properties": { + "googleCloudSecret": { + "type": "string", + "description": "The resource name of a Google Cloud secret. Must be in the format `projects//secrets//versions/`. See https://cloud.google.com/secret-manager/docs/creating-and-accessing-secrets" + } + }, + "required": [ + "googleCloudSecret" + ], + "additionalProperties": false + } + ] + }, + "clientSecret": { + "anyOf": [ + { + "type": "object", + "properties": { + "env": { + "type": "string", + "description": "The name of the environment variable that contains the token." + } + }, + "required": [ + "env" + ], + "additionalProperties": false + }, + { + "type": "object", + "properties": { + "googleCloudSecret": { + "type": "string", + "description": "The resource name of a Google Cloud secret. Must be in the format `projects//secrets//versions/`. See https://cloud.google.com/secret-manager/docs/creating-and-accessing-secrets" + } + }, + "required": [ + "googleCloudSecret" + ], + "additionalProperties": false + } + ] + }, + "issuer": { + "anyOf": [ + { + "type": "object", + "properties": { + "env": { + "type": "string", + "description": "The name of the environment variable that contains the token." + } + }, + "required": [ + "env" + ], + "additionalProperties": false + }, + { + "type": "object", + "properties": { + "googleCloudSecret": { + "type": "string", + "description": "The resource name of a Google Cloud secret. Must be in the format `projects//secrets//versions/`. See https://cloud.google.com/secret-manager/docs/creating-and-accessing-secrets" + } + }, + "required": [ + "googleCloudSecret" + ], + "additionalProperties": false + } + ] + } + }, + "required": [ + "provider", + "purpose", + "clientId", + "clientSecret", + "issuer" + ] + }, { "type": "object", "additionalProperties": false, diff --git a/packages/schemas/src/v3/identityProvider.type.ts b/packages/schemas/src/v3/identityProvider.type.ts index f409e96c9..5eebe2596 100644 --- a/packages/schemas/src/v3/identityProvider.type.ts +++ b/packages/schemas/src/v3/identityProvider.type.ts @@ -10,6 +10,7 @@ export type IdentityProviderConfig = | GCPIAPIdentityProviderConfig | AuthentikIdentityProviderConfig | BitbucketCloudIdentityProviderConfig + | JumpCloudIdentityProviderConfig | BitbucketServerIdentityProviderConfig; export interface GitHubIdentityProviderConfig { @@ -332,6 +333,49 @@ export interface BitbucketCloudIdentityProviderConfig { }; accountLinkingRequired?: boolean; } +export interface JumpCloudIdentityProviderConfig { + provider: "jumpcloud"; + purpose: "sso"; + clientId: + | { + /** + * The name of the environment variable that contains the token. + */ + env: string; + } + | { + /** + * The resource name of a Google Cloud secret. Must be in the format `projects//secrets//versions/`. See https://cloud.google.com/secret-manager/docs/creating-and-accessing-secrets + */ + googleCloudSecret: string; + }; + clientSecret: + | { + /** + * The name of the environment variable that contains the token. + */ + env: string; + } + | { + /** + * The resource name of a Google Cloud secret. Must be in the format `projects//secrets//versions/`. See https://cloud.google.com/secret-manager/docs/creating-and-accessing-secrets + */ + googleCloudSecret: string; + }; + issuer: + | { + /** + * The name of the environment variable that contains the token. + */ + env: string; + } + | { + /** + * The resource name of a Google Cloud secret. Must be in the format `projects//secrets//versions/`. See https://cloud.google.com/secret-manager/docs/creating-and-accessing-secrets + */ + googleCloudSecret: string; + }; +} export interface BitbucketServerIdentityProviderConfig { provider: "bitbucket-server"; purpose: "sso" | "account_linking"; diff --git a/packages/schemas/src/v3/index.schema.ts b/packages/schemas/src/v3/index.schema.ts index 41539d3ee..ffd0250b8 100644 --- a/packages/schemas/src/v3/index.schema.ts +++ b/packages/schemas/src/v3/index.schema.ts @@ -5535,6 +5535,115 @@ const schema = { "issuer" ] }, + "JumpCloudIdentityProviderConfig": { + "type": "object", + "additionalProperties": false, + "properties": { + "provider": { + "const": "jumpcloud" + }, + "purpose": { + "const": "sso" + }, + "clientId": { + "anyOf": [ + { + "type": "object", + "properties": { + "env": { + "type": "string", + "description": "The name of the environment variable that contains the token." + } + }, + "required": [ + "env" + ], + "additionalProperties": false + }, + { + "type": "object", + "properties": { + "googleCloudSecret": { + "type": "string", + "description": "The resource name of a Google Cloud secret. Must be in the format `projects//secrets//versions/`. See https://cloud.google.com/secret-manager/docs/creating-and-accessing-secrets" + } + }, + "required": [ + "googleCloudSecret" + ], + "additionalProperties": false + } + ] + }, + "clientSecret": { + "anyOf": [ + { + "type": "object", + "properties": { + "env": { + "type": "string", + "description": "The name of the environment variable that contains the token." + } + }, + "required": [ + "env" + ], + "additionalProperties": false + }, + { + "type": "object", + "properties": { + "googleCloudSecret": { + "type": "string", + "description": "The resource name of a Google Cloud secret. Must be in the format `projects//secrets//versions/`. See https://cloud.google.com/secret-manager/docs/creating-and-accessing-secrets" + } + }, + "required": [ + "googleCloudSecret" + ], + "additionalProperties": false + } + ] + }, + "issuer": { + "anyOf": [ + { + "type": "object", + "properties": { + "env": { + "type": "string", + "description": "The name of the environment variable that contains the token." + } + }, + "required": [ + "env" + ], + "additionalProperties": false + }, + { + "type": "object", + "properties": { + "googleCloudSecret": { + "type": "string", + "description": "The resource name of a Google Cloud secret. Must be in the format `projects//secrets//versions/`. See https://cloud.google.com/secret-manager/docs/creating-and-accessing-secrets" + } + }, + "required": [ + "googleCloudSecret" + ], + "additionalProperties": false + } + ] + } + }, + "required": [ + "provider", + "purpose", + "clientId", + "clientSecret", + "issuer" + ] + }, "BitbucketServerIdentityProviderConfig": { "type": "object", "additionalProperties": false, @@ -6469,6 +6578,115 @@ const schema = { "clientSecret" ] }, + { + "type": "object", + "additionalProperties": false, + "properties": { + "provider": { + "const": "jumpcloud" + }, + "purpose": { + "const": "sso" + }, + "clientId": { + "anyOf": [ + { + "type": "object", + "properties": { + "env": { + "type": "string", + "description": "The name of the environment variable that contains the token." + } + }, + "required": [ + "env" + ], + "additionalProperties": false + }, + { + "type": "object", + "properties": { + "googleCloudSecret": { + "type": "string", + "description": "The resource name of a Google Cloud secret. Must be in the format `projects//secrets//versions/`. See https://cloud.google.com/secret-manager/docs/creating-and-accessing-secrets" + } + }, + "required": [ + "googleCloudSecret" + ], + "additionalProperties": false + } + ] + }, + "clientSecret": { + "anyOf": [ + { + "type": "object", + "properties": { + "env": { + "type": "string", + "description": "The name of the environment variable that contains the token." + } + }, + "required": [ + "env" + ], + "additionalProperties": false + }, + { + "type": "object", + "properties": { + "googleCloudSecret": { + "type": "string", + "description": "The resource name of a Google Cloud secret. Must be in the format `projects//secrets//versions/`. See https://cloud.google.com/secret-manager/docs/creating-and-accessing-secrets" + } + }, + "required": [ + "googleCloudSecret" + ], + "additionalProperties": false + } + ] + }, + "issuer": { + "anyOf": [ + { + "type": "object", + "properties": { + "env": { + "type": "string", + "description": "The name of the environment variable that contains the token." + } + }, + "required": [ + "env" + ], + "additionalProperties": false + }, + { + "type": "object", + "properties": { + "googleCloudSecret": { + "type": "string", + "description": "The resource name of a Google Cloud secret. Must be in the format `projects//secrets//versions/`. See https://cloud.google.com/secret-manager/docs/creating-and-accessing-secrets" + } + }, + "required": [ + "googleCloudSecret" + ], + "additionalProperties": false + } + ] + } + }, + "required": [ + "provider", + "purpose", + "clientId", + "clientSecret", + "issuer" + ] + }, { "type": "object", "additionalProperties": false, diff --git a/packages/schemas/src/v3/index.type.ts b/packages/schemas/src/v3/index.type.ts index fd7c8d6a3..eee14391a 100644 --- a/packages/schemas/src/v3/index.type.ts +++ b/packages/schemas/src/v3/index.type.ts @@ -36,6 +36,7 @@ export type IdentityProviderConfig = | GCPIAPIdentityProviderConfig | AuthentikIdentityProviderConfig | BitbucketCloudIdentityProviderConfig + | JumpCloudIdentityProviderConfig | BitbucketServerIdentityProviderConfig; export interface SourcebotConfig { @@ -1576,6 +1577,49 @@ export interface BitbucketCloudIdentityProviderConfig { }; accountLinkingRequired?: boolean; } +export interface JumpCloudIdentityProviderConfig { + provider: "jumpcloud"; + purpose: "sso"; + clientId: + | { + /** + * The name of the environment variable that contains the token. + */ + env: string; + } + | { + /** + * The resource name of a Google Cloud secret. Must be in the format `projects//secrets//versions/`. See https://cloud.google.com/secret-manager/docs/creating-and-accessing-secrets + */ + googleCloudSecret: string; + }; + clientSecret: + | { + /** + * The name of the environment variable that contains the token. + */ + env: string; + } + | { + /** + * The resource name of a Google Cloud secret. Must be in the format `projects//secrets//versions/`. See https://cloud.google.com/secret-manager/docs/creating-and-accessing-secrets + */ + googleCloudSecret: string; + }; + issuer: + | { + /** + * The name of the environment variable that contains the token. + */ + env: string; + } + | { + /** + * The resource name of a Google Cloud secret. Must be in the format `projects//secrets//versions/`. See https://cloud.google.com/secret-manager/docs/creating-and-accessing-secrets + */ + googleCloudSecret: string; + }; +} export interface BitbucketServerIdentityProviderConfig { provider: "bitbucket-server"; purpose: "sso" | "account_linking"; diff --git a/packages/web/public/jumpcloud.svg b/packages/web/public/jumpcloud.svg new file mode 100644 index 000000000..59875b6ae --- /dev/null +++ b/packages/web/public/jumpcloud.svg @@ -0,0 +1 @@ + diff --git a/packages/web/src/app/login/components/loginForm.tsx b/packages/web/src/app/login/components/loginForm.tsx index 1f8917235..dac25ba9b 100644 --- a/packages/web/src/app/login/components/loginForm.tsx +++ b/packages/web/src/app/login/components/loginForm.tsx @@ -58,6 +58,8 @@ export const LoginForm = ({ callbackUrl, error, providers, context, isAnonymousA return "wa_login_with_keycloak" as const; case "microsoft-entra-id": return "wa_login_with_microsoft_entra_id" as const; + case "jumpcloud": + return "wa_login_with_jumpcloud" as const; default: return "wa_login_with_github" as const; // fallback } diff --git a/packages/web/src/ee/features/sso/sso.ts b/packages/web/src/ee/features/sso/sso.ts index daf0216cb..bbfa7015c 100644 --- a/packages/web/src/ee/features/sso/sso.ts +++ b/packages/web/src/ee/features/sso/sso.ts @@ -1,7 +1,7 @@ import type { IdentityProvider } from "@/auth"; import { onCreateUser } from "@/lib/authUtils"; import { prisma } from "@/prisma"; -import { AuthentikIdentityProviderConfig, BitbucketCloudIdentityProviderConfig, BitbucketServerIdentityProviderConfig, GCPIAPIdentityProviderConfig, GitHubIdentityProviderConfig, GitLabIdentityProviderConfig, GoogleIdentityProviderConfig, KeycloakIdentityProviderConfig, MicrosoftEntraIDIdentityProviderConfig, OktaIdentityProviderConfig } from "@sourcebot/schemas/v3/index.type"; +import { AuthentikIdentityProviderConfig, BitbucketCloudIdentityProviderConfig, BitbucketServerIdentityProviderConfig, GCPIAPIdentityProviderConfig, GitHubIdentityProviderConfig, GitLabIdentityProviderConfig, GoogleIdentityProviderConfig, JumpCloudIdentityProviderConfig, KeycloakIdentityProviderConfig, MicrosoftEntraIDIdentityProviderConfig, OktaIdentityProviderConfig } from "@sourcebot/schemas/v3/index.type"; import type { IdentityProviderType } from "@sourcebot/shared"; import { createLogger, env, getTokenFromConfig, hasEntitlement, loadConfig } from "@sourcebot/shared"; import { OAuth2Client } from "google-auth-library"; @@ -133,6 +133,18 @@ export const getEEIdentityProviders = async (): Promise => { }); } + if (identityProvider.provider === "jumpcloud") { + const providerConfig = identityProvider as JumpCloudIdentityProviderConfig; + const clientId = await getTokenFromConfig(providerConfig.clientId); + const clientSecret = await getTokenFromConfig(providerConfig.clientSecret); + const issuer = (await getTokenFromConfig(providerConfig.issuer)).replace(/\/+$/, ''); + providers.push({ + provider: createJumpCloudProvider(clientId, clientSecret, issuer), + purpose: providerConfig.purpose, + issuerUrl: issuer + }); + } + if (identityProvider.provider === "bitbucket-server") { const providerConfig = identityProvider as BitbucketServerIdentityProviderConfig; const clientId = await getTokenFromConfig(providerConfig.clientId); @@ -422,6 +434,18 @@ export const createAuthentikProvider = (clientId: string, clientSecret: string, }); } +const createJumpCloudProvider = (clientId: string, clientSecret: string, issuer: string): Provider => { + return { + id: 'jumpcloud' satisfies IdentityProviderType, + name: "JumpCloud", + type: "oidc", + clientId: clientId, + clientSecret: clientSecret, + issuer: issuer, + allowDangerousEmailAccountLinking: env.AUTH_EE_ALLOW_EMAIL_ACCOUNT_LINKING === 'true', + } as Provider; +} + const createGCPIAPProvider = (audience: string): Provider => { return Credentials({ id: 'gcp-iap' satisfies IdentityProviderType, diff --git a/packages/web/src/lib/posthogEvents.ts b/packages/web/src/lib/posthogEvents.ts index d73eafa21..6eee712d6 100644 --- a/packages/web/src/lib/posthogEvents.ts +++ b/packages/web/src/lib/posthogEvents.ts @@ -113,6 +113,7 @@ export type PosthogEventMap = { wa_login_with_okta: {}, wa_login_with_keycloak: {}, wa_login_with_microsoft_entra_id: {}, + wa_login_with_jumpcloud: {}, wa_login_with_magic_link: {}, wa_login_with_credentials: {}, ////////////////////////////////////////////////////////////////// diff --git a/packages/web/src/lib/utils.ts b/packages/web/src/lib/utils.ts index b79ca4ae0..dd7f783e5 100644 --- a/packages/web/src/lib/utils.ts +++ b/packages/web/src/lib/utils.ts @@ -12,6 +12,7 @@ import oktaLogo from "@/public/okta.svg"; import keycloakLogo from "@/public/keycloak.svg"; import microsoftLogo from "@/public/microsoft_entra.svg"; import authentikLogo from "@/public/authentik.svg"; +import jumpcloudLogo from "@/public/jumpcloud.svg"; import { ServiceError } from "./serviceError"; import { StatusCodes } from "http-status-codes"; import { ErrorCode } from "./errorCodes"; @@ -137,6 +138,15 @@ export const getAuthProviderInfo = (providerId: string): AuthProviderInfo => { src: authentikLogo, }, } + case "jumpcloud": + return { + id: "jumpcloud", + name: "JumpCloud", + displayName: "JumpCloud", + icon: { + src: jumpcloudLogo, + }, + }; case "credentials": return { id: "credentials", diff --git a/schemas/v3/identityProvider.json b/schemas/v3/identityProvider.json index 0f288703a..d4ac9e38f 100644 --- a/schemas/v3/identityProvider.json +++ b/schemas/v3/identityProvider.json @@ -216,6 +216,28 @@ }, "required": ["provider", "purpose", "clientId", "clientSecret", "issuer"] }, + "JumpCloudIdentityProviderConfig": { + "type": "object", + "additionalProperties": false, + "properties": { + "provider": { + "const": "jumpcloud" + }, + "purpose": { + "const": "sso" + }, + "clientId": { + "$ref": "./shared.json#/definitions/Token" + }, + "clientSecret": { + "$ref": "./shared.json#/definitions/Token" + }, + "issuer": { + "$ref": "./shared.json#/definitions/Token" + } + }, + "required": ["provider", "purpose", "clientId", "clientSecret", "issuer"] + }, "BitbucketServerIdentityProviderConfig": { "type": "object", "additionalProperties": false, @@ -274,6 +296,9 @@ { "$ref": "#/definitions/BitbucketCloudIdentityProviderConfig" }, + { + "$ref": "#/definitions/JumpCloudIdentityProviderConfig" + }, { "$ref": "#/definitions/BitbucketServerIdentityProviderConfig" } From 16c77101986366dbbd6cce2b582d4d2b5be5e25d Mon Sep 17 00:00:00 2001 From: msukkari Date: Thu, 12 Mar 2026 09:33:54 -0700 Subject: [PATCH 2/2] chore: update CHANGELOG for JumpCloud SSO feature --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 86aa6a383..0e6023f7e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Added +- [EE] Added JumpCloud as an identity provider for SSO authentication. [#997](https://github.com/sourcebot-dev/sourcebot/pull/997) + ### Changed - Require explicit invocation of ask_codebase tool in MCP [#995](https://github.com/sourcebot-dev/sourcebot/pull/995) - Gate MCP API behind authentication when Ask GitHub is enabled. [#994](https://github.com/sourcebot-dev/sourcebot/pull/994)