diff --git a/CHANGELOG.md b/CHANGELOG.md index 3397dc81..24e03b1a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,9 +21,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Removed - Removed built-in secret manager. [#592](https://github.com/sourcebot-dev/sourcebot/pull/592) -## Removed -- Removed built-in secret manager. [#592](https://github.com/sourcebot-dev/sourcebot/pull/592) - ## [4.8.1] - 2025-10-29 ### Fixed diff --git a/docs/docs/configuration/auth/providers.mdx b/docs/docs/configuration/auth/providers.mdx index c3d54363..91328b37 100644 --- a/docs/docs/configuration/auth/providers.mdx +++ b/docs/docs/configuration/auth/providers.mdx @@ -26,95 +26,5 @@ See [transactional emails](/docs/configuration/transactional-emails) for more de # Enterprise Authentication Providers -The following authentication providers require an [enterprise license](/docs/license-key) to be enabled. - -### GitHub ---- - -[Auth.js GitHub Provider Docs](https://authjs.dev/getting-started/providers/github) - -Authentication using both a **GitHub OAuth App** and a **GitHub App** is supported. In both cases, you must provide Sourcebot the `CLIENT_ID` and `SECRET_ID` and configure the -callback URL correctly (more info in Auth.js docs). - -When using a **GitHub App** for auth, enable the following permissions: -- `“Email addresses” account permissions (read)` -- `"Metadata" repository permissions (read)` (only needed if enabling [permission syncing](/docs/features/permission-syncing)) - -**Required environment variables:** -- `AUTH_EE_GITHUB_CLIENT_ID` -- `AUTH_EE_GITHUB_CLIENT_SECRET` - -Optional environment variables: -- `AUTH_EE_GITHUB_BASE_URL` - Base URL for GitHub Enterprise (defaults to https://github.com) - -### GitLab ---- - -[Auth.js GitLab Provider Docs](https://authjs.dev/getting-started/providers/gitlab) - -Authentication using GitLab is supported via a [OAuth2.0 app](https://docs.gitlab.com/integration/oauth_provider/#create-an-instance-wide-application) installed on the GitLab instance. Follow the instructions in the [GitLab docs](https://docs.gitlab.com/integration/oauth_provider/) to create an app. The callback URL should be configurd to `/api/auth/callback/gitlab`, and the following scopes need to be set: - -| Scope | Required | Notes | -|------------|----------|----------------------------------------------------------------------------------------------------| -| read_user | Yes | Allows Sourcebot to read basic user information required for authentication. | -| read_api | Conditional | Required **only** when [permission syncing](/docs/features/permission-syncing) is enabled. Enables Sourcebot to list all repositories and projects for the authenticated user. | - - -**Required environment variables:** -- `AUTH_EE_GITLAB_CLIENT_ID` -- `AUTH_EE_GITLAB_CLIENT_SECRET` - -Optional environment variables: -- `AUTH_EE_GITLAB_BASE_URL` - Base URL for GitLab instance (defaults to https://gitlab.com) - -### Google ---- - -[Auth.js Google Provider Docs](https://authjs.dev/getting-started/providers/google) - -**Required environment variables:** -- `AUTH_EE_GOOGLE_CLIENT_ID` -- `AUTH_EE_GOOGLE_CLIENT_SECRET` - -### GCP IAP ---- - -If you're running Sourcebot in an environment that blocks egress, make sure you allow the [IAP IP ranges](https://www.gstatic.com/ipranges/goog.json) - -Custom provider built to enable automatic Sourcebot account registration/login when using GCP IAP. - -**Required environment variables** -- `AUTH_EE_GCP_IAP_ENABLED` -- `AUTH_EE_GCP_IAP_AUDIENCE` - - This can be found by selecting the ⋮ icon next to the IAP-enabled backend service and pressing `Get JWT audience code` - -### Okta ---- - -[Auth.js Okta Provider Docs](https://authjs.dev/getting-started/providers/okta) - -**Required environment variables:** -- `AUTH_EE_OKTA_CLIENT_ID` -- `AUTH_EE_OKTA_CLIENT_SECRET` -- `AUTH_EE_OKTA_ISSUER` - -### Keycloak ---- - -[Auth.js Keycloak Provider Docs](https://authjs.dev/getting-started/providers/keycloak) - -**Required environment variables:** -- `AUTH_EE_KEYCLOAK_CLIENT_ID` -- `AUTH_EE_KEYCLOAK_CLIENT_SECRET` -- `AUTH_EE_KEYCLOAK_ISSUER` - -### Microsoft Entra ID - -[Auth.js Microsoft Entra ID Provider Docs](https://authjs.dev/getting-started/providers/microsoft-entra-id) - -**Required environment variables:** -- `AUTH_EE_MICROSOFT_ENTRA_ID_CLIENT_ID` -- `AUTH_EE_MICROSOFT_ENTRA_ID_CLIENT_SECRET` -- `AUTH_EE_MICROSOFT_ENTRA_ID_ISSUER` - ---- \ No newline at end of file +Sourcebot supports authentication using several different [external identity providers](/docs/configuration/idp) as well. These identity providers require an +[enterprise license](/docs/license-key) \ No newline at end of file diff --git a/docs/docs/configuration/idp.mdx b/docs/docs/configuration/idp.mdx index f8edb811..af4f8e05 100644 --- a/docs/docs/configuration/idp.mdx +++ b/docs/docs/configuration/idp.mdx @@ -18,8 +18,8 @@ External identity providers can be used for [authentication](/docs/configuration "identityProviders": [ { "provider": "github", - "purpose": "integration", - "required": true, + "purpose": "account_linking", + "accountLinkingRequired": true, /* Secrets are provided through environment variables. Set the secret into an env var and provide the name here to tell Sourcebot where to get @@ -61,7 +61,7 @@ in the GitHub identity provider config. To begin, you must register an Oauth client in GitHub to faciliate the identity provider connection. You can do this by creating a **GitHub App** or a **GitHub OAuth App**. Either - one works, but the **GitHub App** is the [modern way](https://docs.github.com/en/apps/oauth-apps/building-oauth-apps/differences-between-github-apps-and-oauth-apps). + one works, but the **GitHub App** is the [recommended mechanism](https://docs.github.com/en/apps/oauth-apps/building-oauth-apps/differences-between-github-apps-and-oauth-apps). The result of registering an OAuth client is a `CLIENT_ID` and `CLIENT_SECRET` which you'll provide to Sourcebot. @@ -74,6 +74,7 @@ in the GitHub identity provider config. Set the following fine-grained permissions in the GitHub App: - `“Email addresses” account permissions (read)` + - `"Metadata" repository permissions (read)` (only needed if using permission syncing) Follow [this guide](https://docs.github.com/en/apps/oauth-apps/building-oauth-apps/creating-an-oauth-app) by GitHub to create an OAuth App. @@ -95,10 +96,10 @@ in the GitHub identity provider config. "identityProviders": [ { "provider": "github", - // "sso" for auth + perm sync, "integration" for only perm sync - "purpose": "integration", - // if purpose == "integration" this controls if a user must connect to the IdP - "required": true, + // "sso" for auth + perm sync, "account_linking" for only perm sync + "purpose": "account_linking", + // if purpose == "account_linking" this controls if a user must connect to the IdP + "accountLinkingRequired": true, "clientId": { "env": "YOUR_CLIENT_ID_ENV_VAR" }, @@ -115,11 +116,256 @@ in the GitHub identity provider config. ### GitLab +[Auth.js GitLab Provider Docs](https://authjs.dev/getting-started/providers/gitlab) + +A GitLab connection can be used for either [authentication](/docs/configuration/auth) or [permission syncing](/docs/features/permission-syncing). This is controlled using the `purpose` field +in the GitLab identity provider config. + + + + + To begin, you must register an OAuth application in GitLab to facilitate the identity provider connection. + + Follow [this guide](https://docs.gitlab.com/integration/oauth_provider/) by GitLab to create an OAuth application. + + When configuring your application: + - Set the callback URL to `/api/auth/callback/gitlab` (ex. https://sourcebot.coolcorp.com/api/auth/callback/gitlab) + - Enable the `read_user` scope + - If using for permission syncing, also enable the `read_api` scope + + The result of registering an OAuth application is an `APPLICATION_ID` (`CLIENT_ID`) and `SECRET` (`CLIENT_SECRET`) which you'll provide to Sourcebot. + + + To provide Sourcebot the client id and secret for your OAuth application you must set them as environment variables. These can be named whatever you like + (ex. `GITLAB_IDENTITY_PROVIDER_CLIENT_ID` and `GITLAB_IDENTITY_PROVIDER_CLIENT_SECRET`) + + + Finally, pass the client id and secret to Sourcebot by defining a `identityProvider` object in the [config file](/docs/configuration/config-file): + + ```json wrap icon="code" + { + "$schema": "https://raw.githubusercontent.com/sourcebot-dev/sourcebot/main/schemas/v3/index.json", + "identityProviders": [ + { + "provider": "gitlab", + // "sso" for auth + perm sync, "account_linking" for only perm sync + "purpose": "account_linking", + // if purpose == "account_linking" this controls if a user must connect to the IdP + "accountLinkingRequired": true, + "clientId": { + "env": "YOUR_CLIENT_ID_ENV_VAR" + }, + "clientSecret": { + "env": "YOUR_CLIENT_SECRET_ENV_VAR" + }, + // Optional: for self-hosted GitLab instances + "baseUrl": "https://gitlab.example.com" + } + ] + } + ``` + + + + ### Google +[Auth.js Google Provider Docs](https://authjs.dev/getting-started/providers/google) + +A Google connection can be used for [authentication](/docs/configuration/auth). + + + + + To begin, you must register an OAuth client in Google Cloud Console to facilitate the identity provider connection. + + Follow [this guide](https://support.google.com/cloud/answer/6158849) by Google to create OAuth 2.0 credentials. + + When configuring your OAuth client: + - Set the application type to "Web application" + - Add `/api/auth/callback/google` to the authorized redirect URIs (ex. https://sourcebot.coolcorp.com/api/auth/callback/google) + + The result of creating OAuth credentials is a `CLIENT_ID` and `CLIENT_SECRET` which you'll provide to Sourcebot. + + + To provide Sourcebot the client id and secret for your OAuth client you must set them as environment variables. These can be named whatever you like + (ex. `GOOGLE_IDENTITY_PROVIDER_CLIENT_ID` and `GOOGLE_IDENTITY_PROVIDER_CLIENT_SECRET`) + + + Finally, pass the client id and secret to Sourcebot by defining a `identityProvider` object in the [config file](/docs/configuration/config-file): + + ```json wrap icon="code" + { + "$schema": "https://raw.githubusercontent.com/sourcebot-dev/sourcebot/main/schemas/v3/index.json", + "identityProviders": [ + { + "provider": "google", + "purpose": "sso", + "clientId": { + "env": "YOUR_CLIENT_ID_ENV_VAR" + }, + "clientSecret": { + "env": "YOUR_CLIENT_SECRET_ENV_VAR" + } + } + ] + } + ``` + + + + ### Okta +[Auth.js Okta Provider Docs](https://authjs.dev/getting-started/providers/okta) + +An Okta connection can be used for [authentication](/docs/configuration/auth). + + + + + To begin, you must register an OAuth application in Okta to facilitate the identity provider connection. + + Follow [this guide](https://developer.okta.com/docs/guides/implement-oauth-for-okta/main/) by Okta to create an OAuth application. + + When configuring your application: + - Set the application type to "Web Application" + - Add `/api/auth/callback/okta` to the sign-in redirect URIs (ex. https://sourcebot.coolcorp.com/api/auth/callback/okta) + + The result of creating an OAuth application is a `CLIENT_ID`, `CLIENT_SECRET`, and `ISSUER` URL which you'll provide to Sourcebot. + + + To provide Sourcebot the client id, client secret, and issuer for your OAuth application you must set them as environment variables. These can be named whatever you like + (ex. `OKTA_IDENTITY_PROVIDER_CLIENT_ID`, `OKTA_IDENTITY_PROVIDER_CLIENT_SECRET`, and `OKTA_IDENTITY_PROVIDER_ISSUER`) + + + Finally, pass the client id, client secret, and issuer to Sourcebot by defining a `identityProvider` object in the [config file](/docs/configuration/config-file): + + ```json wrap icon="code" + { + "$schema": "https://raw.githubusercontent.com/sourcebot-dev/sourcebot/main/schemas/v3/index.json", + "identityProviders": [ + { + "provider": "okta", + "purpose": "sso", + "clientId": { + "env": "YOUR_CLIENT_ID_ENV_VAR" + }, + "clientSecret": { + "env": "YOUR_CLIENT_SECRET_ENV_VAR" + }, + "issuer": { + "env": "YOUR_ISSUER_ENV_VAR" + } + } + ] + } + ``` + + + + ### Keycloak +[Auth.js Keycloak Provider Docs](https://authjs.dev/getting-started/providers/keycloak) + +A Keycloak connection can be used for [authentication](/docs/configuration/auth). + + + + + To begin, you must register an OAuth client in Keycloak to facilitate the identity provider connection. + + Follow [this guide](https://www.keycloak.org/docs/latest/server_admin/#_oidc_clients) by Keycloak to create an OpenID Connect client. + + When configuring your client: + - Set the client protocol to "openid-connect" + - Set the access type to "confidential" + - Add `/api/auth/callback/keycloak` to the valid redirect URIs (ex. https://sourcebot.coolcorp.com/api/auth/callback/keycloak) + + The result of creating an OAuth client is a `CLIENT_ID`, `CLIENT_SECRET`, and an `ISSUER` URL (typically in the format `https:///realms/`) which you'll provide to Sourcebot. + + + To provide Sourcebot the client id, client secret, and issuer for your OAuth client you must set them as environment variables. These can be named whatever you like + (ex. `KEYCLOAK_IDENTITY_PROVIDER_CLIENT_ID`, `KEYCLOAK_IDENTITY_PROVIDER_CLIENT_SECRET`, and `KEYCLOAK_IDENTITY_PROVIDER_ISSUER`) + + + Finally, pass the client id, client secret, and issuer to Sourcebot by defining a `identityProvider` object in the [config file](/docs/configuration/config-file): + + ```json wrap icon="code" + { + "$schema": "https://raw.githubusercontent.com/sourcebot-dev/sourcebot/main/schemas/v3/index.json", + "identityProviders": [ + { + "provider": "keycloak", + "purpose": "sso", + "clientId": { + "env": "YOUR_CLIENT_ID_ENV_VAR" + }, + "clientSecret": { + "env": "YOUR_CLIENT_SECRET_ENV_VAR" + }, + "issuer": { + "env": "YOUR_ISSUER_ENV_VAR" + } + } + ] + } + ``` + + + + ### Microsoft Entra ID +[Auth.js Microsoft Entra ID Provider Docs](https://authjs.dev/getting-started/providers/microsoft-entra-id) + +A Microsoft Entra ID connection can be used for [authentication](/docs/configuration/auth). + + + + + To begin, you must register an OAuth application in Microsoft Entra ID (formerly Azure Active Directory) to facilitate the identity provider connection. + + Follow [this guide](https://learn.microsoft.com/en-us/entra/identity-platform/quickstart-register-app) by Microsoft to register an application. + + When configuring your application: + - Under "Authentication", add a platform and select "Web" + - Set the redirect URI to `/api/auth/callback/microsoft-entra-id` (ex. https://sourcebot.coolcorp.com/api/auth/callback/microsoft-entra-id) + - Under "Certificates & secrets", create a new client secret + + The result of registering an application is a `CLIENT_ID` (Application ID), `CLIENT_SECRET`, and `TENANT_ID` which you'll use to construct the issuer URL. + + + To provide Sourcebot the client id, client secret, and issuer for your OAuth application you must set them as environment variables. These can be named whatever you like + (ex. `MICROSOFT_ENTRA_ID_IDENTITY_PROVIDER_CLIENT_ID`, `MICROSOFT_ENTRA_ID_IDENTITY_PROVIDER_CLIENT_SECRET`, and `MICROSOFT_ENTRA_ID_IDENTITY_PROVIDER_ISSUER`) + + The issuer URL should be in the format: `https://login.microsoftonline.com//v2.0` + + + Finally, pass the client id, client secret, and issuer to Sourcebot by defining a `identityProvider` object in the [config file](/docs/configuration/config-file): + + ```json wrap icon="code" + { + "$schema": "https://raw.githubusercontent.com/sourcebot-dev/sourcebot/main/schemas/v3/index.json", + "identityProviders": [ + { + "provider": "microsoft-entra-id", + "purpose": "sso", + "clientId": { + "env": "YOUR_CLIENT_ID_ENV_VAR" + }, + "clientSecret": { + "env": "YOUR_CLIENT_SECRET_ENV_VAR" + }, + "issuer": { + "env": "YOUR_ISSUER_ENV_VAR" + } + } + ] + } + ``` + + + + diff --git a/docs/docs/features/permission-syncing.mdx b/docs/docs/features/permission-syncing.mdx index ee6a96e7..403f534f 100644 --- a/docs/docs/features/permission-syncing.mdx +++ b/docs/docs/features/permission-syncing.mdx @@ -12,10 +12,12 @@ import ExperimentalFeatureWarning from '/snippets/experimental-feature-warning.m # Overview -Permission syncing allows you to sync Access Permission Lists (ACLs) from a code host to Sourcebot. When configured, users signed into Sourcebot (via the code host's OAuth provider) will only be able to access repositories that they have access to on the code host. Practically, this means: +Permission syncing allows you to sync Access Permission Lists (ACLs) from a code host to Sourcebot. When configured, users signed into Sourcebot will only be able to access repositories +that they have access to on the code host. Practically, this means: - Code Search results will only include repositories that the user has access to. - Code navigation results will only include repositories that the user has access to. +- MCP results will only include results from repositories the user has access to. - Ask Sourcebot (and the underlying LLM) will only have access to repositories that the user has access to. - File browsing is scoped to the repositories that the user has access to. @@ -46,7 +48,7 @@ We are actively working on supporting more code hosts. If you'd like to see a sp ## GitHub -Prerequisite: [Add GitHub as an OAuth provider](/docs/configuration/auth/providers#github). +Prerequisite: Configure GitHub as an [external identity provider](/docs/configuration/idp). Permission syncing works with **GitHub.com**, **GitHub Enterprise Cloud**, and **GitHub Enterprise Server**. For organization-owned repositories, users that have **read-only** access (or above) via the following methods will have their access synced to Sourcebot: - Outside collaborators @@ -56,18 +58,18 @@ Permission syncing works with **GitHub.com**, **GitHub Enterprise Cloud**, and * - Organization owners. **Notes:** -- A GitHub OAuth provider must be configured to (1) correlate a Sourcebot user with a GitHub user, and (2) to list repositories that the user has access to for [User driven syncing](/docs/features/permission-syncing#how-it-works). +- A GitHub [external identity provider](/docs/configuration/idp) must be configured to (1) correlate a Sourcebot user with a GitHub user, and (2) to list repositories that the user has access to for [User driven syncing](/docs/features/permission-syncing#how-it-works). - OAuth tokens must assume the `repo` scope in order to use the [List repositories for the authenticated user API](https://docs.github.com/en/rest/repos/repos?apiVersion=2022-11-28#list-repositories-for-the-authenticated-user) during [User driven syncing](/docs/features/permission-syncing#how-it-works). Sourcebot **will only** use this token for **reads**. ## GitLab -Prerequisite: [Add GitLab as an OAuth provider](/docs/configuration/auth/providers#gitlab). +Prerequisite: Configure GitLab as an [external identity provider](/docs/configuration/idp). Permission syncing works with **GitLab Self-managed** and **GitLab Cloud**. Users with **Guest** role or above with membership to a group or project will have their access synced to Sourcebot. Both direct and indirect membership to a group or project will be synced with Sourcebot. For more details, see the [GitLab docs](https://docs.gitlab.com/user/project/members/#membership-types). **Notes:** -- A GitLab OAuth provider must be configured to (1) correlate a Sourcebot user with a GitLab user, and (2) to list repositories that the user has access to for [User driven syncing](/docs/features/permission-syncing#how-it-works). +- A GitLab [external identity provider](/docs/configuration/idp) must be configured to (1) correlate a Sourcebot user with a GitLab user, and (2) to list repositories that the user has access to for [User driven syncing](/docs/features/permission-syncing#how-it-works). - OAuth tokens require the `read_api` scope in order to use the [List projects for the authenticated user API](https://docs.gitlab.com/ee/api/projects.html#list-all-projects) during [User driven syncing](/docs/features/permission-syncing#how-it-works). diff --git a/docs/snippets/schemas/v3/authProvider.schema.mdx b/docs/snippets/schemas/v3/authProvider.schema.mdx deleted file mode 100644 index 0ebf9eb1..00000000 --- a/docs/snippets/schemas/v3/authProvider.schema.mdx +++ /dev/null @@ -1,1307 +0,0 @@ -{/* THIS IS A AUTO-GENERATED FILE. DO NOT MODIFY MANUALLY! */} -```json -{ - "$schema": "http://json-schema.org/draft-07/schema#", - "title": "IdentityProviderConfig", - "definitions": { - "GitHubIdentityProviderConfig": { - "type": "object", - "properties": { - "provider": { - "const": "github" - }, - "purpose": { - "enum": [ - "sso", - "identity" - ] - }, - "clientId": { - "anyOf": [ - { - "type": "object", - "properties": { - "secret": { - "type": "string", - "description": "The name of the secret that contains the token." - } - }, - "required": [ - "secret" - ], - "additionalProperties": false - }, - { - "type": "object", - "properties": { - "env": { - "type": "string", - "description": "The name of the environment variable that contains the token. Only supported in declarative connection configs." - } - }, - "required": [ - "env" - ], - "additionalProperties": false - } - ] - }, - "clientSecret": { - "anyOf": [ - { - "type": "object", - "properties": { - "secret": { - "type": "string", - "description": "The name of the secret that contains the token." - } - }, - "required": [ - "secret" - ], - "additionalProperties": false - }, - { - "type": "object", - "properties": { - "env": { - "type": "string", - "description": "The name of the environment variable that contains the token. Only supported in declarative connection configs." - } - }, - "required": [ - "env" - ], - "additionalProperties": false - } - ] - }, - "baseUrl": { - "anyOf": [ - { - "type": "object", - "properties": { - "secret": { - "type": "string", - "description": "The name of the secret that contains the token." - } - }, - "required": [ - "secret" - ], - "additionalProperties": false - }, - { - "type": "object", - "properties": { - "env": { - "type": "string", - "description": "The name of the environment variable that contains the token. Only supported in declarative connection configs." - } - }, - "required": [ - "env" - ], - "additionalProperties": false - } - ] - } - }, - "required": [ - "provider", - "purpose", - "clientId", - "clientSecret" - ] - }, - "GitLabIdentityProviderConfig": { - "type": "object", - "properties": { - "provider": { - "const": "gitlab" - }, - "purpose": { - "enum": [ - "sso", - "identity" - ] - }, - "clientId": { - "anyOf": [ - { - "type": "object", - "properties": { - "secret": { - "type": "string", - "description": "The name of the secret that contains the token." - } - }, - "required": [ - "secret" - ], - "additionalProperties": false - }, - { - "type": "object", - "properties": { - "env": { - "type": "string", - "description": "The name of the environment variable that contains the token. Only supported in declarative connection configs." - } - }, - "required": [ - "env" - ], - "additionalProperties": false - } - ] - }, - "clientSecret": { - "anyOf": [ - { - "type": "object", - "properties": { - "secret": { - "type": "string", - "description": "The name of the secret that contains the token." - } - }, - "required": [ - "secret" - ], - "additionalProperties": false - }, - { - "type": "object", - "properties": { - "env": { - "type": "string", - "description": "The name of the environment variable that contains the token. Only supported in declarative connection configs." - } - }, - "required": [ - "env" - ], - "additionalProperties": false - } - ] - }, - "baseUrl": { - "anyOf": [ - { - "type": "object", - "properties": { - "secret": { - "type": "string", - "description": "The name of the secret that contains the token." - } - }, - "required": [ - "secret" - ], - "additionalProperties": false - }, - { - "type": "object", - "properties": { - "env": { - "type": "string", - "description": "The name of the environment variable that contains the token. Only supported in declarative connection configs." - } - }, - "required": [ - "env" - ], - "additionalProperties": false - } - ] - } - }, - "required": [ - "provider", - "purpose", - "clientId", - "clientSecret", - "baseUrl" - ] - }, - "GoogleIdentityProviderConfig": { - "type": "object", - "properties": { - "provider": { - "const": "google" - }, - "clientId": { - "anyOf": [ - { - "type": "object", - "properties": { - "secret": { - "type": "string", - "description": "The name of the secret that contains the token." - } - }, - "required": [ - "secret" - ], - "additionalProperties": false - }, - { - "type": "object", - "properties": { - "env": { - "type": "string", - "description": "The name of the environment variable that contains the token. Only supported in declarative connection configs." - } - }, - "required": [ - "env" - ], - "additionalProperties": false - } - ] - }, - "clientSecret": { - "anyOf": [ - { - "type": "object", - "properties": { - "secret": { - "type": "string", - "description": "The name of the secret that contains the token." - } - }, - "required": [ - "secret" - ], - "additionalProperties": false - }, - { - "type": "object", - "properties": { - "env": { - "type": "string", - "description": "The name of the environment variable that contains the token. Only supported in declarative connection configs." - } - }, - "required": [ - "env" - ], - "additionalProperties": false - } - ] - } - }, - "required": [ - "provider", - "clientId", - "clientSecret" - ] - }, - "OktaIdentityProviderConfig": { - "type": "object", - "properties": { - "provider": { - "const": "okta" - }, - "clientId": { - "anyOf": [ - { - "type": "object", - "properties": { - "secret": { - "type": "string", - "description": "The name of the secret that contains the token." - } - }, - "required": [ - "secret" - ], - "additionalProperties": false - }, - { - "type": "object", - "properties": { - "env": { - "type": "string", - "description": "The name of the environment variable that contains the token. Only supported in declarative connection configs." - } - }, - "required": [ - "env" - ], - "additionalProperties": false - } - ] - }, - "clientSecret": { - "anyOf": [ - { - "type": "object", - "properties": { - "secret": { - "type": "string", - "description": "The name of the secret that contains the token." - } - }, - "required": [ - "secret" - ], - "additionalProperties": false - }, - { - "type": "object", - "properties": { - "env": { - "type": "string", - "description": "The name of the environment variable that contains the token. Only supported in declarative connection configs." - } - }, - "required": [ - "env" - ], - "additionalProperties": false - } - ] - }, - "issuer": { - "anyOf": [ - { - "type": "object", - "properties": { - "secret": { - "type": "string", - "description": "The name of the secret that contains the token." - } - }, - "required": [ - "secret" - ], - "additionalProperties": false - }, - { - "type": "object", - "properties": { - "env": { - "type": "string", - "description": "The name of the environment variable that contains the token. Only supported in declarative connection configs." - } - }, - "required": [ - "env" - ], - "additionalProperties": false - } - ] - } - }, - "required": [ - "provider", - "clientId", - "clientSecret", - "issuer" - ] - }, - "KeycloakIdentityProviderConfig": { - "type": "object", - "properties": { - "provider": { - "const": "keycloak" - }, - "clientId": { - "anyOf": [ - { - "type": "object", - "properties": { - "secret": { - "type": "string", - "description": "The name of the secret that contains the token." - } - }, - "required": [ - "secret" - ], - "additionalProperties": false - }, - { - "type": "object", - "properties": { - "env": { - "type": "string", - "description": "The name of the environment variable that contains the token. Only supported in declarative connection configs." - } - }, - "required": [ - "env" - ], - "additionalProperties": false - } - ] - }, - "clientSecret": { - "anyOf": [ - { - "type": "object", - "properties": { - "secret": { - "type": "string", - "description": "The name of the secret that contains the token." - } - }, - "required": [ - "secret" - ], - "additionalProperties": false - }, - { - "type": "object", - "properties": { - "env": { - "type": "string", - "description": "The name of the environment variable that contains the token. Only supported in declarative connection configs." - } - }, - "required": [ - "env" - ], - "additionalProperties": false - } - ] - }, - "issuer": { - "anyOf": [ - { - "type": "object", - "properties": { - "secret": { - "type": "string", - "description": "The name of the secret that contains the token." - } - }, - "required": [ - "secret" - ], - "additionalProperties": false - }, - { - "type": "object", - "properties": { - "env": { - "type": "string", - "description": "The name of the environment variable that contains the token. Only supported in declarative connection configs." - } - }, - "required": [ - "env" - ], - "additionalProperties": false - } - ] - } - }, - "required": [ - "provider", - "clientId", - "clientSecret", - "issuer" - ] - }, - "MicrosoftEntraIDIdentityProviderConfig": { - "type": "object", - "properties": { - "provider": { - "const": "microsoft-entra-id" - }, - "clientId": { - "anyOf": [ - { - "type": "object", - "properties": { - "secret": { - "type": "string", - "description": "The name of the secret that contains the token." - } - }, - "required": [ - "secret" - ], - "additionalProperties": false - }, - { - "type": "object", - "properties": { - "env": { - "type": "string", - "description": "The name of the environment variable that contains the token. Only supported in declarative connection configs." - } - }, - "required": [ - "env" - ], - "additionalProperties": false - } - ] - }, - "clientSecret": { - "anyOf": [ - { - "type": "object", - "properties": { - "secret": { - "type": "string", - "description": "The name of the secret that contains the token." - } - }, - "required": [ - "secret" - ], - "additionalProperties": false - }, - { - "type": "object", - "properties": { - "env": { - "type": "string", - "description": "The name of the environment variable that contains the token. Only supported in declarative connection configs." - } - }, - "required": [ - "env" - ], - "additionalProperties": false - } - ] - }, - "issuer": { - "anyOf": [ - { - "type": "object", - "properties": { - "secret": { - "type": "string", - "description": "The name of the secret that contains the token." - } - }, - "required": [ - "secret" - ], - "additionalProperties": false - }, - { - "type": "object", - "properties": { - "env": { - "type": "string", - "description": "The name of the environment variable that contains the token. Only supported in declarative connection configs." - } - }, - "required": [ - "env" - ], - "additionalProperties": false - } - ] - } - }, - "required": [ - "provider", - "clientId", - "clientSecret", - "issuer" - ] - }, - "GCPIAPIdentityProviderConfig": { - "type": "object", - "properties": { - "provider": { - "const": "gcp-iap" - }, - "audience": { - "anyOf": [ - { - "type": "object", - "properties": { - "secret": { - "type": "string", - "description": "The name of the secret that contains the token." - } - }, - "required": [ - "secret" - ], - "additionalProperties": false - }, - { - "type": "object", - "properties": { - "env": { - "type": "string", - "description": "The name of the environment variable that contains the token. Only supported in declarative connection configs." - } - }, - "required": [ - "env" - ], - "additionalProperties": false - } - ] - } - }, - "required": [ - "provider", - "audience" - ] - } - }, - "oneOf": [ - { - "type": "object", - "properties": { - "provider": { - "const": "github" - }, - "purpose": { - "enum": [ - "sso", - "identity" - ] - }, - "clientId": { - "anyOf": [ - { - "type": "object", - "properties": { - "secret": { - "type": "string", - "description": "The name of the secret that contains the token." - } - }, - "required": [ - "secret" - ], - "additionalProperties": false - }, - { - "type": "object", - "properties": { - "env": { - "type": "string", - "description": "The name of the environment variable that contains the token. Only supported in declarative connection configs." - } - }, - "required": [ - "env" - ], - "additionalProperties": false - } - ] - }, - "clientSecret": { - "anyOf": [ - { - "type": "object", - "properties": { - "secret": { - "type": "string", - "description": "The name of the secret that contains the token." - } - }, - "required": [ - "secret" - ], - "additionalProperties": false - }, - { - "type": "object", - "properties": { - "env": { - "type": "string", - "description": "The name of the environment variable that contains the token. Only supported in declarative connection configs." - } - }, - "required": [ - "env" - ], - "additionalProperties": false - } - ] - }, - "baseUrl": { - "anyOf": [ - { - "type": "object", - "properties": { - "secret": { - "type": "string", - "description": "The name of the secret that contains the token." - } - }, - "required": [ - "secret" - ], - "additionalProperties": false - }, - { - "type": "object", - "properties": { - "env": { - "type": "string", - "description": "The name of the environment variable that contains the token. Only supported in declarative connection configs." - } - }, - "required": [ - "env" - ], - "additionalProperties": false - } - ] - } - }, - "required": [ - "provider", - "purpose", - "clientId", - "clientSecret" - ] - }, - { - "type": "object", - "properties": { - "provider": { - "const": "gitlab" - }, - "purpose": { - "enum": [ - "sso", - "identity" - ] - }, - "clientId": { - "anyOf": [ - { - "type": "object", - "properties": { - "secret": { - "type": "string", - "description": "The name of the secret that contains the token." - } - }, - "required": [ - "secret" - ], - "additionalProperties": false - }, - { - "type": "object", - "properties": { - "env": { - "type": "string", - "description": "The name of the environment variable that contains the token. Only supported in declarative connection configs." - } - }, - "required": [ - "env" - ], - "additionalProperties": false - } - ] - }, - "clientSecret": { - "anyOf": [ - { - "type": "object", - "properties": { - "secret": { - "type": "string", - "description": "The name of the secret that contains the token." - } - }, - "required": [ - "secret" - ], - "additionalProperties": false - }, - { - "type": "object", - "properties": { - "env": { - "type": "string", - "description": "The name of the environment variable that contains the token. Only supported in declarative connection configs." - } - }, - "required": [ - "env" - ], - "additionalProperties": false - } - ] - }, - "baseUrl": { - "anyOf": [ - { - "type": "object", - "properties": { - "secret": { - "type": "string", - "description": "The name of the secret that contains the token." - } - }, - "required": [ - "secret" - ], - "additionalProperties": false - }, - { - "type": "object", - "properties": { - "env": { - "type": "string", - "description": "The name of the environment variable that contains the token. Only supported in declarative connection configs." - } - }, - "required": [ - "env" - ], - "additionalProperties": false - } - ] - } - }, - "required": [ - "provider", - "purpose", - "clientId", - "clientSecret", - "baseUrl" - ] - }, - { - "type": "object", - "properties": { - "provider": { - "const": "google" - }, - "clientId": { - "anyOf": [ - { - "type": "object", - "properties": { - "secret": { - "type": "string", - "description": "The name of the secret that contains the token." - } - }, - "required": [ - "secret" - ], - "additionalProperties": false - }, - { - "type": "object", - "properties": { - "env": { - "type": "string", - "description": "The name of the environment variable that contains the token. Only supported in declarative connection configs." - } - }, - "required": [ - "env" - ], - "additionalProperties": false - } - ] - }, - "clientSecret": { - "anyOf": [ - { - "type": "object", - "properties": { - "secret": { - "type": "string", - "description": "The name of the secret that contains the token." - } - }, - "required": [ - "secret" - ], - "additionalProperties": false - }, - { - "type": "object", - "properties": { - "env": { - "type": "string", - "description": "The name of the environment variable that contains the token. Only supported in declarative connection configs." - } - }, - "required": [ - "env" - ], - "additionalProperties": false - } - ] - } - }, - "required": [ - "provider", - "clientId", - "clientSecret" - ] - }, - { - "type": "object", - "properties": { - "provider": { - "const": "okta" - }, - "clientId": { - "anyOf": [ - { - "type": "object", - "properties": { - "secret": { - "type": "string", - "description": "The name of the secret that contains the token." - } - }, - "required": [ - "secret" - ], - "additionalProperties": false - }, - { - "type": "object", - "properties": { - "env": { - "type": "string", - "description": "The name of the environment variable that contains the token. Only supported in declarative connection configs." - } - }, - "required": [ - "env" - ], - "additionalProperties": false - } - ] - }, - "clientSecret": { - "anyOf": [ - { - "type": "object", - "properties": { - "secret": { - "type": "string", - "description": "The name of the secret that contains the token." - } - }, - "required": [ - "secret" - ], - "additionalProperties": false - }, - { - "type": "object", - "properties": { - "env": { - "type": "string", - "description": "The name of the environment variable that contains the token. Only supported in declarative connection configs." - } - }, - "required": [ - "env" - ], - "additionalProperties": false - } - ] - }, - "issuer": { - "anyOf": [ - { - "type": "object", - "properties": { - "secret": { - "type": "string", - "description": "The name of the secret that contains the token." - } - }, - "required": [ - "secret" - ], - "additionalProperties": false - }, - { - "type": "object", - "properties": { - "env": { - "type": "string", - "description": "The name of the environment variable that contains the token. Only supported in declarative connection configs." - } - }, - "required": [ - "env" - ], - "additionalProperties": false - } - ] - } - }, - "required": [ - "provider", - "clientId", - "clientSecret", - "issuer" - ] - }, - { - "type": "object", - "properties": { - "provider": { - "const": "keycloak" - }, - "clientId": { - "anyOf": [ - { - "type": "object", - "properties": { - "secret": { - "type": "string", - "description": "The name of the secret that contains the token." - } - }, - "required": [ - "secret" - ], - "additionalProperties": false - }, - { - "type": "object", - "properties": { - "env": { - "type": "string", - "description": "The name of the environment variable that contains the token. Only supported in declarative connection configs." - } - }, - "required": [ - "env" - ], - "additionalProperties": false - } - ] - }, - "clientSecret": { - "anyOf": [ - { - "type": "object", - "properties": { - "secret": { - "type": "string", - "description": "The name of the secret that contains the token." - } - }, - "required": [ - "secret" - ], - "additionalProperties": false - }, - { - "type": "object", - "properties": { - "env": { - "type": "string", - "description": "The name of the environment variable that contains the token. Only supported in declarative connection configs." - } - }, - "required": [ - "env" - ], - "additionalProperties": false - } - ] - }, - "issuer": { - "anyOf": [ - { - "type": "object", - "properties": { - "secret": { - "type": "string", - "description": "The name of the secret that contains the token." - } - }, - "required": [ - "secret" - ], - "additionalProperties": false - }, - { - "type": "object", - "properties": { - "env": { - "type": "string", - "description": "The name of the environment variable that contains the token. Only supported in declarative connection configs." - } - }, - "required": [ - "env" - ], - "additionalProperties": false - } - ] - } - }, - "required": [ - "provider", - "clientId", - "clientSecret", - "issuer" - ] - }, - { - "type": "object", - "properties": { - "provider": { - "const": "microsoft-entra-id" - }, - "clientId": { - "anyOf": [ - { - "type": "object", - "properties": { - "secret": { - "type": "string", - "description": "The name of the secret that contains the token." - } - }, - "required": [ - "secret" - ], - "additionalProperties": false - }, - { - "type": "object", - "properties": { - "env": { - "type": "string", - "description": "The name of the environment variable that contains the token. Only supported in declarative connection configs." - } - }, - "required": [ - "env" - ], - "additionalProperties": false - } - ] - }, - "clientSecret": { - "anyOf": [ - { - "type": "object", - "properties": { - "secret": { - "type": "string", - "description": "The name of the secret that contains the token." - } - }, - "required": [ - "secret" - ], - "additionalProperties": false - }, - { - "type": "object", - "properties": { - "env": { - "type": "string", - "description": "The name of the environment variable that contains the token. Only supported in declarative connection configs." - } - }, - "required": [ - "env" - ], - "additionalProperties": false - } - ] - }, - "issuer": { - "anyOf": [ - { - "type": "object", - "properties": { - "secret": { - "type": "string", - "description": "The name of the secret that contains the token." - } - }, - "required": [ - "secret" - ], - "additionalProperties": false - }, - { - "type": "object", - "properties": { - "env": { - "type": "string", - "description": "The name of the environment variable that contains the token. Only supported in declarative connection configs." - } - }, - "required": [ - "env" - ], - "additionalProperties": false - } - ] - } - }, - "required": [ - "provider", - "clientId", - "clientSecret", - "issuer" - ] - }, - { - "type": "object", - "properties": { - "provider": { - "const": "gcp-iap" - }, - "audience": { - "anyOf": [ - { - "type": "object", - "properties": { - "secret": { - "type": "string", - "description": "The name of the secret that contains the token." - } - }, - "required": [ - "secret" - ], - "additionalProperties": false - }, - { - "type": "object", - "properties": { - "env": { - "type": "string", - "description": "The name of the environment variable that contains the token. Only supported in declarative connection configs." - } - }, - "required": [ - "env" - ], - "additionalProperties": false - } - ] - } - }, - "required": [ - "provider", - "audience" - ] - } - ] -} -``` diff --git a/docs/snippets/schemas/v3/identityProvider.schema.mdx b/docs/snippets/schemas/v3/identityProvider.schema.mdx index 988cffcb..30c172be 100644 --- a/docs/snippets/schemas/v3/identityProvider.schema.mdx +++ b/docs/snippets/schemas/v3/identityProvider.schema.mdx @@ -6,6 +6,7 @@ "definitions": { "GitHubIdentityProviderConfig": { "type": "object", + "additionalProperties": false, "properties": { "provider": { "const": "github" @@ -13,7 +14,7 @@ "purpose": { "enum": [ "sso", - "integration" + "account_linking" ] }, "clientId": { @@ -77,36 +78,17 @@ ] }, "baseUrl": { - "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 - } - ] + "type": "string", + "format": "url", + "default": "https://github.com", + "description": "The URL of the GitHub host. Defaults to https://github.com", + "examples": [ + "https://github.com", + "https://github.example.com" + ], + "pattern": "^https?:\\/\\/[^\\s/$.?#].[^\\s]*$" }, - "required": { + "accountLinkingRequired": { "type": "boolean", "default": false } @@ -120,6 +102,7 @@ }, "GitLabIdentityProviderConfig": { "type": "object", + "additionalProperties": false, "properties": { "provider": { "const": "gitlab" @@ -127,7 +110,7 @@ "purpose": { "enum": [ "sso", - "integration" + "account_linking" ] }, "clientId": { @@ -191,36 +174,17 @@ ] }, "baseUrl": { - "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 - } - ] + "type": "string", + "format": "url", + "default": "https://gitlab.com", + "description": "The URL of the GitLab host. Defaults to https://gitlab.com", + "examples": [ + "https://gitlab.com", + "https://gitlab.example.com" + ], + "pattern": "^https?:\\/\\/[^\\s/$.?#].[^\\s]*$" }, - "required": { + "accountLinkingRequired": { "type": "boolean", "default": false } @@ -234,10 +198,14 @@ }, "GoogleIdentityProviderConfig": { "type": "object", + "additionalProperties": false, "properties": { "provider": { "const": "google" }, + "purpose": { + "const": "sso" + }, "clientId": { "anyOf": [ { @@ -301,16 +269,21 @@ }, "required": [ "provider", + "purpose", "clientId", "clientSecret" ] }, "OktaIdentityProviderConfig": { "type": "object", + "additionalProperties": false, "properties": { "provider": { "const": "okta" }, + "purpose": { + "const": "sso" + }, "clientId": { "anyOf": [ { @@ -404,6 +377,7 @@ }, "required": [ "provider", + "purpose", "clientId", "clientSecret", "issuer" @@ -411,10 +385,14 @@ }, "KeycloakIdentityProviderConfig": { "type": "object", + "additionalProperties": false, "properties": { "provider": { "const": "keycloak" }, + "purpose": { + "const": "sso" + }, "clientId": { "anyOf": [ { @@ -508,6 +486,7 @@ }, "required": [ "provider", + "purpose", "clientId", "clientSecret", "issuer" @@ -515,10 +494,14 @@ }, "MicrosoftEntraIDIdentityProviderConfig": { "type": "object", + "additionalProperties": false, "properties": { "provider": { "const": "microsoft-entra-id" }, + "purpose": { + "const": "sso" + }, "clientId": { "anyOf": [ { @@ -612,6 +595,7 @@ }, "required": [ "provider", + "purpose", "clientId", "clientSecret", "issuer" @@ -619,10 +603,14 @@ }, "GCPIAPIdentityProviderConfig": { "type": "object", + "additionalProperties": false, "properties": { "provider": { "const": "gcp-iap" }, + "purpose": { + "const": "sso" + }, "audience": { "anyOf": [ { @@ -656,6 +644,7 @@ }, "required": [ "provider", + "purpose", "audience" ] } @@ -663,6 +652,7 @@ "oneOf": [ { "type": "object", + "additionalProperties": false, "properties": { "provider": { "const": "github" @@ -670,7 +660,7 @@ "purpose": { "enum": [ "sso", - "integration" + "account_linking" ] }, "clientId": { @@ -734,36 +724,17 @@ ] }, "baseUrl": { - "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 - } - ] + "type": "string", + "format": "url", + "default": "https://github.com", + "description": "The URL of the GitHub host. Defaults to https://github.com", + "examples": [ + "https://github.com", + "https://github.example.com" + ], + "pattern": "^https?:\\/\\/[^\\s/$.?#].[^\\s]*$" }, - "required": { + "accountLinkingRequired": { "type": "boolean", "default": false } @@ -777,6 +748,7 @@ }, { "type": "object", + "additionalProperties": false, "properties": { "provider": { "const": "gitlab" @@ -784,7 +756,7 @@ "purpose": { "enum": [ "sso", - "integration" + "account_linking" ] }, "clientId": { @@ -848,36 +820,17 @@ ] }, "baseUrl": { - "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 - } - ] + "type": "string", + "format": "url", + "default": "https://gitlab.com", + "description": "The URL of the GitLab host. Defaults to https://gitlab.com", + "examples": [ + "https://gitlab.com", + "https://gitlab.example.com" + ], + "pattern": "^https?:\\/\\/[^\\s/$.?#].[^\\s]*$" }, - "required": { + "accountLinkingRequired": { "type": "boolean", "default": false } @@ -891,10 +844,14 @@ }, { "type": "object", + "additionalProperties": false, "properties": { "provider": { "const": "google" }, + "purpose": { + "const": "sso" + }, "clientId": { "anyOf": [ { @@ -958,16 +915,21 @@ }, "required": [ "provider", + "purpose", "clientId", "clientSecret" ] }, { "type": "object", + "additionalProperties": false, "properties": { "provider": { "const": "okta" }, + "purpose": { + "const": "sso" + }, "clientId": { "anyOf": [ { @@ -1061,6 +1023,7 @@ }, "required": [ "provider", + "purpose", "clientId", "clientSecret", "issuer" @@ -1068,10 +1031,14 @@ }, { "type": "object", + "additionalProperties": false, "properties": { "provider": { "const": "keycloak" }, + "purpose": { + "const": "sso" + }, "clientId": { "anyOf": [ { @@ -1165,6 +1132,7 @@ }, "required": [ "provider", + "purpose", "clientId", "clientSecret", "issuer" @@ -1172,10 +1140,14 @@ }, { "type": "object", + "additionalProperties": false, "properties": { "provider": { "const": "microsoft-entra-id" }, + "purpose": { + "const": "sso" + }, "clientId": { "anyOf": [ { @@ -1269,6 +1241,7 @@ }, "required": [ "provider", + "purpose", "clientId", "clientSecret", "issuer" @@ -1276,10 +1249,14 @@ }, { "type": "object", + "additionalProperties": false, "properties": { "provider": { "const": "gcp-iap" }, + "purpose": { + "const": "sso" + }, "audience": { "anyOf": [ { @@ -1313,6 +1290,7 @@ }, "required": [ "provider", + "purpose", "audience" ] } diff --git a/docs/snippets/schemas/v3/index.schema.mdx b/docs/snippets/schemas/v3/index.schema.mdx index d43562fa..615c058a 100644 --- a/docs/snippets/schemas/v3/index.schema.mdx +++ b/docs/snippets/schemas/v3/index.schema.mdx @@ -4410,6 +4410,7 @@ "definitions": { "GitHubIdentityProviderConfig": { "type": "object", + "additionalProperties": false, "properties": { "provider": { "const": "github" @@ -4417,7 +4418,7 @@ "purpose": { "enum": [ "sso", - "integration" + "account_linking" ] }, "clientId": { @@ -4481,36 +4482,17 @@ ] }, "baseUrl": { - "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 - } - ] + "type": "string", + "format": "url", + "default": "https://github.com", + "description": "The URL of the GitHub host. Defaults to https://github.com", + "examples": [ + "https://github.com", + "https://github.example.com" + ], + "pattern": "^https?:\\/\\/[^\\s/$.?#].[^\\s]*$" }, - "required": { + "accountLinkingRequired": { "type": "boolean", "default": false } @@ -4524,6 +4506,7 @@ }, "GitLabIdentityProviderConfig": { "type": "object", + "additionalProperties": false, "properties": { "provider": { "const": "gitlab" @@ -4531,7 +4514,7 @@ "purpose": { "enum": [ "sso", - "integration" + "account_linking" ] }, "clientId": { @@ -4595,36 +4578,17 @@ ] }, "baseUrl": { - "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 - } - ] + "type": "string", + "format": "url", + "default": "https://gitlab.com", + "description": "The URL of the GitLab host. Defaults to https://gitlab.com", + "examples": [ + "https://gitlab.com", + "https://gitlab.example.com" + ], + "pattern": "^https?:\\/\\/[^\\s/$.?#].[^\\s]*$" }, - "required": { + "accountLinkingRequired": { "type": "boolean", "default": false } @@ -4638,10 +4602,14 @@ }, "GoogleIdentityProviderConfig": { "type": "object", + "additionalProperties": false, "properties": { "provider": { "const": "google" }, + "purpose": { + "const": "sso" + }, "clientId": { "anyOf": [ { @@ -4705,16 +4673,21 @@ }, "required": [ "provider", + "purpose", "clientId", "clientSecret" ] }, "OktaIdentityProviderConfig": { "type": "object", + "additionalProperties": false, "properties": { "provider": { "const": "okta" }, + "purpose": { + "const": "sso" + }, "clientId": { "anyOf": [ { @@ -4808,6 +4781,7 @@ }, "required": [ "provider", + "purpose", "clientId", "clientSecret", "issuer" @@ -4815,10 +4789,14 @@ }, "KeycloakIdentityProviderConfig": { "type": "object", + "additionalProperties": false, "properties": { "provider": { "const": "keycloak" }, + "purpose": { + "const": "sso" + }, "clientId": { "anyOf": [ { @@ -4912,6 +4890,7 @@ }, "required": [ "provider", + "purpose", "clientId", "clientSecret", "issuer" @@ -4919,10 +4898,14 @@ }, "MicrosoftEntraIDIdentityProviderConfig": { "type": "object", + "additionalProperties": false, "properties": { "provider": { "const": "microsoft-entra-id" }, + "purpose": { + "const": "sso" + }, "clientId": { "anyOf": [ { @@ -5016,6 +4999,7 @@ }, "required": [ "provider", + "purpose", "clientId", "clientSecret", "issuer" @@ -5023,10 +5007,14 @@ }, "GCPIAPIdentityProviderConfig": { "type": "object", + "additionalProperties": false, "properties": { "provider": { "const": "gcp-iap" }, + "purpose": { + "const": "sso" + }, "audience": { "anyOf": [ { @@ -5060,6 +5048,7 @@ }, "required": [ "provider", + "purpose", "audience" ] } @@ -5067,6 +5056,7 @@ "oneOf": [ { "type": "object", + "additionalProperties": false, "properties": { "provider": { "const": "github" @@ -5074,7 +5064,7 @@ "purpose": { "enum": [ "sso", - "integration" + "account_linking" ] }, "clientId": { @@ -5138,36 +5128,17 @@ ] }, "baseUrl": { - "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 - } - ] + "type": "string", + "format": "url", + "default": "https://github.com", + "description": "The URL of the GitHub host. Defaults to https://github.com", + "examples": [ + "https://github.com", + "https://github.example.com" + ], + "pattern": "^https?:\\/\\/[^\\s/$.?#].[^\\s]*$" }, - "required": { + "accountLinkingRequired": { "type": "boolean", "default": false } @@ -5181,6 +5152,7 @@ }, { "type": "object", + "additionalProperties": false, "properties": { "provider": { "const": "gitlab" @@ -5188,7 +5160,7 @@ "purpose": { "enum": [ "sso", - "integration" + "account_linking" ] }, "clientId": { @@ -5252,36 +5224,17 @@ ] }, "baseUrl": { - "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 - } - ] + "type": "string", + "format": "url", + "default": "https://gitlab.com", + "description": "The URL of the GitLab host. Defaults to https://gitlab.com", + "examples": [ + "https://gitlab.com", + "https://gitlab.example.com" + ], + "pattern": "^https?:\\/\\/[^\\s/$.?#].[^\\s]*$" }, - "required": { + "accountLinkingRequired": { "type": "boolean", "default": false } @@ -5295,10 +5248,14 @@ }, { "type": "object", + "additionalProperties": false, "properties": { "provider": { "const": "google" }, + "purpose": { + "const": "sso" + }, "clientId": { "anyOf": [ { @@ -5362,16 +5319,21 @@ }, "required": [ "provider", + "purpose", "clientId", "clientSecret" ] }, { "type": "object", + "additionalProperties": false, "properties": { "provider": { "const": "okta" }, + "purpose": { + "const": "sso" + }, "clientId": { "anyOf": [ { @@ -5465,6 +5427,7 @@ }, "required": [ "provider", + "purpose", "clientId", "clientSecret", "issuer" @@ -5472,10 +5435,14 @@ }, { "type": "object", + "additionalProperties": false, "properties": { "provider": { "const": "keycloak" }, + "purpose": { + "const": "sso" + }, "clientId": { "anyOf": [ { @@ -5569,6 +5536,7 @@ }, "required": [ "provider", + "purpose", "clientId", "clientSecret", "issuer" @@ -5576,10 +5544,14 @@ }, { "type": "object", + "additionalProperties": false, "properties": { "provider": { "const": "microsoft-entra-id" }, + "purpose": { + "const": "sso" + }, "clientId": { "anyOf": [ { @@ -5673,6 +5645,7 @@ }, "required": [ "provider", + "purpose", "clientId", "clientSecret", "issuer" @@ -5680,10 +5653,14 @@ }, { "type": "object", + "additionalProperties": false, "properties": { "provider": { "const": "gcp-iap" }, + "purpose": { + "const": "sso" + }, "audience": { "anyOf": [ { @@ -5717,6 +5694,7 @@ }, "required": [ "provider", + "purpose", "audience" ] } diff --git a/packages/schemas/src/v3/authProvider.schema.ts b/packages/schemas/src/v3/authProvider.schema.ts deleted file mode 100644 index 32a0bc46..00000000 --- a/packages/schemas/src/v3/authProvider.schema.ts +++ /dev/null @@ -1,1306 +0,0 @@ -// THIS IS A AUTO-GENERATED FILE. DO NOT MODIFY MANUALLY! -const schema = { - "$schema": "http://json-schema.org/draft-07/schema#", - "title": "IdentityProviderConfig", - "definitions": { - "GitHubIdentityProviderConfig": { - "type": "object", - "properties": { - "provider": { - "const": "github" - }, - "purpose": { - "enum": [ - "sso", - "identity" - ] - }, - "clientId": { - "anyOf": [ - { - "type": "object", - "properties": { - "secret": { - "type": "string", - "description": "The name of the secret that contains the token." - } - }, - "required": [ - "secret" - ], - "additionalProperties": false - }, - { - "type": "object", - "properties": { - "env": { - "type": "string", - "description": "The name of the environment variable that contains the token. Only supported in declarative connection configs." - } - }, - "required": [ - "env" - ], - "additionalProperties": false - } - ] - }, - "clientSecret": { - "anyOf": [ - { - "type": "object", - "properties": { - "secret": { - "type": "string", - "description": "The name of the secret that contains the token." - } - }, - "required": [ - "secret" - ], - "additionalProperties": false - }, - { - "type": "object", - "properties": { - "env": { - "type": "string", - "description": "The name of the environment variable that contains the token. Only supported in declarative connection configs." - } - }, - "required": [ - "env" - ], - "additionalProperties": false - } - ] - }, - "baseUrl": { - "anyOf": [ - { - "type": "object", - "properties": { - "secret": { - "type": "string", - "description": "The name of the secret that contains the token." - } - }, - "required": [ - "secret" - ], - "additionalProperties": false - }, - { - "type": "object", - "properties": { - "env": { - "type": "string", - "description": "The name of the environment variable that contains the token. Only supported in declarative connection configs." - } - }, - "required": [ - "env" - ], - "additionalProperties": false - } - ] - } - }, - "required": [ - "provider", - "purpose", - "clientId", - "clientSecret" - ] - }, - "GitLabIdentityProviderConfig": { - "type": "object", - "properties": { - "provider": { - "const": "gitlab" - }, - "purpose": { - "enum": [ - "sso", - "identity" - ] - }, - "clientId": { - "anyOf": [ - { - "type": "object", - "properties": { - "secret": { - "type": "string", - "description": "The name of the secret that contains the token." - } - }, - "required": [ - "secret" - ], - "additionalProperties": false - }, - { - "type": "object", - "properties": { - "env": { - "type": "string", - "description": "The name of the environment variable that contains the token. Only supported in declarative connection configs." - } - }, - "required": [ - "env" - ], - "additionalProperties": false - } - ] - }, - "clientSecret": { - "anyOf": [ - { - "type": "object", - "properties": { - "secret": { - "type": "string", - "description": "The name of the secret that contains the token." - } - }, - "required": [ - "secret" - ], - "additionalProperties": false - }, - { - "type": "object", - "properties": { - "env": { - "type": "string", - "description": "The name of the environment variable that contains the token. Only supported in declarative connection configs." - } - }, - "required": [ - "env" - ], - "additionalProperties": false - } - ] - }, - "baseUrl": { - "anyOf": [ - { - "type": "object", - "properties": { - "secret": { - "type": "string", - "description": "The name of the secret that contains the token." - } - }, - "required": [ - "secret" - ], - "additionalProperties": false - }, - { - "type": "object", - "properties": { - "env": { - "type": "string", - "description": "The name of the environment variable that contains the token. Only supported in declarative connection configs." - } - }, - "required": [ - "env" - ], - "additionalProperties": false - } - ] - } - }, - "required": [ - "provider", - "purpose", - "clientId", - "clientSecret", - "baseUrl" - ] - }, - "GoogleIdentityProviderConfig": { - "type": "object", - "properties": { - "provider": { - "const": "google" - }, - "clientId": { - "anyOf": [ - { - "type": "object", - "properties": { - "secret": { - "type": "string", - "description": "The name of the secret that contains the token." - } - }, - "required": [ - "secret" - ], - "additionalProperties": false - }, - { - "type": "object", - "properties": { - "env": { - "type": "string", - "description": "The name of the environment variable that contains the token. Only supported in declarative connection configs." - } - }, - "required": [ - "env" - ], - "additionalProperties": false - } - ] - }, - "clientSecret": { - "anyOf": [ - { - "type": "object", - "properties": { - "secret": { - "type": "string", - "description": "The name of the secret that contains the token." - } - }, - "required": [ - "secret" - ], - "additionalProperties": false - }, - { - "type": "object", - "properties": { - "env": { - "type": "string", - "description": "The name of the environment variable that contains the token. Only supported in declarative connection configs." - } - }, - "required": [ - "env" - ], - "additionalProperties": false - } - ] - } - }, - "required": [ - "provider", - "clientId", - "clientSecret" - ] - }, - "OktaIdentityProviderConfig": { - "type": "object", - "properties": { - "provider": { - "const": "okta" - }, - "clientId": { - "anyOf": [ - { - "type": "object", - "properties": { - "secret": { - "type": "string", - "description": "The name of the secret that contains the token." - } - }, - "required": [ - "secret" - ], - "additionalProperties": false - }, - { - "type": "object", - "properties": { - "env": { - "type": "string", - "description": "The name of the environment variable that contains the token. Only supported in declarative connection configs." - } - }, - "required": [ - "env" - ], - "additionalProperties": false - } - ] - }, - "clientSecret": { - "anyOf": [ - { - "type": "object", - "properties": { - "secret": { - "type": "string", - "description": "The name of the secret that contains the token." - } - }, - "required": [ - "secret" - ], - "additionalProperties": false - }, - { - "type": "object", - "properties": { - "env": { - "type": "string", - "description": "The name of the environment variable that contains the token. Only supported in declarative connection configs." - } - }, - "required": [ - "env" - ], - "additionalProperties": false - } - ] - }, - "issuer": { - "anyOf": [ - { - "type": "object", - "properties": { - "secret": { - "type": "string", - "description": "The name of the secret that contains the token." - } - }, - "required": [ - "secret" - ], - "additionalProperties": false - }, - { - "type": "object", - "properties": { - "env": { - "type": "string", - "description": "The name of the environment variable that contains the token. Only supported in declarative connection configs." - } - }, - "required": [ - "env" - ], - "additionalProperties": false - } - ] - } - }, - "required": [ - "provider", - "clientId", - "clientSecret", - "issuer" - ] - }, - "KeycloakIdentityProviderConfig": { - "type": "object", - "properties": { - "provider": { - "const": "keycloak" - }, - "clientId": { - "anyOf": [ - { - "type": "object", - "properties": { - "secret": { - "type": "string", - "description": "The name of the secret that contains the token." - } - }, - "required": [ - "secret" - ], - "additionalProperties": false - }, - { - "type": "object", - "properties": { - "env": { - "type": "string", - "description": "The name of the environment variable that contains the token. Only supported in declarative connection configs." - } - }, - "required": [ - "env" - ], - "additionalProperties": false - } - ] - }, - "clientSecret": { - "anyOf": [ - { - "type": "object", - "properties": { - "secret": { - "type": "string", - "description": "The name of the secret that contains the token." - } - }, - "required": [ - "secret" - ], - "additionalProperties": false - }, - { - "type": "object", - "properties": { - "env": { - "type": "string", - "description": "The name of the environment variable that contains the token. Only supported in declarative connection configs." - } - }, - "required": [ - "env" - ], - "additionalProperties": false - } - ] - }, - "issuer": { - "anyOf": [ - { - "type": "object", - "properties": { - "secret": { - "type": "string", - "description": "The name of the secret that contains the token." - } - }, - "required": [ - "secret" - ], - "additionalProperties": false - }, - { - "type": "object", - "properties": { - "env": { - "type": "string", - "description": "The name of the environment variable that contains the token. Only supported in declarative connection configs." - } - }, - "required": [ - "env" - ], - "additionalProperties": false - } - ] - } - }, - "required": [ - "provider", - "clientId", - "clientSecret", - "issuer" - ] - }, - "MicrosoftEntraIDIdentityProviderConfig": { - "type": "object", - "properties": { - "provider": { - "const": "microsoft-entra-id" - }, - "clientId": { - "anyOf": [ - { - "type": "object", - "properties": { - "secret": { - "type": "string", - "description": "The name of the secret that contains the token." - } - }, - "required": [ - "secret" - ], - "additionalProperties": false - }, - { - "type": "object", - "properties": { - "env": { - "type": "string", - "description": "The name of the environment variable that contains the token. Only supported in declarative connection configs." - } - }, - "required": [ - "env" - ], - "additionalProperties": false - } - ] - }, - "clientSecret": { - "anyOf": [ - { - "type": "object", - "properties": { - "secret": { - "type": "string", - "description": "The name of the secret that contains the token." - } - }, - "required": [ - "secret" - ], - "additionalProperties": false - }, - { - "type": "object", - "properties": { - "env": { - "type": "string", - "description": "The name of the environment variable that contains the token. Only supported in declarative connection configs." - } - }, - "required": [ - "env" - ], - "additionalProperties": false - } - ] - }, - "issuer": { - "anyOf": [ - { - "type": "object", - "properties": { - "secret": { - "type": "string", - "description": "The name of the secret that contains the token." - } - }, - "required": [ - "secret" - ], - "additionalProperties": false - }, - { - "type": "object", - "properties": { - "env": { - "type": "string", - "description": "The name of the environment variable that contains the token. Only supported in declarative connection configs." - } - }, - "required": [ - "env" - ], - "additionalProperties": false - } - ] - } - }, - "required": [ - "provider", - "clientId", - "clientSecret", - "issuer" - ] - }, - "GCPIAPIdentityProviderConfig": { - "type": "object", - "properties": { - "provider": { - "const": "gcp-iap" - }, - "audience": { - "anyOf": [ - { - "type": "object", - "properties": { - "secret": { - "type": "string", - "description": "The name of the secret that contains the token." - } - }, - "required": [ - "secret" - ], - "additionalProperties": false - }, - { - "type": "object", - "properties": { - "env": { - "type": "string", - "description": "The name of the environment variable that contains the token. Only supported in declarative connection configs." - } - }, - "required": [ - "env" - ], - "additionalProperties": false - } - ] - } - }, - "required": [ - "provider", - "audience" - ] - } - }, - "oneOf": [ - { - "type": "object", - "properties": { - "provider": { - "const": "github" - }, - "purpose": { - "enum": [ - "sso", - "identity" - ] - }, - "clientId": { - "anyOf": [ - { - "type": "object", - "properties": { - "secret": { - "type": "string", - "description": "The name of the secret that contains the token." - } - }, - "required": [ - "secret" - ], - "additionalProperties": false - }, - { - "type": "object", - "properties": { - "env": { - "type": "string", - "description": "The name of the environment variable that contains the token. Only supported in declarative connection configs." - } - }, - "required": [ - "env" - ], - "additionalProperties": false - } - ] - }, - "clientSecret": { - "anyOf": [ - { - "type": "object", - "properties": { - "secret": { - "type": "string", - "description": "The name of the secret that contains the token." - } - }, - "required": [ - "secret" - ], - "additionalProperties": false - }, - { - "type": "object", - "properties": { - "env": { - "type": "string", - "description": "The name of the environment variable that contains the token. Only supported in declarative connection configs." - } - }, - "required": [ - "env" - ], - "additionalProperties": false - } - ] - }, - "baseUrl": { - "anyOf": [ - { - "type": "object", - "properties": { - "secret": { - "type": "string", - "description": "The name of the secret that contains the token." - } - }, - "required": [ - "secret" - ], - "additionalProperties": false - }, - { - "type": "object", - "properties": { - "env": { - "type": "string", - "description": "The name of the environment variable that contains the token. Only supported in declarative connection configs." - } - }, - "required": [ - "env" - ], - "additionalProperties": false - } - ] - } - }, - "required": [ - "provider", - "purpose", - "clientId", - "clientSecret" - ] - }, - { - "type": "object", - "properties": { - "provider": { - "const": "gitlab" - }, - "purpose": { - "enum": [ - "sso", - "identity" - ] - }, - "clientId": { - "anyOf": [ - { - "type": "object", - "properties": { - "secret": { - "type": "string", - "description": "The name of the secret that contains the token." - } - }, - "required": [ - "secret" - ], - "additionalProperties": false - }, - { - "type": "object", - "properties": { - "env": { - "type": "string", - "description": "The name of the environment variable that contains the token. Only supported in declarative connection configs." - } - }, - "required": [ - "env" - ], - "additionalProperties": false - } - ] - }, - "clientSecret": { - "anyOf": [ - { - "type": "object", - "properties": { - "secret": { - "type": "string", - "description": "The name of the secret that contains the token." - } - }, - "required": [ - "secret" - ], - "additionalProperties": false - }, - { - "type": "object", - "properties": { - "env": { - "type": "string", - "description": "The name of the environment variable that contains the token. Only supported in declarative connection configs." - } - }, - "required": [ - "env" - ], - "additionalProperties": false - } - ] - }, - "baseUrl": { - "anyOf": [ - { - "type": "object", - "properties": { - "secret": { - "type": "string", - "description": "The name of the secret that contains the token." - } - }, - "required": [ - "secret" - ], - "additionalProperties": false - }, - { - "type": "object", - "properties": { - "env": { - "type": "string", - "description": "The name of the environment variable that contains the token. Only supported in declarative connection configs." - } - }, - "required": [ - "env" - ], - "additionalProperties": false - } - ] - } - }, - "required": [ - "provider", - "purpose", - "clientId", - "clientSecret", - "baseUrl" - ] - }, - { - "type": "object", - "properties": { - "provider": { - "const": "google" - }, - "clientId": { - "anyOf": [ - { - "type": "object", - "properties": { - "secret": { - "type": "string", - "description": "The name of the secret that contains the token." - } - }, - "required": [ - "secret" - ], - "additionalProperties": false - }, - { - "type": "object", - "properties": { - "env": { - "type": "string", - "description": "The name of the environment variable that contains the token. Only supported in declarative connection configs." - } - }, - "required": [ - "env" - ], - "additionalProperties": false - } - ] - }, - "clientSecret": { - "anyOf": [ - { - "type": "object", - "properties": { - "secret": { - "type": "string", - "description": "The name of the secret that contains the token." - } - }, - "required": [ - "secret" - ], - "additionalProperties": false - }, - { - "type": "object", - "properties": { - "env": { - "type": "string", - "description": "The name of the environment variable that contains the token. Only supported in declarative connection configs." - } - }, - "required": [ - "env" - ], - "additionalProperties": false - } - ] - } - }, - "required": [ - "provider", - "clientId", - "clientSecret" - ] - }, - { - "type": "object", - "properties": { - "provider": { - "const": "okta" - }, - "clientId": { - "anyOf": [ - { - "type": "object", - "properties": { - "secret": { - "type": "string", - "description": "The name of the secret that contains the token." - } - }, - "required": [ - "secret" - ], - "additionalProperties": false - }, - { - "type": "object", - "properties": { - "env": { - "type": "string", - "description": "The name of the environment variable that contains the token. Only supported in declarative connection configs." - } - }, - "required": [ - "env" - ], - "additionalProperties": false - } - ] - }, - "clientSecret": { - "anyOf": [ - { - "type": "object", - "properties": { - "secret": { - "type": "string", - "description": "The name of the secret that contains the token." - } - }, - "required": [ - "secret" - ], - "additionalProperties": false - }, - { - "type": "object", - "properties": { - "env": { - "type": "string", - "description": "The name of the environment variable that contains the token. Only supported in declarative connection configs." - } - }, - "required": [ - "env" - ], - "additionalProperties": false - } - ] - }, - "issuer": { - "anyOf": [ - { - "type": "object", - "properties": { - "secret": { - "type": "string", - "description": "The name of the secret that contains the token." - } - }, - "required": [ - "secret" - ], - "additionalProperties": false - }, - { - "type": "object", - "properties": { - "env": { - "type": "string", - "description": "The name of the environment variable that contains the token. Only supported in declarative connection configs." - } - }, - "required": [ - "env" - ], - "additionalProperties": false - } - ] - } - }, - "required": [ - "provider", - "clientId", - "clientSecret", - "issuer" - ] - }, - { - "type": "object", - "properties": { - "provider": { - "const": "keycloak" - }, - "clientId": { - "anyOf": [ - { - "type": "object", - "properties": { - "secret": { - "type": "string", - "description": "The name of the secret that contains the token." - } - }, - "required": [ - "secret" - ], - "additionalProperties": false - }, - { - "type": "object", - "properties": { - "env": { - "type": "string", - "description": "The name of the environment variable that contains the token. Only supported in declarative connection configs." - } - }, - "required": [ - "env" - ], - "additionalProperties": false - } - ] - }, - "clientSecret": { - "anyOf": [ - { - "type": "object", - "properties": { - "secret": { - "type": "string", - "description": "The name of the secret that contains the token." - } - }, - "required": [ - "secret" - ], - "additionalProperties": false - }, - { - "type": "object", - "properties": { - "env": { - "type": "string", - "description": "The name of the environment variable that contains the token. Only supported in declarative connection configs." - } - }, - "required": [ - "env" - ], - "additionalProperties": false - } - ] - }, - "issuer": { - "anyOf": [ - { - "type": "object", - "properties": { - "secret": { - "type": "string", - "description": "The name of the secret that contains the token." - } - }, - "required": [ - "secret" - ], - "additionalProperties": false - }, - { - "type": "object", - "properties": { - "env": { - "type": "string", - "description": "The name of the environment variable that contains the token. Only supported in declarative connection configs." - } - }, - "required": [ - "env" - ], - "additionalProperties": false - } - ] - } - }, - "required": [ - "provider", - "clientId", - "clientSecret", - "issuer" - ] - }, - { - "type": "object", - "properties": { - "provider": { - "const": "microsoft-entra-id" - }, - "clientId": { - "anyOf": [ - { - "type": "object", - "properties": { - "secret": { - "type": "string", - "description": "The name of the secret that contains the token." - } - }, - "required": [ - "secret" - ], - "additionalProperties": false - }, - { - "type": "object", - "properties": { - "env": { - "type": "string", - "description": "The name of the environment variable that contains the token. Only supported in declarative connection configs." - } - }, - "required": [ - "env" - ], - "additionalProperties": false - } - ] - }, - "clientSecret": { - "anyOf": [ - { - "type": "object", - "properties": { - "secret": { - "type": "string", - "description": "The name of the secret that contains the token." - } - }, - "required": [ - "secret" - ], - "additionalProperties": false - }, - { - "type": "object", - "properties": { - "env": { - "type": "string", - "description": "The name of the environment variable that contains the token. Only supported in declarative connection configs." - } - }, - "required": [ - "env" - ], - "additionalProperties": false - } - ] - }, - "issuer": { - "anyOf": [ - { - "type": "object", - "properties": { - "secret": { - "type": "string", - "description": "The name of the secret that contains the token." - } - }, - "required": [ - "secret" - ], - "additionalProperties": false - }, - { - "type": "object", - "properties": { - "env": { - "type": "string", - "description": "The name of the environment variable that contains the token. Only supported in declarative connection configs." - } - }, - "required": [ - "env" - ], - "additionalProperties": false - } - ] - } - }, - "required": [ - "provider", - "clientId", - "clientSecret", - "issuer" - ] - }, - { - "type": "object", - "properties": { - "provider": { - "const": "gcp-iap" - }, - "audience": { - "anyOf": [ - { - "type": "object", - "properties": { - "secret": { - "type": "string", - "description": "The name of the secret that contains the token." - } - }, - "required": [ - "secret" - ], - "additionalProperties": false - }, - { - "type": "object", - "properties": { - "env": { - "type": "string", - "description": "The name of the environment variable that contains the token. Only supported in declarative connection configs." - } - }, - "required": [ - "env" - ], - "additionalProperties": false - } - ] - } - }, - "required": [ - "provider", - "audience" - ] - } - ] -} as const; -export { schema as authProviderSchema }; \ No newline at end of file diff --git a/packages/schemas/src/v3/authProvider.type.ts b/packages/schemas/src/v3/authProvider.type.ts deleted file mode 100644 index 5cea9cf4..00000000 --- a/packages/schemas/src/v3/authProvider.type.ts +++ /dev/null @@ -1,275 +0,0 @@ -// THIS IS A AUTO-GENERATED FILE. DO NOT MODIFY MANUALLY! - -export type IdentityProviderConfig = - | GitHubIdentityProviderConfig - | GitLabIdentityProviderConfig - | GoogleIdentityProviderConfig - | OktaIdentityProviderConfig - | KeycloakIdentityProviderConfig - | MicrosoftEntraIDIdentityProviderConfig - | GCPIAPIdentityProviderConfig; - -export interface GitHubIdentityProviderConfig { - provider: "github"; - purpose: "sso" | "identity"; - clientId: - | { - /** - * The name of the secret that contains the token. - */ - secret: string; - } - | { - /** - * The name of the environment variable that contains the token. Only supported in declarative connection configs. - */ - env: string; - }; - clientSecret: - | { - /** - * The name of the secret that contains the token. - */ - secret: string; - } - | { - /** - * The name of the environment variable that contains the token. Only supported in declarative connection configs. - */ - env: string; - }; - baseUrl?: - | { - /** - * The name of the secret that contains the token. - */ - secret: string; - } - | { - /** - * The name of the environment variable that contains the token. Only supported in declarative connection configs. - */ - env: string; - }; - [k: string]: unknown; -} -export interface GitLabIdentityProviderConfig { - provider: "gitlab"; - purpose: "sso" | "identity"; - clientId: - | { - /** - * The name of the secret that contains the token. - */ - secret: string; - } - | { - /** - * The name of the environment variable that contains the token. Only supported in declarative connection configs. - */ - env: string; - }; - clientSecret: - | { - /** - * The name of the secret that contains the token. - */ - secret: string; - } - | { - /** - * The name of the environment variable that contains the token. Only supported in declarative connection configs. - */ - env: string; - }; - baseUrl: - | { - /** - * The name of the secret that contains the token. - */ - secret: string; - } - | { - /** - * The name of the environment variable that contains the token. Only supported in declarative connection configs. - */ - env: string; - }; - [k: string]: unknown; -} -export interface GoogleIdentityProviderConfig { - provider: "google"; - clientId: - | { - /** - * The name of the secret that contains the token. - */ - secret: string; - } - | { - /** - * The name of the environment variable that contains the token. Only supported in declarative connection configs. - */ - env: string; - }; - clientSecret: - | { - /** - * The name of the secret that contains the token. - */ - secret: string; - } - | { - /** - * The name of the environment variable that contains the token. Only supported in declarative connection configs. - */ - env: string; - }; - [k: string]: unknown; -} -export interface OktaIdentityProviderConfig { - provider: "okta"; - clientId: - | { - /** - * The name of the secret that contains the token. - */ - secret: string; - } - | { - /** - * The name of the environment variable that contains the token. Only supported in declarative connection configs. - */ - env: string; - }; - clientSecret: - | { - /** - * The name of the secret that contains the token. - */ - secret: string; - } - | { - /** - * The name of the environment variable that contains the token. Only supported in declarative connection configs. - */ - env: string; - }; - issuer: - | { - /** - * The name of the secret that contains the token. - */ - secret: string; - } - | { - /** - * The name of the environment variable that contains the token. Only supported in declarative connection configs. - */ - env: string; - }; - [k: string]: unknown; -} -export interface KeycloakIdentityProviderConfig { - provider: "keycloak"; - clientId: - | { - /** - * The name of the secret that contains the token. - */ - secret: string; - } - | { - /** - * The name of the environment variable that contains the token. Only supported in declarative connection configs. - */ - env: string; - }; - clientSecret: - | { - /** - * The name of the secret that contains the token. - */ - secret: string; - } - | { - /** - * The name of the environment variable that contains the token. Only supported in declarative connection configs. - */ - env: string; - }; - issuer: - | { - /** - * The name of the secret that contains the token. - */ - secret: string; - } - | { - /** - * The name of the environment variable that contains the token. Only supported in declarative connection configs. - */ - env: string; - }; - [k: string]: unknown; -} -export interface MicrosoftEntraIDIdentityProviderConfig { - provider: "microsoft-entra-id"; - clientId: - | { - /** - * The name of the secret that contains the token. - */ - secret: string; - } - | { - /** - * The name of the environment variable that contains the token. Only supported in declarative connection configs. - */ - env: string; - }; - clientSecret: - | { - /** - * The name of the secret that contains the token. - */ - secret: string; - } - | { - /** - * The name of the environment variable that contains the token. Only supported in declarative connection configs. - */ - env: string; - }; - issuer: - | { - /** - * The name of the secret that contains the token. - */ - secret: string; - } - | { - /** - * The name of the environment variable that contains the token. Only supported in declarative connection configs. - */ - env: string; - }; - [k: string]: unknown; -} -export interface GCPIAPIdentityProviderConfig { - provider: "gcp-iap"; - audience: - | { - /** - * The name of the secret that contains the token. - */ - secret: string; - } - | { - /** - * The name of the environment variable that contains the token. Only supported in declarative connection configs. - */ - env: string; - }; - [k: string]: unknown; -} diff --git a/packages/schemas/src/v3/identityProvider.schema.ts b/packages/schemas/src/v3/identityProvider.schema.ts index 0dd3ddc4..708c3b4c 100644 --- a/packages/schemas/src/v3/identityProvider.schema.ts +++ b/packages/schemas/src/v3/identityProvider.schema.ts @@ -5,6 +5,7 @@ const schema = { "definitions": { "GitHubIdentityProviderConfig": { "type": "object", + "additionalProperties": false, "properties": { "provider": { "const": "github" @@ -12,7 +13,7 @@ const schema = { "purpose": { "enum": [ "sso", - "integration" + "account_linking" ] }, "clientId": { @@ -76,36 +77,17 @@ const schema = { ] }, "baseUrl": { - "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 - } - ] + "type": "string", + "format": "url", + "default": "https://github.com", + "description": "The URL of the GitHub host. Defaults to https://github.com", + "examples": [ + "https://github.com", + "https://github.example.com" + ], + "pattern": "^https?:\\/\\/[^\\s/$.?#].[^\\s]*$" }, - "required": { + "accountLinkingRequired": { "type": "boolean", "default": false } @@ -119,6 +101,7 @@ const schema = { }, "GitLabIdentityProviderConfig": { "type": "object", + "additionalProperties": false, "properties": { "provider": { "const": "gitlab" @@ -126,7 +109,7 @@ const schema = { "purpose": { "enum": [ "sso", - "integration" + "account_linking" ] }, "clientId": { @@ -190,36 +173,17 @@ const schema = { ] }, "baseUrl": { - "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 - } - ] + "type": "string", + "format": "url", + "default": "https://gitlab.com", + "description": "The URL of the GitLab host. Defaults to https://gitlab.com", + "examples": [ + "https://gitlab.com", + "https://gitlab.example.com" + ], + "pattern": "^https?:\\/\\/[^\\s/$.?#].[^\\s]*$" }, - "required": { + "accountLinkingRequired": { "type": "boolean", "default": false } @@ -233,10 +197,14 @@ const schema = { }, "GoogleIdentityProviderConfig": { "type": "object", + "additionalProperties": false, "properties": { "provider": { "const": "google" }, + "purpose": { + "const": "sso" + }, "clientId": { "anyOf": [ { @@ -300,16 +268,21 @@ const schema = { }, "required": [ "provider", + "purpose", "clientId", "clientSecret" ] }, "OktaIdentityProviderConfig": { "type": "object", + "additionalProperties": false, "properties": { "provider": { "const": "okta" }, + "purpose": { + "const": "sso" + }, "clientId": { "anyOf": [ { @@ -403,6 +376,7 @@ const schema = { }, "required": [ "provider", + "purpose", "clientId", "clientSecret", "issuer" @@ -410,10 +384,14 @@ const schema = { }, "KeycloakIdentityProviderConfig": { "type": "object", + "additionalProperties": false, "properties": { "provider": { "const": "keycloak" }, + "purpose": { + "const": "sso" + }, "clientId": { "anyOf": [ { @@ -507,6 +485,7 @@ const schema = { }, "required": [ "provider", + "purpose", "clientId", "clientSecret", "issuer" @@ -514,10 +493,14 @@ const schema = { }, "MicrosoftEntraIDIdentityProviderConfig": { "type": "object", + "additionalProperties": false, "properties": { "provider": { "const": "microsoft-entra-id" }, + "purpose": { + "const": "sso" + }, "clientId": { "anyOf": [ { @@ -611,6 +594,7 @@ const schema = { }, "required": [ "provider", + "purpose", "clientId", "clientSecret", "issuer" @@ -618,10 +602,14 @@ const schema = { }, "GCPIAPIdentityProviderConfig": { "type": "object", + "additionalProperties": false, "properties": { "provider": { "const": "gcp-iap" }, + "purpose": { + "const": "sso" + }, "audience": { "anyOf": [ { @@ -655,6 +643,7 @@ const schema = { }, "required": [ "provider", + "purpose", "audience" ] } @@ -662,6 +651,7 @@ const schema = { "oneOf": [ { "type": "object", + "additionalProperties": false, "properties": { "provider": { "const": "github" @@ -669,7 +659,7 @@ const schema = { "purpose": { "enum": [ "sso", - "integration" + "account_linking" ] }, "clientId": { @@ -733,36 +723,17 @@ const schema = { ] }, "baseUrl": { - "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 - } - ] + "type": "string", + "format": "url", + "default": "https://github.com", + "description": "The URL of the GitHub host. Defaults to https://github.com", + "examples": [ + "https://github.com", + "https://github.example.com" + ], + "pattern": "^https?:\\/\\/[^\\s/$.?#].[^\\s]*$" }, - "required": { + "accountLinkingRequired": { "type": "boolean", "default": false } @@ -776,6 +747,7 @@ const schema = { }, { "type": "object", + "additionalProperties": false, "properties": { "provider": { "const": "gitlab" @@ -783,7 +755,7 @@ const schema = { "purpose": { "enum": [ "sso", - "integration" + "account_linking" ] }, "clientId": { @@ -847,36 +819,17 @@ const schema = { ] }, "baseUrl": { - "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 - } - ] + "type": "string", + "format": "url", + "default": "https://gitlab.com", + "description": "The URL of the GitLab host. Defaults to https://gitlab.com", + "examples": [ + "https://gitlab.com", + "https://gitlab.example.com" + ], + "pattern": "^https?:\\/\\/[^\\s/$.?#].[^\\s]*$" }, - "required": { + "accountLinkingRequired": { "type": "boolean", "default": false } @@ -890,10 +843,14 @@ const schema = { }, { "type": "object", + "additionalProperties": false, "properties": { "provider": { "const": "google" }, + "purpose": { + "const": "sso" + }, "clientId": { "anyOf": [ { @@ -957,16 +914,21 @@ const schema = { }, "required": [ "provider", + "purpose", "clientId", "clientSecret" ] }, { "type": "object", + "additionalProperties": false, "properties": { "provider": { "const": "okta" }, + "purpose": { + "const": "sso" + }, "clientId": { "anyOf": [ { @@ -1060,6 +1022,7 @@ const schema = { }, "required": [ "provider", + "purpose", "clientId", "clientSecret", "issuer" @@ -1067,10 +1030,14 @@ const schema = { }, { "type": "object", + "additionalProperties": false, "properties": { "provider": { "const": "keycloak" }, + "purpose": { + "const": "sso" + }, "clientId": { "anyOf": [ { @@ -1164,6 +1131,7 @@ const schema = { }, "required": [ "provider", + "purpose", "clientId", "clientSecret", "issuer" @@ -1171,10 +1139,14 @@ const schema = { }, { "type": "object", + "additionalProperties": false, "properties": { "provider": { "const": "microsoft-entra-id" }, + "purpose": { + "const": "sso" + }, "clientId": { "anyOf": [ { @@ -1268,6 +1240,7 @@ const schema = { }, "required": [ "provider", + "purpose", "clientId", "clientSecret", "issuer" @@ -1275,10 +1248,14 @@ const schema = { }, { "type": "object", + "additionalProperties": false, "properties": { "provider": { "const": "gcp-iap" }, + "purpose": { + "const": "sso" + }, "audience": { "anyOf": [ { @@ -1312,6 +1289,7 @@ const schema = { }, "required": [ "provider", + "purpose", "audience" ] } diff --git a/packages/schemas/src/v3/identityProvider.type.ts b/packages/schemas/src/v3/identityProvider.type.ts index 541f0ca7..31d7a9a8 100644 --- a/packages/schemas/src/v3/identityProvider.type.ts +++ b/packages/schemas/src/v3/identityProvider.type.ts @@ -11,7 +11,7 @@ export type IdentityProviderConfig = export interface GitHubIdentityProviderConfig { provider: "github"; - purpose: "sso" | "integration"; + purpose: "sso" | "account_linking"; clientId: | { /** @@ -38,25 +38,15 @@ export interface GitHubIdentityProviderConfig { */ googleCloudSecret: string; }; - baseUrl?: - | { - /** - * 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; - }; - required?: boolean; - [k: string]: unknown; + /** + * The URL of the GitHub host. Defaults to https://github.com + */ + baseUrl?: string; + accountLinkingRequired?: boolean; } export interface GitLabIdentityProviderConfig { provider: "gitlab"; - purpose: "sso" | "integration"; + purpose: "sso" | "account_linking"; clientId: | { /** @@ -83,24 +73,15 @@ export interface GitLabIdentityProviderConfig { */ googleCloudSecret: string; }; - baseUrl?: - | { - /** - * 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; - }; - required?: boolean; - [k: string]: unknown; + /** + * The URL of the GitLab host. Defaults to https://gitlab.com + */ + baseUrl?: string; + accountLinkingRequired?: boolean; } export interface GoogleIdentityProviderConfig { provider: "google"; + purpose: "sso"; clientId: | { /** @@ -127,10 +108,10 @@ export interface GoogleIdentityProviderConfig { */ googleCloudSecret: string; }; - [k: string]: unknown; } export interface OktaIdentityProviderConfig { provider: "okta"; + purpose: "sso"; clientId: | { /** @@ -170,10 +151,10 @@ export interface OktaIdentityProviderConfig { */ googleCloudSecret: string; }; - [k: string]: unknown; } export interface KeycloakIdentityProviderConfig { provider: "keycloak"; + purpose: "sso"; clientId: | { /** @@ -213,10 +194,10 @@ export interface KeycloakIdentityProviderConfig { */ googleCloudSecret: string; }; - [k: string]: unknown; } export interface MicrosoftEntraIDIdentityProviderConfig { provider: "microsoft-entra-id"; + purpose: "sso"; clientId: | { /** @@ -256,10 +237,10 @@ export interface MicrosoftEntraIDIdentityProviderConfig { */ googleCloudSecret: string; }; - [k: string]: unknown; } export interface GCPIAPIdentityProviderConfig { provider: "gcp-iap"; + purpose: "sso"; audience: | { /** @@ -273,5 +254,4 @@ export interface GCPIAPIdentityProviderConfig { */ googleCloudSecret: string; }; - [k: string]: unknown; } diff --git a/packages/schemas/src/v3/index.schema.ts b/packages/schemas/src/v3/index.schema.ts index a6bdd2f6..ee045243 100644 --- a/packages/schemas/src/v3/index.schema.ts +++ b/packages/schemas/src/v3/index.schema.ts @@ -4409,6 +4409,7 @@ const schema = { "definitions": { "GitHubIdentityProviderConfig": { "type": "object", + "additionalProperties": false, "properties": { "provider": { "const": "github" @@ -4416,7 +4417,7 @@ const schema = { "purpose": { "enum": [ "sso", - "integration" + "account_linking" ] }, "clientId": { @@ -4480,36 +4481,17 @@ const schema = { ] }, "baseUrl": { - "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 - } - ] + "type": "string", + "format": "url", + "default": "https://github.com", + "description": "The URL of the GitHub host. Defaults to https://github.com", + "examples": [ + "https://github.com", + "https://github.example.com" + ], + "pattern": "^https?:\\/\\/[^\\s/$.?#].[^\\s]*$" }, - "required": { + "accountLinkingRequired": { "type": "boolean", "default": false } @@ -4523,6 +4505,7 @@ const schema = { }, "GitLabIdentityProviderConfig": { "type": "object", + "additionalProperties": false, "properties": { "provider": { "const": "gitlab" @@ -4530,7 +4513,7 @@ const schema = { "purpose": { "enum": [ "sso", - "integration" + "account_linking" ] }, "clientId": { @@ -4594,36 +4577,17 @@ const schema = { ] }, "baseUrl": { - "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 - } - ] + "type": "string", + "format": "url", + "default": "https://gitlab.com", + "description": "The URL of the GitLab host. Defaults to https://gitlab.com", + "examples": [ + "https://gitlab.com", + "https://gitlab.example.com" + ], + "pattern": "^https?:\\/\\/[^\\s/$.?#].[^\\s]*$" }, - "required": { + "accountLinkingRequired": { "type": "boolean", "default": false } @@ -4637,10 +4601,14 @@ const schema = { }, "GoogleIdentityProviderConfig": { "type": "object", + "additionalProperties": false, "properties": { "provider": { "const": "google" }, + "purpose": { + "const": "sso" + }, "clientId": { "anyOf": [ { @@ -4704,16 +4672,21 @@ const schema = { }, "required": [ "provider", + "purpose", "clientId", "clientSecret" ] }, "OktaIdentityProviderConfig": { "type": "object", + "additionalProperties": false, "properties": { "provider": { "const": "okta" }, + "purpose": { + "const": "sso" + }, "clientId": { "anyOf": [ { @@ -4807,6 +4780,7 @@ const schema = { }, "required": [ "provider", + "purpose", "clientId", "clientSecret", "issuer" @@ -4814,10 +4788,14 @@ const schema = { }, "KeycloakIdentityProviderConfig": { "type": "object", + "additionalProperties": false, "properties": { "provider": { "const": "keycloak" }, + "purpose": { + "const": "sso" + }, "clientId": { "anyOf": [ { @@ -4911,6 +4889,7 @@ const schema = { }, "required": [ "provider", + "purpose", "clientId", "clientSecret", "issuer" @@ -4918,10 +4897,14 @@ const schema = { }, "MicrosoftEntraIDIdentityProviderConfig": { "type": "object", + "additionalProperties": false, "properties": { "provider": { "const": "microsoft-entra-id" }, + "purpose": { + "const": "sso" + }, "clientId": { "anyOf": [ { @@ -5015,6 +4998,7 @@ const schema = { }, "required": [ "provider", + "purpose", "clientId", "clientSecret", "issuer" @@ -5022,10 +5006,14 @@ const schema = { }, "GCPIAPIdentityProviderConfig": { "type": "object", + "additionalProperties": false, "properties": { "provider": { "const": "gcp-iap" }, + "purpose": { + "const": "sso" + }, "audience": { "anyOf": [ { @@ -5059,6 +5047,7 @@ const schema = { }, "required": [ "provider", + "purpose", "audience" ] } @@ -5066,6 +5055,7 @@ const schema = { "oneOf": [ { "type": "object", + "additionalProperties": false, "properties": { "provider": { "const": "github" @@ -5073,7 +5063,7 @@ const schema = { "purpose": { "enum": [ "sso", - "integration" + "account_linking" ] }, "clientId": { @@ -5137,36 +5127,17 @@ const schema = { ] }, "baseUrl": { - "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 - } - ] + "type": "string", + "format": "url", + "default": "https://github.com", + "description": "The URL of the GitHub host. Defaults to https://github.com", + "examples": [ + "https://github.com", + "https://github.example.com" + ], + "pattern": "^https?:\\/\\/[^\\s/$.?#].[^\\s]*$" }, - "required": { + "accountLinkingRequired": { "type": "boolean", "default": false } @@ -5180,6 +5151,7 @@ const schema = { }, { "type": "object", + "additionalProperties": false, "properties": { "provider": { "const": "gitlab" @@ -5187,7 +5159,7 @@ const schema = { "purpose": { "enum": [ "sso", - "integration" + "account_linking" ] }, "clientId": { @@ -5251,36 +5223,17 @@ const schema = { ] }, "baseUrl": { - "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 - } - ] + "type": "string", + "format": "url", + "default": "https://gitlab.com", + "description": "The URL of the GitLab host. Defaults to https://gitlab.com", + "examples": [ + "https://gitlab.com", + "https://gitlab.example.com" + ], + "pattern": "^https?:\\/\\/[^\\s/$.?#].[^\\s]*$" }, - "required": { + "accountLinkingRequired": { "type": "boolean", "default": false } @@ -5294,10 +5247,14 @@ const schema = { }, { "type": "object", + "additionalProperties": false, "properties": { "provider": { "const": "google" }, + "purpose": { + "const": "sso" + }, "clientId": { "anyOf": [ { @@ -5361,16 +5318,21 @@ const schema = { }, "required": [ "provider", + "purpose", "clientId", "clientSecret" ] }, { "type": "object", + "additionalProperties": false, "properties": { "provider": { "const": "okta" }, + "purpose": { + "const": "sso" + }, "clientId": { "anyOf": [ { @@ -5464,6 +5426,7 @@ const schema = { }, "required": [ "provider", + "purpose", "clientId", "clientSecret", "issuer" @@ -5471,10 +5434,14 @@ const schema = { }, { "type": "object", + "additionalProperties": false, "properties": { "provider": { "const": "keycloak" }, + "purpose": { + "const": "sso" + }, "clientId": { "anyOf": [ { @@ -5568,6 +5535,7 @@ const schema = { }, "required": [ "provider", + "purpose", "clientId", "clientSecret", "issuer" @@ -5575,10 +5543,14 @@ const schema = { }, { "type": "object", + "additionalProperties": false, "properties": { "provider": { "const": "microsoft-entra-id" }, + "purpose": { + "const": "sso" + }, "clientId": { "anyOf": [ { @@ -5672,6 +5644,7 @@ const schema = { }, "required": [ "provider", + "purpose", "clientId", "clientSecret", "issuer" @@ -5679,10 +5652,14 @@ const schema = { }, { "type": "object", + "additionalProperties": false, "properties": { "provider": { "const": "gcp-iap" }, + "purpose": { + "const": "sso" + }, "audience": { "anyOf": [ { @@ -5716,6 +5693,7 @@ const schema = { }, "required": [ "provider", + "purpose", "audience" ] } diff --git a/packages/schemas/src/v3/index.type.ts b/packages/schemas/src/v3/index.type.ts index aef3e6ac..0e88d8ab 100644 --- a/packages/schemas/src/v3/index.type.ts +++ b/packages/schemas/src/v3/index.type.ts @@ -1118,7 +1118,7 @@ export interface GitHubAppConfig { } export interface GitHubIdentityProviderConfig { provider: "github"; - purpose: "sso" | "integration"; + purpose: "sso" | "account_linking"; clientId: | { /** @@ -1145,25 +1145,15 @@ export interface GitHubIdentityProviderConfig { */ googleCloudSecret: string; }; - baseUrl?: - | { - /** - * 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; - }; - required?: boolean; - [k: string]: unknown; + /** + * The URL of the GitHub host. Defaults to https://github.com + */ + baseUrl?: string; + accountLinkingRequired?: boolean; } export interface GitLabIdentityProviderConfig { provider: "gitlab"; - purpose: "sso" | "integration"; + purpose: "sso" | "account_linking"; clientId: | { /** @@ -1190,24 +1180,15 @@ export interface GitLabIdentityProviderConfig { */ googleCloudSecret: string; }; - baseUrl?: - | { - /** - * 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; - }; - required?: boolean; - [k: string]: unknown; + /** + * The URL of the GitLab host. Defaults to https://gitlab.com + */ + baseUrl?: string; + accountLinkingRequired?: boolean; } export interface GoogleIdentityProviderConfig { provider: "google"; + purpose: "sso"; clientId: | { /** @@ -1234,10 +1215,10 @@ export interface GoogleIdentityProviderConfig { */ googleCloudSecret: string; }; - [k: string]: unknown; } export interface OktaIdentityProviderConfig { provider: "okta"; + purpose: "sso"; clientId: | { /** @@ -1277,10 +1258,10 @@ export interface OktaIdentityProviderConfig { */ googleCloudSecret: string; }; - [k: string]: unknown; } export interface KeycloakIdentityProviderConfig { provider: "keycloak"; + purpose: "sso"; clientId: | { /** @@ -1320,10 +1301,10 @@ export interface KeycloakIdentityProviderConfig { */ googleCloudSecret: string; }; - [k: string]: unknown; } export interface MicrosoftEntraIDIdentityProviderConfig { provider: "microsoft-entra-id"; + purpose: "sso"; clientId: | { /** @@ -1363,10 +1344,10 @@ export interface MicrosoftEntraIDIdentityProviderConfig { */ googleCloudSecret: string; }; - [k: string]: unknown; } export interface GCPIAPIdentityProviderConfig { provider: "gcp-iap"; + purpose: "sso"; audience: | { /** @@ -1380,5 +1361,4 @@ export interface GCPIAPIdentityProviderConfig { */ googleCloudSecret: string; }; - [k: string]: unknown; } diff --git a/packages/web/src/app/[domain]/layout.tsx b/packages/web/src/app/[domain]/layout.tsx index 38935953..a3079d7f 100644 --- a/packages/web/src/app/[domain]/layout.tsx +++ b/packages/web/src/app/[domain]/layout.tsx @@ -23,7 +23,7 @@ import { JoinOrganizationCard } from "@/app/components/joinOrganizationCard"; import { LogoutEscapeHatch } from "@/app/components/logoutEscapeHatch"; import { GitHubStarToast } from "./components/githubStarToast"; import { UpgradeToast } from "./components/upgradeToast"; -import { getIntegrationProviderStates } from "@/ee/features/permissionSyncing/actions"; +import { getLinkedAccountProviderStates } from "@/ee/features/permissionSyncing/actions"; import { LinkAccounts } from "@/ee/features/permissionSyncing/components/linkAccounts"; interface LayoutProps { @@ -126,16 +126,16 @@ export default async function Layout(props: LayoutProps) { } if (hasEntitlement("permission-syncing")) { - const integrationProviderStates = await getIntegrationProviderStates(); - if (isServiceError(integrationProviderStates)) { + const linkedAccountProviderStates = await getLinkedAccountProviderStates(); + if (isServiceError(linkedAccountProviderStates)) { return (

An error occurred

- {typeof integrationProviderStates.message === 'string' - ? integrationProviderStates.message + {typeof linkedAccountProviderStates.message === 'string' + ? linkedAccountProviderStates.message : "A server error occurred while checking your account status. Please try again or contact support."}

@@ -143,18 +143,18 @@ export default async function Layout(props: LayoutProps) { ) } - const hasUnlinkedProviders = integrationProviderStates.some(state => state.isLinked === false); + const hasUnlinkedProviders = linkedAccountProviderStates.some(state => state.isLinked === false); if (hasUnlinkedProviders) { const cookieStore = await cookies(); const hasSkippedOptional = cookieStore.has(OPTIONAL_PROVIDERS_LINK_SKIPPED_COOKIE_NAME); - const hasUnlinkedRequiredProviders = integrationProviderStates.some(state => state.required && !state.isLinked) + const hasUnlinkedRequiredProviders = linkedAccountProviderStates.some(state => state.required && !state.isLinked) const shouldShowLinkAccounts = hasUnlinkedRequiredProviders || !hasSkippedOptional; if (shouldShowLinkAccounts) { return (
- +
) } diff --git a/packages/web/src/app/[domain]/settings/permission-syncing/page.tsx b/packages/web/src/app/[domain]/settings/permission-syncing/page.tsx index 948171b1..d9c136c9 100644 --- a/packages/web/src/app/[domain]/settings/permission-syncing/page.tsx +++ b/packages/web/src/app/[domain]/settings/permission-syncing/page.tsx @@ -1,11 +1,11 @@ import { hasEntitlement } from "@sourcebot/shared"; -import { notFound } from "@/lib/serviceError"; +import { notFound } from "next/navigation" import { LinkedAccountsSettings } from "@/ee/features/permissionSyncing/components/linkedAccountsSettings"; export default async function PermissionSyncingPage() { const hasPermissionSyncingEntitlement = await hasEntitlement("permission-syncing"); if (!hasPermissionSyncingEntitlement) { - notFound(); + return notFound(); } return ; diff --git a/packages/web/src/auth.ts b/packages/web/src/auth.ts index d9cfeb88..64dab588 100644 --- a/packages/web/src/auth.ts +++ b/packages/web/src/auth.ts @@ -18,7 +18,7 @@ import { hasEntitlement } from '@sourcebot/shared'; import { onCreateUser } from '@/lib/authUtils'; import { getAuditService } from '@/ee/features/audit/factory'; import { SINGLE_TENANT_ORG_ID } from './lib/constants'; -import { refreshIntegrationTokens } from '@/ee/features/permissionSyncing/tokenRefresh'; +import { refreshLinkedAccountTokens } from '@/ee/features/permissionSyncing/tokenRefresh'; const auditService = getAuditService(); const eeIdentityProviders = hasEntitlement("sso") ? await getEEIdentityProviders() : []; @@ -27,29 +27,32 @@ export const runtime = 'nodejs'; export type IdentityProvider = { provider: Provider; - purpose: "sso" | "integration"; + purpose: "sso" | "account_linking"; required?: boolean; } +export type LinkedAccountToken = { + provider: string; + accessToken: string; + refreshToken: string; + expiresAt: number; + error?: string; +}; +export type LinkedAccountTokensMap = Record; + declare module 'next-auth' { interface Session { user: { id: string; } & DefaultSession['user']; - integrationProviderErrors?: Record; + linkedAccountProviderErrors?: Record; } } declare module 'next-auth/jwt' { interface JWT { userId: string; - integrationTokens?: Record; - error?: string; + linkedAccountTokens?: LinkedAccountTokensMap; } } @@ -196,24 +199,20 @@ export const { handlers, signIn, signOut, auth } = NextAuth({ token.userId = user.id; } - // When a user links a new account, store the tokens if it's an integration provider - if (account && hasEntitlement('permission-syncing')) { - if (account.access_token && account.refresh_token && account.expires_at) { - token.integrationTokens = token.integrationTokens || {}; - token.integrationTokens[account.provider] = { + if (hasEntitlement('permission-syncing')) { + if (account && account.access_token && account.refresh_token && account.expires_at) { + token.linkedAccountTokens = token.linkedAccountTokens || {}; + token.linkedAccountTokens[account.providerAccountId] = { + provider: account.provider, accessToken: account.access_token, refreshToken: account.refresh_token, expiresAt: account.expires_at, }; } - } - // Refresh all integration provider tokens that are about to expire - if (hasEntitlement('permission-syncing') && token.integrationTokens) { - token.integrationTokens = await refreshIntegrationTokens( - token.integrationTokens, - token.userId - ); + if (token.linkedAccountTokens) { + token.linkedAccountTokens = await refreshLinkedAccountTokens(token.linkedAccountTokens); + } } return token; @@ -226,16 +225,17 @@ export const { handlers, signIn, signOut, auth } = NextAuth({ // Propagate the userId to the session. id: token.userId, } - // Pass only integration provider errors to the session (not sensitive tokens) - if (token.integrationTokens) { + + // Pass only linked account provider errors to the session (not sensitive tokens) + if (token.linkedAccountTokens) { const errors: Record = {}; - for (const [provider, tokenData] of Object.entries(token.integrationTokens)) { + for (const [providerAccountId, tokenData] of Object.entries(token.linkedAccountTokens)) { if (tokenData.error) { - errors[provider] = tokenData.error; + errors[providerAccountId] = tokenData.error; } } if (Object.keys(errors).length > 0) { - session.integrationProviderErrors = errors; + session.linkedAccountProviderErrors = errors; } } return session; diff --git a/packages/web/src/ee/features/permissionSyncing/actions.ts b/packages/web/src/ee/features/permissionSyncing/actions.ts index 08779d69..95667027 100644 --- a/packages/web/src/ee/features/permissionSyncing/actions.ts +++ b/packages/web/src/ee/features/permissionSyncing/actions.ts @@ -8,21 +8,21 @@ import { env } from "@/env.mjs"; import { OrgRole } from "@sourcebot/db"; import { cookies } from "next/headers"; import { OPTIONAL_PROVIDERS_LINK_SKIPPED_COOKIE_NAME } from "@/lib/constants"; -import { IntegrationIdentityProviderState } from "@/ee/features/permissionSyncing/types"; +import { LinkedAccountProviderState } from "@/ee/features/permissionSyncing/types"; import { auth } from "@/auth"; const logger = createLogger('web-ee-permission-syncing-actions'); -export const getIntegrationProviderStates = async () => sew(() => +export const getLinkedAccountProviderStates = async () => sew(() => withAuthV2(async ({ prisma, role, user }) => withMinimumOrgRole(role, OrgRole.MEMBER, async () => { const config = await loadConfig(env.CONFIG_PATH); - const integrationProviderConfigs = config.identityProviders ?? []; + const linkedAccountProviderConfigs = config.identityProviders ?? []; const linkedAccounts = await prisma.account.findMany({ where: { userId: user.id, provider: { - in: integrationProviderConfigs.map(p => p.provider) + in: linkedAccountProviderConfigs.map(p => p.provider) } }, select: { @@ -33,44 +33,44 @@ export const getIntegrationProviderStates = async () => sew(() => // Fetch the session to get token errors const session = await auth(); - const providerErrors = session?.integrationProviderErrors; + const providerErrors = session?.linkedAccountProviderErrors; - const integrationProviderState: IntegrationIdentityProviderState[] = []; - for (const integrationProviderConfig of integrationProviderConfigs) { - if (integrationProviderConfig.purpose === "integration") { + const linkedAccountProviderState: LinkedAccountProviderState[] = []; + for (const linkedAccountProviderConfig of linkedAccountProviderConfigs) { + if (linkedAccountProviderConfig.purpose === "account_linking") { const linkedAccount = linkedAccounts.find( - account => account.provider === integrationProviderConfig.provider + account => account.provider === linkedAccountProviderConfig.provider ); const isLinked = !!linkedAccount; - const isRequired = integrationProviderConfig.required ?? false; - const providerError = providerErrors?.[integrationProviderConfig.provider]; + const isRequired = linkedAccountProviderConfig.accountLinkingRequired ?? false; + const providerError = linkedAccount ? providerErrors?.[linkedAccount.providerAccountId] : undefined; - integrationProviderState.push({ - id: integrationProviderConfig.provider, + linkedAccountProviderState.push({ + id: linkedAccountProviderConfig.provider, required: isRequired, isLinked, linkedAccountId: linkedAccount?.providerAccountId, error: providerError - } as IntegrationIdentityProviderState); + } as LinkedAccountProviderState); } } - return integrationProviderState; + return linkedAccountProviderState; }) ) ); -export const unlinkIntegrationProvider = async (provider: string) => sew(() => +export const unlinkLinkedAccountProvider = async (provider: string) => sew(() => withAuthV2(async ({ prisma, role, user }) => withMinimumOrgRole(role, OrgRole.MEMBER, async () => { const config = await loadConfig(env.CONFIG_PATH); const identityProviders = config.identityProviders ?? []; const providerConfig = identityProviders.find(idp => idp.provider === provider) - if (!providerConfig || !('purpose' in providerConfig) || providerConfig.purpose !== "integration") { - throw new Error("Provider is not an integration provider"); + if (!providerConfig || providerConfig.purpose !== "account_linking") { + throw new Error("Provider is not a linked account provider"); } // Delete the account @@ -81,11 +81,11 @@ export const unlinkIntegrationProvider = async (provider: string) => sew(() => }, }); - logger.info(`Unlinked integration provider ${provider} for user ${user.id}. Deleted ${result.count} account(s).`); + logger.info(`Unlinked account provider ${provider} for user ${user.id}. Deleted ${result.count} account(s).`); // If we're unlinking a required identity provider then we want to wipe the optional skip cookie if it exists so that we give the // user the option of linking optional providers in the same link accounts screen - const isRequired = providerConfig.required ?? false; + const isRequired = providerConfig.accountLinkingRequired ?? false; if (isRequired) { const cookieStore = await cookies(); cookieStore.delete(OPTIONAL_PROVIDERS_LINK_SKIPPED_COOKIE_NAME); diff --git a/packages/web/src/ee/features/permissionSyncing/components/linkAccounts.tsx b/packages/web/src/ee/features/permissionSyncing/components/linkAccounts.tsx index 52859269..dd3aa531 100644 --- a/packages/web/src/ee/features/permissionSyncing/components/linkAccounts.tsx +++ b/packages/web/src/ee/features/permissionSyncing/components/linkAccounts.tsx @@ -5,15 +5,15 @@ import { Button } from "@/components/ui/button"; import { skipOptionalProvidersLink } from "@/ee/features/permissionSyncing/actions"; import { useRouter } from "next/navigation"; import { useState } from "react"; -import { IntegrationProviderCard } from "./integrationProviderCard"; -import { IntegrationIdentityProviderState } from "@/ee/features/permissionSyncing/types"; +import { LinkedAccountProviderCard } from "./linkedAccountProviderCard"; +import { LinkedAccountProviderState } from "@/ee/features/permissionSyncing/types"; interface LinkAccountsProps { - integrationProviderStates: IntegrationIdentityProviderState[] + linkedAccountProviderStates: LinkedAccountProviderState[] callbackUrl?: string; } -export const LinkAccounts = ({ integrationProviderStates, callbackUrl }: LinkAccountsProps) => { +export const LinkAccounts = ({ linkedAccountProviderStates, callbackUrl }: LinkAccountsProps) => { const router = useRouter(); const [isSkipping, setIsSkipping] = useState(false); @@ -29,7 +29,7 @@ export const LinkAccounts = ({ integrationProviderStates, callbackUrl }: LinkAcc } }; - const canSkip = !integrationProviderStates.some(state => state.required && !state.isLinked); + const canSkip = !linkedAccountProviderStates.some(state => state.required && !state.isLinked); return ( @@ -42,12 +42,12 @@ export const LinkAccounts = ({ integrationProviderStates, callbackUrl }: LinkAcc
- {integrationProviderStates + {linkedAccountProviderStates .sort((a, b) => (b.required ? 1 : 0) - (a.required ? 1 : 0)) .map(state => ( - ))} diff --git a/packages/web/src/ee/features/permissionSyncing/components/integrationProviderCard.tsx b/packages/web/src/ee/features/permissionSyncing/components/linkedAccountProviderCard.tsx similarity index 79% rename from packages/web/src/ee/features/permissionSyncing/components/integrationProviderCard.tsx rename to packages/web/src/ee/features/permissionSyncing/components/linkedAccountProviderCard.tsx index 85522712..766784dd 100644 --- a/packages/web/src/ee/features/permissionSyncing/components/integrationProviderCard.tsx +++ b/packages/web/src/ee/features/permissionSyncing/components/linkedAccountProviderCard.tsx @@ -5,19 +5,19 @@ import { ProviderIcon } from "./providerIcon"; import { ProviderInfo } from "./providerInfo"; import { UnlinkButton } from "./unlinkButton"; import { LinkButton } from "./linkButton"; -import { IntegrationIdentityProviderState } from "@/ee/features/permissionSyncing/types" +import { LinkedAccountProviderState } from "@/ee/features/permissionSyncing/types" import { SINGLE_TENANT_ORG_DOMAIN } from "@/lib/constants"; -interface IntegrationProviderCardProps { - integrationProviderState: IntegrationIdentityProviderState; +interface LinkedAccountProviderCardProps { + linkedAccountProviderState: LinkedAccountProviderState; callbackUrl?: string; } -export function IntegrationProviderCard({ - integrationProviderState, +export function LinkedAccountProviderCard({ + linkedAccountProviderState, callbackUrl, -}: IntegrationProviderCardProps) { - const providerInfo = getAuthProviderInfo(integrationProviderState.id); +}: LinkedAccountProviderCardProps) { + const providerInfo = getAuthProviderInfo(linkedAccountProviderState.id); const defaultCallbackUrl = `/${SINGLE_TENANT_ORG_DOMAIN}/settings/permission-syncing`; return ( @@ -35,14 +35,14 @@ export function IntegrationProviderCard({
- {integrationProviderState.isLinked? ( + {linkedAccountProviderState.isLinked? (
@@ -50,11 +50,11 @@ export function IntegrationProviderCard({ Connected
- {integrationProviderState.linkedAccountId && ( + {linkedAccountProviderState.linkedAccountId && ( <> - {integrationProviderState.linkedAccountId} + {linkedAccountProviderState.linkedAccountId} )} @@ -67,7 +67,7 @@ export function IntegrationProviderCard({
)} - {integrationProviderState.error && ( + {linkedAccountProviderState.error && (
@@ -80,14 +80,14 @@ export function IntegrationProviderCard({
- {integrationProviderState.isLinked? ( + {linkedAccountProviderState.isLinked? ( ) : ( )} diff --git a/packages/web/src/ee/features/permissionSyncing/components/linkedAccountsSettings.tsx b/packages/web/src/ee/features/permissionSyncing/components/linkedAccountsSettings.tsx index 3b7fff88..8b326bdf 100644 --- a/packages/web/src/ee/features/permissionSyncing/components/linkedAccountsSettings.tsx +++ b/packages/web/src/ee/features/permissionSyncing/components/linkedAccountsSettings.tsx @@ -1,20 +1,20 @@ import { ShieldCheck } from "lucide-react"; -import { getIntegrationProviderStates } from "@/ee/features/permissionSyncing/actions" +import { getLinkedAccountProviderStates } from "@/ee/features/permissionSyncing/actions" import { Card, CardContent } from "@/components/ui/card"; -import { IntegrationProviderCard } from "./integrationProviderCard"; +import { LinkedAccountProviderCard } from "./linkedAccountProviderCard"; import { LogoutEscapeHatch } from "@/app/components/logoutEscapeHatch"; import { isServiceError } from "@/lib/utils"; export async function LinkedAccountsSettings() { - const integrationProviderStates = await getIntegrationProviderStates(); - if (isServiceError(integrationProviderStates)) { + const linkedAccountProviderStates = await getLinkedAccountProviderStates(); + if (isServiceError(linkedAccountProviderStates)) { return

An error occurred

- {typeof integrationProviderStates.message === 'string' - ? integrationProviderStates.message + {typeof linkedAccountProviderStates.message === 'string' + ? linkedAccountProviderStates.message : "A server error occurred while checking your account status. Please try again or contact support."}

@@ -30,7 +30,7 @@ export async function LinkedAccountsSettings() {

- {integrationProviderStates.length === 0 ? ( + {linkedAccountProviderStates.length === 0 ? (
@@ -44,13 +44,13 @@ export async function LinkedAccountsSettings() { ) : (
- {integrationProviderStates + {linkedAccountProviderStates .sort((a, b) => (b.required ? 1 : 0) - (a.required ? 1 : 0)) .map((state) => { return ( - ); })} diff --git a/packages/web/src/ee/features/permissionSyncing/components/providerIcon.tsx b/packages/web/src/ee/features/permissionSyncing/components/providerIcon.tsx index 3a23d1d5..fa439473 100644 --- a/packages/web/src/ee/features/permissionSyncing/components/providerIcon.tsx +++ b/packages/web/src/ee/features/permissionSyncing/components/providerIcon.tsx @@ -25,8 +25,15 @@ const sizeClasses = { } }; +const sizeDimensions = { + sm: { width: 16, height: 16 }, + md: { width: 20, height: 20 }, + lg: { width: 24, height: 24 } +}; + export function ProviderIcon({ icon, displayName, size = "md" }: ProviderIconProps) { const sizes = sizeClasses[size]; + const dimensions = sizeDimensions[size]; if (icon) { return ( @@ -34,6 +41,8 @@ export function ProviderIcon({ icon, displayName, size = "md" }: ProviderIconPro {displayName}
diff --git a/packages/web/src/ee/features/permissionSyncing/components/unlinkButton.tsx b/packages/web/src/ee/features/permissionSyncing/components/unlinkButton.tsx index 9a7a65cb..ebd05ab9 100644 --- a/packages/web/src/ee/features/permissionSyncing/components/unlinkButton.tsx +++ b/packages/web/src/ee/features/permissionSyncing/components/unlinkButton.tsx @@ -3,7 +3,7 @@ import { useState } from "react"; import { Button } from "@/components/ui/button"; import { Unlink, Loader2 } from "lucide-react"; -import { unlinkIntegrationProvider } from "../actions"; +import { unlinkLinkedAccountProvider } from "../actions"; import { isServiceError } from "@/lib/utils"; import { useRouter } from "next/navigation"; import { useToast } from "@/components/hooks/use-toast"; @@ -25,7 +25,7 @@ export const UnlinkButton = ({ provider, providerName }: UnlinkButtonProps) => { setIsUnlinking(true); try { - const result = await unlinkIntegrationProvider(provider); + const result = await unlinkLinkedAccountProvider(provider); if (isServiceError(result)) { toast({ diff --git a/packages/web/src/ee/features/permissionSyncing/tokenRefresh.ts b/packages/web/src/ee/features/permissionSyncing/tokenRefresh.ts index dcb94d2a..f6ebef06 100644 --- a/packages/web/src/ee/features/permissionSyncing/tokenRefresh.ts +++ b/packages/web/src/ee/features/permissionSyncing/tokenRefresh.ts @@ -3,22 +3,14 @@ import { env } from "@/env.mjs"; import { createLogger } from "@sourcebot/logger"; import { getTokenFromConfig } from '@sourcebot/crypto'; import { GitHubIdentityProviderConfig, GitLabIdentityProviderConfig } from "@sourcebot/schemas/v3/index.type"; +import { LinkedAccountTokensMap } from "@/auth" +const { prisma } = await import('@/prisma'); const logger = createLogger('web-ee-token-refresh'); -export type IntegrationToken = { - accessToken: string; - refreshToken: string; - expiresAt: number; - error?: string; -}; - -export type IntegrationTokensMap = Record; - -export async function refreshIntegrationTokens( - currentTokens: IntegrationTokensMap | undefined, - userId: string -): Promise { +export async function refreshLinkedAccountTokens( + currentTokens: LinkedAccountTokensMap | undefined +): Promise { if (!currentTokens) { return {}; } @@ -26,26 +18,40 @@ export async function refreshIntegrationTokens( const now = Math.floor(Date.now() / 1000); const bufferTimeS = 5 * 60; // 5 minutes - const updatedTokens: IntegrationTokensMap = { ...currentTokens }; + const updatedTokens: LinkedAccountTokensMap = { ...currentTokens }; - // Refresh tokens for each integration provider await Promise.all( - Object.entries(currentTokens).map(async ([provider, tokenData]) => { + Object.entries(currentTokens).map(async ([providerAccountId, tokenData]) => { + const provider = tokenData.provider; if (provider !== 'github' && provider !== 'gitlab') { return; } if (tokenData.expiresAt && now >= (tokenData.expiresAt - bufferTimeS)) { try { - logger.info(`Refreshing token for provider: ${provider}`); + logger.info(`Refreshing token for providerAccountId: ${providerAccountId} (${tokenData.provider})`); const refreshedTokens = await refreshOAuthToken( provider, - tokenData.refreshToken, - userId + tokenData.refreshToken ); if (refreshedTokens) { - updatedTokens[provider] = { + await prisma.account.update({ + where: { + provider_providerAccountId: { + provider: provider, + providerAccountId: providerAccountId + } + }, + data: { + access_token: refreshedTokens.accessToken, + refresh_token: refreshedTokens.refreshToken, + expires_at: refreshedTokens.expiresAt, + }, + }); + + updatedTokens[providerAccountId] = { + provider: tokenData.provider, accessToken: refreshedTokens.accessToken, refreshToken: refreshedTokens.refreshToken ?? tokenData.refreshToken, expiresAt: refreshedTokens.expiresAt, @@ -53,14 +59,14 @@ export async function refreshIntegrationTokens( logger.info(`Successfully refreshed token for provider: ${provider}`); } else { logger.error(`Failed to refresh token for provider: ${provider}`); - updatedTokens[provider] = { + updatedTokens[providerAccountId] = { ...tokenData, error: 'RefreshTokenError', }; } } catch (error) { logger.error(`Error refreshing token for provider ${provider}:`, error); - updatedTokens[provider] = { + updatedTokens[providerAccountId] = { ...tokenData, error: 'RefreshTokenError', }; @@ -75,82 +81,83 @@ export async function refreshIntegrationTokens( export async function refreshOAuthToken( provider: string, refreshToken: string, - userId: string ): Promise<{ accessToken: string; refreshToken: string | null; expiresAt: number } | null> { try { const config = await loadConfig(env.CONFIG_PATH); const identityProviders = config?.identityProviders ?? []; - const providerConfig = identityProviders.find(idp => idp.provider === provider); - if (!providerConfig) { + const providerConfigs = identityProviders.filter(idp => idp.provider === provider); + if (providerConfigs.length === 0) { logger.error(`Provider config not found or invalid for: ${provider}`); return null; } - // Get client credentials from config - const integrationProviderConfig = providerConfig as GitHubIdentityProviderConfig | GitLabIdentityProviderConfig - const clientId = await getTokenFromConfig(integrationProviderConfig.clientId); - const clientSecret = await getTokenFromConfig(integrationProviderConfig.clientSecret); - const baseUrl = 'baseUrl' in integrationProviderConfig && integrationProviderConfig.baseUrl - ? await getTokenFromConfig(integrationProviderConfig.baseUrl) - : undefined; + // Loop through all provider configs and return on first successful fetch + // + // The reason we have to do this is because 1) we might have multiple providers of the same type (ex. we're connecting to multiple gitlab instances) and 2) there isn't + // a trivial way to map a provider config to the associated Account object in the DB. The reason the config is involved at all here is because we need the client + // id/secret in order to refresh the token, and that info is in the config. We could in theory bypass this by storing the client id/secret for the provider in the + // Account table but we decided not to do that since these are secret. Instead, we simply try all of the client/id secrets for the associated provider type. This is safe + // to do because only the correct client id/secret will work since we're using a specific refresh token. + for (const providerConfig of providerConfigs) { + try { + // Get client credentials from config + const linkedAccountProviderConfig = providerConfig as GitHubIdentityProviderConfig | GitLabIdentityProviderConfig + const clientId = await getTokenFromConfig(linkedAccountProviderConfig.clientId); + const clientSecret = await getTokenFromConfig(linkedAccountProviderConfig.clientSecret); + const baseUrl = linkedAccountProviderConfig.baseUrl - let url: string; - if (baseUrl) { - url = provider === 'github' - ? `${baseUrl}/login/oauth/access_token` - : `${baseUrl}/oauth/token`; - } else if (provider === 'github') { - url = 'https://github.com/login/oauth/access_token'; - } else if (provider === 'gitlab') { - url = 'https://gitlab.com/oauth/token'; - } else { - logger.error(`Unsupported provider for token refresh: ${provider}`); - return null; + let url: string; + if (baseUrl) { + url = provider === 'github' + ? `${baseUrl}/login/oauth/access_token` + : `${baseUrl}/oauth/token`; + } else if (provider === 'github') { + url = 'https://github.com/login/oauth/access_token'; + } else if (provider === 'gitlab') { + url = 'https://gitlab.com/oauth/token'; + } else { + logger.error(`Unsupported provider for token refresh: ${provider}`); + continue; + } + + const response = await fetch(url, { + method: 'POST', + headers: { + 'Content-Type': 'application/x-www-form-urlencoded', + 'Accept': 'application/json', + }, + body: new URLSearchParams({ + client_id: clientId, + client_secret: clientSecret, + grant_type: 'refresh_token', + refresh_token: refreshToken, + }), + }); + + if (!response.ok) { + const errorText = await response.text(); + logger.debug(`Failed to refresh ${provider} token with config: ${response.status} ${errorText}`); + continue; + } + + const data = await response.json(); + + const result = { + accessToken: data.access_token, + refreshToken: data.refresh_token ?? null, + expiresAt: data.expires_in ? Math.floor(Date.now() / 1000) + data.expires_in : 0, + }; + + return result; + } catch (configError) { + logger.debug(`Error trying provider config for ${provider}:`, configError); + continue; + } } - const response = await fetch(url, { - method: 'POST', - headers: { - 'Content-Type': 'application/x-www-form-urlencoded', - 'Accept': 'application/json', - }, - body: new URLSearchParams({ - client_id: clientId, - client_secret: clientSecret, - grant_type: 'refresh_token', - refresh_token: refreshToken, - }), - }); - - if (!response.ok) { - const errorText = await response.text(); - logger.error(`Failed to refresh ${provider} token: ${response.status} ${errorText}`); - return null; - } - - const data = await response.json(); - - const result = { - accessToken: data.access_token, - refreshToken: data.refresh_token ?? null, - expiresAt: data.expires_in ? Math.floor(Date.now() / 1000) + data.expires_in : 0, - }; - - const { prisma } = await import('@/prisma'); - await prisma.account.updateMany({ - where: { - userId: userId, - provider: provider, - }, - data: { - access_token: result.accessToken, - refresh_token: result.refreshToken, - expires_at: result.expiresAt, - }, - }); - - return result; + logger.error(`All provider configs failed for: ${provider}`); + return null; } catch (error) { logger.error(`Error refreshing ${provider} token:`, error); return null; diff --git a/packages/web/src/ee/features/permissionSyncing/types.ts b/packages/web/src/ee/features/permissionSyncing/types.ts index 3bdb87c7..3fdc16e2 100644 --- a/packages/web/src/ee/features/permissionSyncing/types.ts +++ b/packages/web/src/ee/features/permissionSyncing/types.ts @@ -1,4 +1,4 @@ -export type IntegrationIdentityProviderState = { +export type LinkedAccountProviderState = { id: string; required: boolean; isLinked: boolean; diff --git a/packages/web/src/ee/features/sso/sso.ts b/packages/web/src/ee/features/sso/sso.ts index 2e925f18..9a5153ff 100644 --- a/packages/web/src/ee/features/sso/sso.ts +++ b/packages/web/src/ee/features/sso/sso.ts @@ -19,6 +19,8 @@ import { GCPIAPIdentityProviderConfig, GitHubIdentityProviderConfig, GitLabIdent const logger = createLogger('web-sso'); +const GITHUB_CLOUD_HOSTNAME = "github.com" + export const getEEIdentityProviders = async (): Promise => { const providers: IdentityProvider[] = []; @@ -30,89 +32,93 @@ export const getEEIdentityProviders = async (): Promise => { const providerConfig = identityProvider as GitHubIdentityProviderConfig; const clientId = await getTokenFromConfig(providerConfig.clientId); const clientSecret = await getTokenFromConfig(providerConfig.clientSecret); - const baseUrl = providerConfig.baseUrl ? await getTokenFromConfig(providerConfig.baseUrl) : undefined; - providers.push({ provider: createGitHubProvider(clientId, clientSecret, baseUrl), purpose: providerConfig.purpose, required: providerConfig.required ?? false}); + const baseUrl = providerConfig.baseUrl; + providers.push({ provider: createGitHubProvider(clientId, clientSecret, baseUrl), purpose: providerConfig.purpose, required: providerConfig.accountLinkingRequired ?? false}); } if (identityProvider.provider === "gitlab") { const providerConfig = identityProvider as GitLabIdentityProviderConfig; const clientId = await getTokenFromConfig(providerConfig.clientId); const clientSecret = await getTokenFromConfig(providerConfig.clientSecret); - const baseUrl = providerConfig.baseUrl ? await getTokenFromConfig(providerConfig.baseUrl) : undefined; - providers.push({ provider: createGitLabProvider(clientId, clientSecret, baseUrl), purpose: providerConfig.purpose, required: providerConfig.required ?? false}); + const baseUrl = providerConfig.baseUrl; + providers.push({ provider: createGitLabProvider(clientId, clientSecret, baseUrl), purpose: providerConfig.purpose, required: providerConfig.accountLinkingRequired ?? false}); } if (identityProvider.provider === "google") { const providerConfig = identityProvider as GoogleIdentityProviderConfig; const clientId = await getTokenFromConfig(providerConfig.clientId); const clientSecret = await getTokenFromConfig(providerConfig.clientSecret); - providers.push({ provider: createGoogleProvider(clientId, clientSecret), purpose: "sso"}); + providers.push({ provider: createGoogleProvider(clientId, clientSecret), purpose: providerConfig.purpose}); } if (identityProvider.provider === "okta") { const providerConfig = identityProvider as OktaIdentityProviderConfig; const clientId = await getTokenFromConfig(providerConfig.clientId); const clientSecret = await getTokenFromConfig(providerConfig.clientSecret); const issuer = await getTokenFromConfig(providerConfig.issuer); - providers.push({ provider: createOktaProvider(clientId, clientSecret, issuer), purpose: "sso"}); + providers.push({ provider: createOktaProvider(clientId, clientSecret, issuer), purpose: providerConfig.purpose}); } if (identityProvider.provider === "keycloak") { const providerConfig = identityProvider as KeycloakIdentityProviderConfig; const clientId = await getTokenFromConfig(providerConfig.clientId); const clientSecret = await getTokenFromConfig(providerConfig.clientSecret); const issuer = await getTokenFromConfig(providerConfig.issuer); - providers.push({ provider: createKeycloakProvider(clientId, clientSecret, issuer), purpose: "sso" }); + providers.push({ provider: createKeycloakProvider(clientId, clientSecret, issuer), purpose: providerConfig.purpose }); } if (identityProvider.provider === "microsoft-entra-id") { const providerConfig = identityProvider as MicrosoftEntraIDIdentityProviderConfig; const clientId = await getTokenFromConfig(providerConfig.clientId); const clientSecret = await getTokenFromConfig(providerConfig.clientSecret); const issuer = await getTokenFromConfig(providerConfig.issuer); - providers.push({ provider: createMicrosoftEntraIDProvider(clientId, clientSecret, issuer), purpose: "sso" }); + providers.push({ provider: createMicrosoftEntraIDProvider(clientId, clientSecret, issuer), purpose: providerConfig.purpose }); } if (identityProvider.provider === "gcp-iap") { const providerConfig = identityProvider as GCPIAPIdentityProviderConfig; const audience = await getTokenFromConfig(providerConfig.audience); - providers.push({ provider: createGCPIAPProvider(audience), purpose: "sso" }); + providers.push({ provider: createGCPIAPProvider(audience), purpose: providerConfig.purpose }); } } - // @deprecate - if (env.AUTH_EE_GITHUB_CLIENT_ID && env.AUTH_EE_GITHUB_CLIENT_SECRET) { - providers.push({ provider: createGitHubProvider(env.AUTH_EE_GITHUB_CLIENT_ID, env.AUTH_EE_GITHUB_CLIENT_SECRET, env.AUTH_EE_GITHUB_BASE_URL), purpose: "sso" }); + // @deprecate in favor of defining identity providers throught the identityProvider object in the config file. This was done to allow for more control over + // which identity providers are defined and their purpose. We've left this logic here to support backwards compat with deployments that expect these env vars, + // but this logic will be removed in the future + // We only go through this path if no identityProviders are defined in the config to prevent accidental duplication of providers + if (identityProviders.length == 0) { + if (env.AUTH_EE_GITHUB_CLIENT_ID && env.AUTH_EE_GITHUB_CLIENT_SECRET) { + providers.push({ provider: createGitHubProvider(env.AUTH_EE_GITHUB_CLIENT_ID, env.AUTH_EE_GITHUB_CLIENT_SECRET, env.AUTH_EE_GITHUB_BASE_URL), purpose: "sso" }); + } + + if (env.AUTH_EE_GITLAB_CLIENT_ID && env.AUTH_EE_GITLAB_CLIENT_SECRET) { + providers.push({ provider: createGitLabProvider(env.AUTH_EE_GITLAB_CLIENT_ID, env.AUTH_EE_GITLAB_CLIENT_SECRET, env.AUTH_EE_GITLAB_BASE_URL), purpose: "sso" }); + } + + if (env.AUTH_EE_GOOGLE_CLIENT_ID && env.AUTH_EE_GOOGLE_CLIENT_SECRET) { + providers.push({ provider: createGoogleProvider(env.AUTH_EE_GOOGLE_CLIENT_ID, env.AUTH_EE_GOOGLE_CLIENT_SECRET), purpose: "sso" }); + } + + if (env.AUTH_EE_OKTA_CLIENT_ID && env.AUTH_EE_OKTA_CLIENT_SECRET && env.AUTH_EE_OKTA_ISSUER) { + providers.push({ provider: createOktaProvider(env.AUTH_EE_OKTA_CLIENT_ID, env.AUTH_EE_OKTA_CLIENT_SECRET, env.AUTH_EE_OKTA_ISSUER), purpose: "sso" }); + } + + if (env.AUTH_EE_KEYCLOAK_CLIENT_ID && env.AUTH_EE_KEYCLOAK_CLIENT_SECRET && env.AUTH_EE_KEYCLOAK_ISSUER) { + providers.push({ provider: createKeycloakProvider(env.AUTH_EE_KEYCLOAK_CLIENT_ID, env.AUTH_EE_KEYCLOAK_CLIENT_SECRET, env.AUTH_EE_KEYCLOAK_ISSUER), purpose: "sso" }); + } + + if (env.AUTH_EE_MICROSOFT_ENTRA_ID_CLIENT_ID && env.AUTH_EE_MICROSOFT_ENTRA_ID_CLIENT_SECRET && env.AUTH_EE_MICROSOFT_ENTRA_ID_ISSUER) { + providers.push({ provider: createMicrosoftEntraIDProvider(env.AUTH_EE_MICROSOFT_ENTRA_ID_CLIENT_ID, env.AUTH_EE_MICROSOFT_ENTRA_ID_CLIENT_SECRET, env.AUTH_EE_MICROSOFT_ENTRA_ID_ISSUER), purpose: "sso" }); + } + + if (env.AUTH_EE_GCP_IAP_ENABLED && env.AUTH_EE_GCP_IAP_AUDIENCE) { + providers.push({ provider: createGCPIAPProvider(env.AUTH_EE_GCP_IAP_AUDIENCE), purpose: "sso" }); + } } - - if (env.AUTH_EE_GITLAB_CLIENT_ID && env.AUTH_EE_GITLAB_CLIENT_SECRET) { - providers.push({ provider: createGitLabProvider(env.AUTH_EE_GITLAB_CLIENT_ID, env.AUTH_EE_GITLAB_CLIENT_SECRET, env.AUTH_EE_GITLAB_BASE_URL), purpose: "sso" }); - } - - if (env.AUTH_EE_GOOGLE_CLIENT_ID && env.AUTH_EE_GOOGLE_CLIENT_SECRET) { - providers.push({ provider: createGoogleProvider(env.AUTH_EE_GOOGLE_CLIENT_ID, env.AUTH_EE_GOOGLE_CLIENT_SECRET), purpose: "sso" }); - } - - if (env.AUTH_EE_OKTA_CLIENT_ID && env.AUTH_EE_OKTA_CLIENT_SECRET && env.AUTH_EE_OKTA_ISSUER) { - providers.push({ provider: createOktaProvider(env.AUTH_EE_OKTA_CLIENT_ID, env.AUTH_EE_OKTA_CLIENT_SECRET, env.AUTH_EE_OKTA_ISSUER), purpose: "sso" }); - } - - if (env.AUTH_EE_KEYCLOAK_CLIENT_ID && env.AUTH_EE_KEYCLOAK_CLIENT_SECRET && env.AUTH_EE_KEYCLOAK_ISSUER) { - providers.push({ provider: createKeycloakProvider(env.AUTH_EE_KEYCLOAK_CLIENT_ID, env.AUTH_EE_KEYCLOAK_CLIENT_SECRET, env.AUTH_EE_KEYCLOAK_ISSUER), purpose: "sso" }); - } - - if (env.AUTH_EE_MICROSOFT_ENTRA_ID_CLIENT_ID && env.AUTH_EE_MICROSOFT_ENTRA_ID_CLIENT_SECRET && env.AUTH_EE_MICROSOFT_ENTRA_ID_ISSUER) { - providers.push({ provider: createMicrosoftEntraIDProvider(env.AUTH_EE_MICROSOFT_ENTRA_ID_CLIENT_ID, env.AUTH_EE_MICROSOFT_ENTRA_ID_CLIENT_SECRET, env.AUTH_EE_MICROSOFT_ENTRA_ID_ISSUER), purpose: "sso" }); - } - - if (env.AUTH_EE_GCP_IAP_ENABLED && env.AUTH_EE_GCP_IAP_AUDIENCE) { - providers.push({ provider: createGCPIAPProvider(env.AUTH_EE_GCP_IAP_AUDIENCE), purpose: "sso" }); - } - + return providers; } const createGitHubProvider = (clientId: string, clientSecret: string, baseUrl?: string): Provider => { + const hostname = baseUrl ? new URL(baseUrl).hostname : GITHUB_CLOUD_HOSTNAME return GitHub({ clientId: clientId, clientSecret: clientSecret, - enterprise: { - baseUrl: baseUrl, - }, + ...(hostname === GITHUB_CLOUD_HOSTNAME ? { enterprise: { baseUrl: baseUrl } } : {}), // if this is set the provider expects GHE so we need this check authorization: { params: { scope: [ diff --git a/packages/web/src/features/chat/actions.ts b/packages/web/src/features/chat/actions.ts index 349c8bcc..19ec9abd 100644 --- a/packages/web/src/features/chat/actions.ts +++ b/packages/web/src/features/chat/actions.ts @@ -22,6 +22,7 @@ import { fromNodeProviderChain } from '@aws-sdk/credential-providers'; import { createOpenRouter } from '@openrouter/ai-sdk-provider'; import { getTokenFromConfig } from "@sourcebot/crypto"; import { ChatVisibility, OrgRole, Prisma } from "@sourcebot/db"; +import { createLogger } from "@sourcebot/logger"; import { LanguageModel } from "@sourcebot/schemas/v3/languageModel.type"; import { Token } from "@sourcebot/schemas/v3/shared.type"; import { generateText, JSONValue, extractReasoningMiddleware, wrapLanguageModel } from "ai"; @@ -31,6 +32,8 @@ import { StatusCodes } from "http-status-codes"; import path from 'path'; import { LanguageModelInfo, SBChatMessage } from "./types"; +const logger = createLogger('chat-actions'); + export const createChat = async (domain: string) => sew(() => withAuth((userId) => withOrgMembership(userId, domain, async ({ org }) => { @@ -360,11 +363,15 @@ export const getConfiguredLanguageModelsInfo = async (): Promise => { - const config = await loadConfig(env.CONFIG_PATH); - return config.models ?? []; + try { + const config = await loadConfig(env.CONFIG_PATH); + return config.models ?? []; + } catch (error) { + logger.error('Failed to load language model configuration', error); + return []; + } } - export const _getAISDKLanguageModelAndOptions = async (config: LanguageModel): Promise<{ model: AISDKLanguageModelV2, providerOptions?: Record>, diff --git a/packages/web/src/lib/identityProviders.ts b/packages/web/src/lib/identityProviders.ts index d35cd2db..dff90187 100644 --- a/packages/web/src/lib/identityProviders.ts +++ b/packages/web/src/lib/identityProviders.ts @@ -3,7 +3,7 @@ import { getProviders } from "@/auth"; export interface IdentityProviderMetadata { id: string; name: string; - purpose: "sso" | "integration"; + purpose: "sso" | "account_linking"; required: boolean; } diff --git a/schemas/v3/identityProvider.json b/schemas/v3/identityProvider.json index 3eeeef96..de2e0d11 100644 --- a/schemas/v3/identityProvider.json +++ b/schemas/v3/identityProvider.json @@ -4,12 +4,13 @@ "definitions": { "GitHubIdentityProviderConfig": { "type": "object", + "additionalProperties": false, "properties": { "provider": { "const": "github" }, "purpose": { - "enum": ["sso", "integration"] + "enum": ["sso", "account_linking"] }, "clientId": { "$ref": "./shared.json#/definitions/Token" @@ -18,9 +19,17 @@ "$ref": "./shared.json#/definitions/Token" }, "baseUrl": { - "$ref": "./shared.json#/definitions/Token" + "type": "string", + "format": "url", + "default": "https://github.com", + "description": "The URL of the GitHub host. Defaults to https://github.com", + "examples": [ + "https://github.com", + "https://github.example.com" + ], + "pattern": "^https?:\\/\\/[^\\s/$.?#].[^\\s]*$" }, - "required": { + "accountLinkingRequired": { "type": "boolean", "default": false } @@ -29,12 +38,13 @@ }, "GitLabIdentityProviderConfig": { "type": "object", + "additionalProperties": false, "properties": { "provider": { "const": "gitlab" }, "purpose": { - "enum": ["sso", "integration"] + "enum": ["sso", "account_linking"] }, "clientId": { "$ref": "./shared.json#/definitions/Token" @@ -43,21 +53,33 @@ "$ref": "./shared.json#/definitions/Token" }, "baseUrl": { - "$ref": "./shared.json#/definitions/Token" + "type": "string", + "format": "url", + "default": "https://gitlab.com", + "description": "The URL of the GitLab host. Defaults to https://gitlab.com", + "examples": [ + "https://gitlab.com", + "https://gitlab.example.com" + ], + "pattern": "^https?:\\/\\/[^\\s/$.?#].[^\\s]*$" }, - "required": { + "accountLinkingRequired": { "type": "boolean", - "default": false + "default": false } }, "required": ["provider", "purpose", "clientId", "clientSecret"] }, "GoogleIdentityProviderConfig": { "type": "object", + "additionalProperties": false, "properties": { "provider": { "const": "google" }, + "purpose": { + "const": "sso" + }, "clientId": { "$ref": "./shared.json#/definitions/Token" }, @@ -65,14 +87,18 @@ "$ref": "./shared.json#/definitions/Token" } }, - "required": ["provider", "clientId", "clientSecret"] + "required": ["provider", "purpose", "clientId", "clientSecret"] }, "OktaIdentityProviderConfig": { "type": "object", + "additionalProperties": false, "properties": { "provider": { "const": "okta" }, + "purpose": { + "const": "sso" + }, "clientId": { "$ref": "./shared.json#/definitions/Token" }, @@ -83,14 +109,18 @@ "$ref": "./shared.json#/definitions/Token" } }, - "required": ["provider", "clientId", "clientSecret", "issuer"] + "required": ["provider", "purpose", "clientId", "clientSecret", "issuer"] }, "KeycloakIdentityProviderConfig": { "type": "object", + "additionalProperties": false, "properties": { "provider": { "const": "keycloak" }, + "purpose": { + "const": "sso" + }, "clientId": { "$ref": "./shared.json#/definitions/Token" }, @@ -101,14 +131,18 @@ "$ref": "./shared.json#/definitions/Token" } }, - "required": ["provider", "clientId", "clientSecret", "issuer"] + "required": ["provider", "purpose", "clientId", "clientSecret", "issuer"] }, "MicrosoftEntraIDIdentityProviderConfig": { "type": "object", + "additionalProperties": false, "properties": { "provider": { "const": "microsoft-entra-id" }, + "purpose": { + "const": "sso" + }, "clientId": { "$ref": "./shared.json#/definitions/Token" }, @@ -119,19 +153,23 @@ "$ref": "./shared.json#/definitions/Token" } }, - "required": ["provider", "clientId", "clientSecret", "issuer"] + "required": ["provider", "purpose", "clientId", "clientSecret", "issuer"] }, "GCPIAPIdentityProviderConfig": { "type": "object", + "additionalProperties": false, "properties": { "provider": { "const": "gcp-iap" }, + "purpose": { + "const": "sso" + }, "audience": { "$ref": "./shared.json#/definitions/Token" } }, - "required": ["provider", "audience"] + "required": ["provider", "purpose", "audience"] } }, "oneOf": [