diff --git a/backend/src/ee/services/permission/permission-fns.ts b/backend/src/ee/services/permission/permission-fns.ts index 78b8ad8f2..eda19c215 100644 --- a/backend/src/ee/services/permission/permission-fns.ts +++ b/backend/src/ee/services/permission/permission-fns.ts @@ -5,9 +5,13 @@ import { ActorAuthMethod, AuthMethod } from "@app/services/auth/auth-type"; function isAuthMethodSaml(actorAuthMethod: ActorAuthMethod) { if (!actorAuthMethod) return false; - return [AuthMethod.AZURE_SAML, AuthMethod.OKTA_SAML, AuthMethod.JUMPCLOUD_SAML, AuthMethod.GOOGLE_SAML].includes( - actorAuthMethod - ); + return [ + AuthMethod.AZURE_SAML, + AuthMethod.OKTA_SAML, + AuthMethod.JUMPCLOUD_SAML, + AuthMethod.GOOGLE_SAML, + AuthMethod.KEYCLOAK_SAML + ].includes(actorAuthMethod); } function validateOrgSAML(actorAuthMethod: ActorAuthMethod, isSamlEnforced: TOrganizations["authEnforced"]) { diff --git a/backend/src/ee/services/saml-config/saml-config-service.ts b/backend/src/ee/services/saml-config/saml-config-service.ts index dc9728957..f88182e61 100644 --- a/backend/src/ee/services/saml-config/saml-config-service.ts +++ b/backend/src/ee/services/saml-config/saml-config-service.ts @@ -319,6 +319,11 @@ export const samlConfigServiceFactory = ({ const organization = await orgDAL.findOrgById(orgId); if (!organization) throw new BadRequestError({ message: "Org not found" }); + // TODO(dangtony98): remove this after aliases update + if (authProvider === AuthMethod.KEYCLOAK_SAML && appCfg.LICENSE_SERVER_KEY) { + throw new BadRequestError({ message: "Keycloak SAML is not yet available on Infisical Cloud" }); + } + if (user) { await userDAL.transaction(async (tx) => { const [orgMembership] = await orgDAL.findMembership( diff --git a/backend/src/ee/services/saml-config/saml-config-types.ts b/backend/src/ee/services/saml-config/saml-config-types.ts index 9aedf5d19..df7694920 100644 --- a/backend/src/ee/services/saml-config/saml-config-types.ts +++ b/backend/src/ee/services/saml-config/saml-config-types.ts @@ -5,7 +5,8 @@ export enum SamlProviders { OKTA_SAML = "okta-saml", AZURE_SAML = "azure-saml", JUMPCLOUD_SAML = "jumpcloud-saml", - GOOGLE_SAML = "google-saml" + GOOGLE_SAML = "google-saml", + KEYCLOAK_SAML = "keycloak-saml" } export type TCreateSamlCfgDTO = { diff --git a/backend/src/services/auth/auth-type.ts b/backend/src/services/auth/auth-type.ts index a3c53658c..8e7b92253 100644 --- a/backend/src/services/auth/auth-type.ts +++ b/backend/src/services/auth/auth-type.ts @@ -7,6 +7,7 @@ export enum AuthMethod { AZURE_SAML = "azure-saml", JUMPCLOUD_SAML = "jumpcloud-saml", GOOGLE_SAML = "google-saml", + KEYCLOAK_SAML = "keycloak-saml", LDAP = "ldap" } diff --git a/docs/documentation/platform/sso/keycloak-saml.mdx b/docs/documentation/platform/sso/keycloak-saml.mdx new file mode 100644 index 000000000..981739711 --- /dev/null +++ b/docs/documentation/platform/sso/keycloak-saml.mdx @@ -0,0 +1,139 @@ +--- +title: "Keycloak SAML" +description: "Learn how to configure Keycloak SAML for Infisical SSO." +--- + + + Keycloak SAML SSO is a paid feature. + + If you're using Infisical Cloud, then it is available under the **Pro Tier**. If you're self-hosting Infisical, + then you should contact sales@infisical.com to purchase an enterprise license to use it. + + + + + In Infisical, head to your Organization Settings > Authentication > SAML SSO Configuration and select **Manage**. + + ![Keycloak SAML organization security section](../../../images/sso/keycloak/org-security-section.png) + + Next, copy the **Valid redirect URI** and **SP Entity ID** to use when configuring the Keycloak SAML application. + + ![Keycloak SAML initial configuration](../../../images/sso/keycloak/init-config.png) + + + 2.1. In your realm, navigate to the **Clients** tab and click **Create client** to create a new client application. + + ![SAML keycloak list of clients](../../../images/sso/keycloak/clients-list.png) + + + You don’t typically need to make a realm dedicated to Infisical. We recommend adding Infisical as a client to your primary realm. + + + In the General Settings step, set **Client type** to **SAML**, the **Client ID** field to `https://app.infisical.com`, and the **Name** field to a friendly name like **Infisical**. + + ![SAML keycloak create client general settings](../../../images/sso/keycloak/create-client-general-settings.png) + + + If you’re self-hosting Infisical, then you will want to replace https://app.infisical.com with your own domain. + + + Next, in the Login Settings step, set both the **Home URL** field and **Valid redirect URIs** field to the **Valid redirect URI** from step 1 and press **Save**. + + ![SAML keycloak create client login settings](../../../images/sso/keycloak/create-client-login-settings.png) + + 2.2. Once you've created the client, under its **Settings** tab, make sure to set the following values: + + - Under **SAML Capabilities**: + - Name ID format: email (or username). + - Force name ID format: On. + - Force POST binding: On. + - Include AuthnStatement: On. + - Under **Signature and Encryption**: + - Sign documents: On. + - Sign assertions: On. + - Signature algorithm: RSA_SHA256. + + ![SAML keycloak client SAML capabilities](../../../images/sso/keycloak/client-saml-capabilities.png) + + ![SAML keycloak client signature encryption](../../../images/sso/keycloak/client-signature-encryption.png) + + 2.3. Next, navigate to the **Client scopes** tab select the client's dedicated scope. + + ![SAML keycloak client scopes list](../../../images/sso/keycloak/client-scopes-list.png) + + Next click **Add predefined mapper**. + + ![SAML keycloak client mappers empty](../../../images/sso/keycloak/client-mappers-empty.png) + + Select the **X500 email**, **X500 givenName**, and **X500 surname** attributes and click **Add**. + + ![SAML keycloak client mappers predefined](../../../images/sso/keycloak/client-mappers-predefined.png) + + Now click on the **X500 email** mapper and set the **SAML Attribute Name** field to **email**. + + ![SAML keycloak client mappers email](../../../images/sso/keycloak/client-mappers-email.png) + + Repeat the same for **X500 givenName** and **X500 surname** mappers, setting the **SAML Attribute Name** field to **firstName** and **lastName** respectively. + + Next, back in the client scope's **Mappers**, click **Add mapper** and select **by configuration**. + + ![SAML keycloak client mappers by configuration](../../../images/sso/keycloak/client-mappers-by-configuration.png) + + Select **User Property**. + + ![SAML keycloak client mappers user property](../../../images/sso/keycloak/client-mappers-user-property.png) + + Set the the **Name** field to **Username**, the **Property** field to **username**, and the **SAML Attribtue Name** to **username**. + + ![SAML keycloak client mappers username](../../../images/sso/keycloak/client-mappers-username.png) + + Repeat the same for the `id` attribute, setting the **Name** field to **ID**, the **Property** field to **id**, and the **SAML Attribute Name** to **id**. + + ![SAML keycloak client mappers id](../../../images/sso/keycloak/client-mappers-id.png) + + Once you've completed the above steps, the list of mappers should look like this: + + ![SAML keycloak client mappers completed](../../../images/sso/keycloak/client-mappers-completed.png) + + + Back in Keycloak, navigate to Configure > Realm settings > General tab > Endpoints > SAML 2.0 Identity Provider Metadata and copy the IDP URL. This should appear in various places and take the form: `https://keycloak-mysite.com/realms/myrealm/protocol/saml`. + + ![SAML keycloak realm SAML metadata](../../../images/sso/keycloak/realm-saml-metadata.png) + + Also, in the **Keys** tab, locate the RS256 key and copy the certificate to use when finishing configuring Keycloak SAML in Infisical. + + ![SAML keycloak realm settings keys](../../../images/sso/keycloak/realm-settings-keys.png) + + + Back in Infisical, set **IDP URL** and **Certificate** to the items from step 3. Also, set the **Client ID** to the `https://app.infisical.com`. + + Once you've done that, press **Update** to complete the required configuration. + + ![SAML Okta paste values into Infisical](../../../images/sso/keycloak/idp-values.png) + + + Enabling SAML SSO allows members in your organization to log into Infisical via Keycloak. + + ![SAML keycloak enable SAML](../../../images/sso/keycloak/enable-saml.png) + + + Enforcing SAML SSO ensures that members in your organization can only access Infisical + by logging into the organization via Keycloak. + + To enforce SAML SSO, you're required to test out the SAML connection by successfully authenticating at least one Keycloak user with Infisical; + Once you've completed this requirement, you can toggle the **Enforce SAML SSO** button to enforce SAML SSO. + + + We recommend ensuring that your account is provisioned the application in Keycloak + prior to enforcing SAML SSO to prevent any unintended issues. + + + + + + If you're configuring SAML SSO on a self-hosted instance of Infisical, make sure to + set the `AUTH_SECRET` and `SITE_URL` environment variable for it to work: + + - `AUTH_SECRET`: A secret key used for signing and verifying JWT. This can be a random 32-byte base64 string generated with `openssl rand -base64 32`. + - `SITE_URL`: The URL of your self-hosted instance of Infisical - should be an absolute URL including the protocol (e.g. https://app.infisical.com) + \ No newline at end of file diff --git a/docs/documentation/platform/sso/overview.mdx b/docs/documentation/platform/sso/overview.mdx index 42c2d986b..6064f26e8 100644 --- a/docs/documentation/platform/sso/overview.mdx +++ b/docs/documentation/platform/sso/overview.mdx @@ -15,11 +15,11 @@ description: "Learn how to log in to Infisical via SSO protocols." You can configure your organization in Infisical to have members authenticate with the platform via protocols like [SAML 2.0](https://en.wikipedia.org/wiki/SAML_2.0). To note, Infisical's SSO implementation decouples the **authentication** and **decryption** steps – which implies that no -Identitiy Provider can have access to the decryption key needed to decrypt your secrets (this also implies that Infisical requires entering the user's Master Password on top of authenticating with SSO). +Identity Provider can have access to the decryption key needed to decrypt your secrets (this also implies that Infisical requires entering the user's Master Password on top of authenticating with SSO). ## Identity providers -Infisical these and many other identity providers: +Infisical supports these and many other identity providers: - [Google SSO](/documentation/platform/sso/google) - [GitHub SSO](/documentation/platform/sso/github) @@ -27,6 +27,7 @@ Infisical these and many other identity providers: - [Okta SAML](/documentation/platform/sso/okta) - [Azure SAML](/documentation/platform/sso/azure) - [JumpCloud SAML](/documentation/platform/sso/jumpcloud) +- [Keycloak SAML](/documentation/platform/sso/keycloak-saml) - [Google SAML](/documentation/platform/sso/google-saml) If your required identity provider is not shown in the list above, please reach out to [team@infisical.com](mailto:team@infisical.com) for assistance. diff --git a/docs/images/sso/keycloak/client-mappers-by-configuration.png b/docs/images/sso/keycloak/client-mappers-by-configuration.png new file mode 100644 index 000000000..9bebb422e Binary files /dev/null and b/docs/images/sso/keycloak/client-mappers-by-configuration.png differ diff --git a/docs/images/sso/keycloak/client-mappers-completed.png b/docs/images/sso/keycloak/client-mappers-completed.png new file mode 100644 index 000000000..38fb82006 Binary files /dev/null and b/docs/images/sso/keycloak/client-mappers-completed.png differ diff --git a/docs/images/sso/keycloak/client-mappers-email.png b/docs/images/sso/keycloak/client-mappers-email.png new file mode 100644 index 000000000..e1a369bab Binary files /dev/null and b/docs/images/sso/keycloak/client-mappers-email.png differ diff --git a/docs/images/sso/keycloak/client-mappers-empty.png b/docs/images/sso/keycloak/client-mappers-empty.png new file mode 100644 index 000000000..01ec1d3e6 Binary files /dev/null and b/docs/images/sso/keycloak/client-mappers-empty.png differ diff --git a/docs/images/sso/keycloak/client-mappers-id.png b/docs/images/sso/keycloak/client-mappers-id.png new file mode 100644 index 000000000..a45638b87 Binary files /dev/null and b/docs/images/sso/keycloak/client-mappers-id.png differ diff --git a/docs/images/sso/keycloak/client-mappers-predefined.png b/docs/images/sso/keycloak/client-mappers-predefined.png new file mode 100644 index 000000000..750d600b7 Binary files /dev/null and b/docs/images/sso/keycloak/client-mappers-predefined.png differ diff --git a/docs/images/sso/keycloak/client-mappers-user-property.png b/docs/images/sso/keycloak/client-mappers-user-property.png new file mode 100644 index 000000000..c854f9521 Binary files /dev/null and b/docs/images/sso/keycloak/client-mappers-user-property.png differ diff --git a/docs/images/sso/keycloak/client-mappers-username.png b/docs/images/sso/keycloak/client-mappers-username.png new file mode 100644 index 000000000..ff2a8fc39 Binary files /dev/null and b/docs/images/sso/keycloak/client-mappers-username.png differ diff --git a/docs/images/sso/keycloak/client-saml-capabilities.png b/docs/images/sso/keycloak/client-saml-capabilities.png new file mode 100644 index 000000000..a4383628a Binary files /dev/null and b/docs/images/sso/keycloak/client-saml-capabilities.png differ diff --git a/docs/images/sso/keycloak/client-scopes-list.png b/docs/images/sso/keycloak/client-scopes-list.png new file mode 100644 index 000000000..16f908af4 Binary files /dev/null and b/docs/images/sso/keycloak/client-scopes-list.png differ diff --git a/docs/images/sso/keycloak/client-signature-encryption.png b/docs/images/sso/keycloak/client-signature-encryption.png new file mode 100644 index 000000000..b03b07a75 Binary files /dev/null and b/docs/images/sso/keycloak/client-signature-encryption.png differ diff --git a/docs/images/sso/keycloak/clients-list.png b/docs/images/sso/keycloak/clients-list.png new file mode 100644 index 000000000..ad05b2004 Binary files /dev/null and b/docs/images/sso/keycloak/clients-list.png differ diff --git a/docs/images/sso/keycloak/create-client-general-settings.png b/docs/images/sso/keycloak/create-client-general-settings.png new file mode 100644 index 000000000..866a92070 Binary files /dev/null and b/docs/images/sso/keycloak/create-client-general-settings.png differ diff --git a/docs/images/sso/keycloak/create-client-login-settings.png b/docs/images/sso/keycloak/create-client-login-settings.png new file mode 100644 index 000000000..6fa8b4ce4 Binary files /dev/null and b/docs/images/sso/keycloak/create-client-login-settings.png differ diff --git a/docs/images/sso/keycloak/enable-saml.png b/docs/images/sso/keycloak/enable-saml.png new file mode 100644 index 000000000..f66af968a Binary files /dev/null and b/docs/images/sso/keycloak/enable-saml.png differ diff --git a/docs/images/sso/keycloak/idp-values.png b/docs/images/sso/keycloak/idp-values.png new file mode 100644 index 000000000..9de14f23b Binary files /dev/null and b/docs/images/sso/keycloak/idp-values.png differ diff --git a/docs/images/sso/keycloak/init-config.png b/docs/images/sso/keycloak/init-config.png new file mode 100644 index 000000000..d500bb86e Binary files /dev/null and b/docs/images/sso/keycloak/init-config.png differ diff --git a/docs/images/sso/keycloak/org-security-section.png b/docs/images/sso/keycloak/org-security-section.png new file mode 100644 index 000000000..bbbfb2d42 Binary files /dev/null and b/docs/images/sso/keycloak/org-security-section.png differ diff --git a/docs/images/sso/keycloak/realm-saml-metadata.png b/docs/images/sso/keycloak/realm-saml-metadata.png new file mode 100644 index 000000000..c5ea5d497 Binary files /dev/null and b/docs/images/sso/keycloak/realm-saml-metadata.png differ diff --git a/docs/images/sso/keycloak/realm-settings-keys.png b/docs/images/sso/keycloak/realm-settings-keys.png new file mode 100644 index 000000000..3add94290 Binary files /dev/null and b/docs/images/sso/keycloak/realm-settings-keys.png differ diff --git a/docs/mint.json b/docs/mint.json index 9d9759f0e..a41f23f9d 100644 --- a/docs/mint.json +++ b/docs/mint.json @@ -161,6 +161,7 @@ "documentation/platform/sso/okta", "documentation/platform/sso/azure", "documentation/platform/sso/jumpcloud", + "documentation/platform/sso/keycloak-saml", "documentation/platform/sso/google-saml" ] }, diff --git a/frontend/src/hooks/api/users/types.ts b/frontend/src/hooks/api/users/types.ts index 23f4dda4c..845030e9f 100644 --- a/frontend/src/hooks/api/users/types.ts +++ b/frontend/src/hooks/api/users/types.ts @@ -8,6 +8,7 @@ export enum AuthMethod { OKTA_SAML = "okta-saml", AZURE_SAML = "azure-saml", JUMPCLOUD_SAML = "jumpcloud-saml", + KEYCLOAK_SAML = "keycloak-saml", LDAP = "ldap" } diff --git a/frontend/src/views/Settings/OrgSettingsPage/components/OrgAuthTab/SSOModal.tsx b/frontend/src/views/Settings/OrgSettingsPage/components/OrgAuthTab/SSOModal.tsx index 4cad75ebd..23f5fab88 100644 --- a/frontend/src/views/Settings/OrgSettingsPage/components/OrgAuthTab/SSOModal.tsx +++ b/frontend/src/views/Settings/OrgSettingsPage/components/OrgAuthTab/SSOModal.tsx @@ -22,6 +22,7 @@ enum AuthProvider { OKTA_SAML = "okta-saml", AZURE_SAML = "azure-saml", JUMPCLOUD_SAML = "jumpcloud-saml", + KEYCLOAK_SAML = "keycloak-saml", GOOGLE_SAML = "google-saml" } @@ -29,6 +30,7 @@ const ssoAuthProviders = [ { label: "Okta SAML", value: AuthProvider.OKTA_SAML }, { label: "Azure SAML", value: AuthProvider.AZURE_SAML }, { label: "JumpCloud SAML", value: AuthProvider.JUMPCLOUD_SAML }, + { label: "Keycloak SAML", value: AuthProvider.KEYCLOAK_SAML }, { label: "Google SAML", value: AuthProvider.GOOGLE_SAML } ]; @@ -142,6 +144,15 @@ export const SSOModal = ({ popUp, handlePopUpClose, handlePopUpToggle }: Props) issuer: "IdP Entity ID", issuerPlaceholder: "xxx" }; + case AuthProvider.KEYCLOAK_SAML: + return { + acsUrl: "Valid redirect URI", + entityId: "SP Entity ID", + entryPoint: "IDP URL", + entryPointPlaceholder: "https://keycloak.mysite.com/realms/myrealm/protocol/saml", + issuer: "Client ID", + issuerPlaceholder: window.origin + }; case AuthProvider.GOOGLE_SAML: return { acsUrl: "ACS URL",