<a id="configure-sso-using-oidc"></a>

# Configure SSO for Confluent Control Center using OIDC on Confluent Platform

You can configure single sign-on (SSO) using [OpenID Connect (OIDC)](https://openid.net), built on top of the [OAuth 2.0 protocol](https://oauth.net). This configuration enables users to sign on with a
single credential from an identity provider (IdP) and to authenticate seamlessly
to Confluent Control Center and to Confluent Cloud.

Authentication with OIDC on Confluent Platform is flexible in that you can integrate with two
different identity providers. For example, your environment can integrate with
one identity provider for authenticating using SSO flows and another, separate
provider, for client credential flows.

Considerations:

- To use SSO with Control Center your installation must use Confluent Platform version 7.5 or later.
- SSO for Control Center does not support identity provider (IdP) refresh
  token rotation. If your IdP has this feature enabled, you must
  turn it off in your IdP settings to ensure successful token renewal.
  For more information, see
  [Refresh token rotation is not supported](troubleshoot.md#troubleshoot-sso-oidc-refresh-token-rotation).

<a id="configure-claims"></a>

## Identity token claims and Confluent Platform checks

Identity (ID) tokens are issued by an IdP and contain claims that verify the
identity of the user and when and how the user authenticated. When an ID token
leaves the IdP and arrives at Confluent Platform, the platform checks and validates the token.

#### IMPORTANT
Your IdP must return an ID token for SSO authentication to
function properly. Ensure that your IdP is configured to include
ID tokens in the authentication response.

The following is an example of what a claims token might contain:

```JSON
{
   "sub": "1234567890",
   "jti": "44b8ebfe-8e20-4a88-a945-8d7a6b988ecf",
   "iat": 1596001234,
   "iss": "https://your-oidc-issuer.com",
   "exp": 1696001234,
   "nonce": "n-0S6_WzA2Mj",
   "email": "user@example.com",
   "preferred_username": "user"
}
```

It’s important to understand that different IdP token claims might or might
not match this example. The token your IdP provides *might be different*.
Configuring Confluent Platform token validation correctly can avoid conflicts between a
provider’s token contents and the content Confluent Platform expects. The following lists
the ID token claims that are optional and those that are required:

`sub`
: A subject identifier claim. This claim is required for Confluent Platform services but
  optional for Apache Kafka® listeners. The claim value can be a user ID, but it can
  also refer to other subjects. To specify that a different claim act as the
  `sub` value, configure the `confluent.oidc.idp.sub.claim.name` property.

`jti`
: An optional token identifier claim. In versions 7.7.2, 7.8.2, and later, you
  can configure whether Confluent Platform checks for the `jti` claim. By default, the
  check is disabled. You can enable the check by setting
  `confluent.oidc.idp.jti.validation.enabled` to `true`, though Confluent Platform does
  not use the claim in token validation.

`iat`
: An optional claim for the time at which the JSON Web Token (JWT) was issued.
  In versions 7.7.2, 7.8.2, and later, you can configure whether Confluent Platform checks
  for the `iat` claim. By default, the check is disabled. You can enable the
  check by setting `confluent.oidc.idp.iat.validation.enabled` to `true`,
  though Confluent Platform does not use the claim in token validation.

`iss`
: A required claim that identifies the server that issued this token.

`exp`
: A required claim that specifies when the token is expired and no longer
  valid.

<a id="steps-to-configure-sso"></a>

## Configuration prerequisites and process

Before you begin, make sure you have met the following prerequisites:

* An OIDC-compliant identity provider (IdP), with optional multi-factor
  authentication (MFA). SAML is not supported.
* Confluent Platform version 7.5.0 or later. All components should be upgraded.
* Confirm role-based access control (RBAC) is enabled on your cluster.

### Decide between manual or automated configuration

You can configure SSO for Confluent Control Center using OIDC manually by completing the
procedures on this page. These procedures provide detailed configurations for
the Okta, Keycloak, and Microsoft Entra ID (Azure) IdPs.

You can also automate the configuration of SSO. Confluent recommends using
Confluent Ansible and Confluent for Kubernetes (CFK) to automate the configuration of SSO for
Confluent Control Center on Confluent Platform. For more information, see [Configure single sign-on
authentication for Control Center](https://docs.confluent.io/ansible/current/ansible-authenticate.html#ansible-c3-sso)
in the Confluent Ansible documentation, or [Configure single sign-on
authentication for Confluent Control Center](https://docs.confluent.io/operator/current/co-authenticate-cp.html#co-authenticate-c3-sso)
in the CFK documentation.

## 1 - Establish trust between the IdP and Confluent Platform

Use the following procedure to ensure that you configure your IdP correctly.
Under each step, select the tab that corresponds to your IdP for details.

1. Create an OIDC client application configured with an authorization code grant
   type in your IdP.

   The following tabs contain provider-specific configuration instructions:

   ### Okta

   In the Okta documentation, complete [Create OIDC app integrations](https://help.okta.com/en-us/content/topics/apps/apps_app_integration_wizard_oidc.htm).

   ### Keycloak

   In the Keycloak documentation, complete [Managing OpenID Connect
   clients](https://www.keycloak.org/docs/latest/server_admin/index.html#oidc-clients).

   ### Microsoft Entra ID

   In the Microsoft Azure documentation, complete [Quickstart: Register an
   application with the Microsoft identity platform](https://docs.microsoft.com/en-us/azure/active-directory/develop/quickstart-register-app).

   #### WARNING
   Microsoft Entra ID users must create a
   separate registered application for OAuth. Do not combine OAuth
   together with SAML in a single application configuration. Such a
   dual configuration can lead to issues with single sign-on (SSO) and
   other authentication flows.
2. Add a redirect (callback) URL to Confluent Control Center on Confluent Platform in the client application.

   The URL should follow this format:
   ```html
   https://<c3-hostname>:<c3-port>/api/metadata/security/1.0/oidc/authorization-code/callback
   ```
3. Enable identity tokens.

   Identity tokens are enabled by default when you create an OIDC application in
   your IdP.

   ### Okta

   Creating the authorization server by default enables identity (ID)
   tokens. For more information, see [ID tokens](https://developer.okta.com/docs/reference/api/oidc/#id-token).

   ### Keycloak

   For more information, see [Server Administration Guide](https://www.keycloak.org/docs/latest/server_admin/) in the
   Keycloak documentation.

   ### Microsoft Entra ID

   For more information on enabling identity tokens, see [ID tokens in
   the Microsoft identity platform](https://learn.microsoft.com/en-us/azure/active-directory/develop/id-tokens)
   in the Azure documentation.
4. Enable refresh tokens.

   ### Okta

   Check the **Refresh Token** option in the **Grant type** section of
   the **Applications** page.

   ### Keycloak

   Refresh tokens are enabled by default. For more information, see
   [Authorization Code Flow](https://www.keycloak.org/docs/latest/server_admin/#_oidc-auth-flows-authorization)
   in the Keycloak documentation.

   ### Microsoft Entra ID

   See [Refresh tokens](https://docs.microsoft.com/en-us/azure/active-directory/develop/refresh-tokens)
   as documented in the Microsoft Azure documentation.
5. Include group claims in the ID tokens. Following are some details and links
   to help you get started.

   ### Okta

   1. Navigate to your authorization server under **Security** > **API**.
   2. Go to **Claims** and configure a claim for groups.
   3. [Add a Groups claim for the org authorization server](https://developer.okta.com/docs/guides/customize-tokens-groups-claim/main/#request-a-token-that-contains-the-custom-claim).

   ### Keycloak

   Configure a new **Group Membership mapper**. Make sure to have **Full
   group path** enabled.

   ### Microsoft Entra ID

   To add groups claims, navigate to **App registrations** > **Token
   configuration** and follow these instructions [Configuring group
   claims and app roles in tokens](https://learn.microsoft.com/en-us/security/zero-trust/develop/configure-tokens-group-claims-app-roles)
   in the Microsoft Azure documentation.

   If a user belongs to more groups than Azure can include in a token, or
   if the `groups` claim is empty, Confluent Control Center receives no groups for
   authorization. For more information, see
   [Microsoft Entra ID group claim behavior and overage](../oauth-oidc/configure-oauth-jwt.md#auth-entra-id-group-claims).
6. Assign users to the client application in your IdP.

   If you are using groups to control access to Confluent Control Center, you assign users to the
   groups in the following group configuration steps.
7. Get the IdP endpoints.

   You can use the OpenID provider configuration response to get the identity
   provider endpoints required to fetch, authorize, and verify tokens.
   * Token endpoint URL (`token_endpoint`)
   * Authorization endpoint URL (`authorization_endpoint`)
   * JSON Web Key Set (JWKS) URL (`jwks_uri`)
   * Issuer URL (`issuer`)

   Use the following OIDC metadata discovery URI for your IdP to get these IdP
   endpoints and save them for later use:

   ### Okta

   ```html
   https://<okta-domain>/oauth2/default/.well-known/openid-configuration
   ```

   For more information, see [/.well-known/openid-configuration [Okta
   documentation]](https://developer.okta.com/docs/reference/api/oidc/#well-known-openid-configuration).

   ### Keycloak

   ```html
   https://<keycloak-domain>/realms/<realm-name>/.well-known/openid-configuration
   ```

   For more information, see [Using OpenID Connect to secure applications
   and services](https://www.keycloak.org/securing-apps/oidc-layers).

   ### Microsoft Entra ID

   ```html
   https://login.microsoftonline.com/<tenant-id>/v2.0/.well-known/openid-configuration
   ```

   For more information, see [OpenID Connect authentication with Azure
   Active Directory](https://learn.microsoft.com/en-us/azure/active-directory/architecture/auth-oidc#implement-oidc-with-azure-ad)
   and [OpenID Connect on the Microsoft identity platform](https://learn.microsoft.com/en-us/azure/active-directory/develop/v2-protocols-oidc)
8. Get the client credential details

   From the client application you created in the IdP, get the following
   client credentials and save them for later use:
   * Client ID (`client_id`)
   * Client secret (`client_secret`)
9. Configure IdP client credentials and endpoints.

   On each Confluent Server broker node, add or update the following parameters in the Confluent Platform
   broker configuration file using the endpoints obtained in the **Get the IdP
   endpoints** step.
   ```properties
   confluent.oidc.idp.issuer=<issuer>
   confluent.oidc.idp.jwks.endpoint.uri=<jwks_uri>
   confluent.oidc.idp.authorize.base.endpoint.uri=<authorization_endpoint>
   confluent.oidc.idp.token.base.endpoint.uri=<token_endpoint>
   confluent.oidc.idp.client.id=<client-id>
   confluent.oidc.idp.client.secret=<client-secret>
   ```
10. Configure groups in your IdP.

    In Confluent Platform you can use group authorization to control user access to any
    resource, for example, a Confluent Platform cluster or a topic. To support this behavior,
    you must create groups and assign users to them in your IdP.
11. Add the following `confluent.oidc.idp.groups.claim.name` parameter to the
    Confluent Platform broker configuration file on each Confluent Server broker.
    ```properties
    confluent.oidc.idp.groups.claim.name=groups
    ```

    The `confluent.oidc.idp.groups.claim.name` is required and must match the
    value of the groups claim configured in your IdP. The default value is
    `groups`, but it should match the claim value on your IdP setup. If the
    values do not match, problems occur during authorization.
12. For KRaft clusters, you must add the following parameter to each Confluent Platform
    controller configuration file for the listener that is used for inter-broker
    communication.
    ```properties
    listener.name.${listenerName}.principal.builder.class=io.confluent.kafka.security.authenticator.OAuthKafkaPrincipalBuilder
    ```

    The `io.confluent.kafka.security.authenticator.OAuthKafkaPrincipalBuilder`
    parameter enables administration requests to process the group extraction
    logic. Without this parameter, group-based authorization does not work.

    In a Confluent Platform cluster, the `DefaultPrincipalBuilder` creates a
    `KafkaPrincipal` that does not include groups. This becomes evident during
    interactions between the Confluent Control Center and Confluent Server brokers. For example, when using the
    `KafkaAdminClient` to retrieve topics, the `DefaultPrincipalBuilder`
    produces the `KafkaPrincipal`.

    In contrast, the `OAuthKafkaPrincipalBuilder` employs an `OAuthBearer`
    token to generate a `KafkaPrincipal`. This builder also incorporates
    groups, allowing them to be passed to the authorizer where necessary. By
    using the `OAuthKafkaPrincipalBuilder` the `KafkaPrincipal` can include
    groups.

## 2 - Enable SSO using OIDC on Confluent Control Center and Metadata Service (MDS)

Use this procedure to enable SSO for Confluent Control Center using OIDC.

1. On each Confluent Server broker, add the following parameter to the Confluent Platform broker
   configuration file:
   ```properties
   confluent.metadata.server.sso.mode=oidc
   ```
2. On the Control Center node, add the following parameter to the Confluent Control Center
   properties file (typically, `control-center-dev.properties` or
   `control-center-production.properties`):
   ```properties
   confluent.controlcenter.auth.sso.mode=oidc
   ```
3. Specify the `groups` and `sub` claims on each Confluent Server broker to the Confluent Platform
   configuration file.
   ```properties
   # Field for groups and sub in JWT
   confluent.oidc.idp.groups.claim.name=groups
   confluent.oidc.idp.sub.claim.name=sub
   ```

   The `groups` claim name identifies the groups to which the principal
   belongs. The default value is `groups`. Other common values are
   `organization` and `team`.

   The `sub` (subject) claim name identifies the principal that is the subject
   of the JWT. The default value is `sub`. Other common values are `email`
   and `employee_id`.
4. Configure claim checks for your identity token as required by your IdP.

   If you skipped it, make sure you read [Identity token claims and Confluent Platform checks](#configure-claims) earlier on
   this page to understand your configuration options.

   As an example, the following shows how to configure just the `jti` claim
   appropriately for your IdP:

   ### Okta

   Specify the claim name by setting the `jti` in each Confluent Platform server
   configuration file.
   ```properties
   confluent.oidc.idp.jti.validation.enabled=true
   ```

   Confluent Platform checks for the claim in your token.

   ### Keycloak

   Specify the claim name by setting the `jti` in each Confluent Platform server
   configuration file.
   ```properties
   confluent.oidc.idp.jti.validation.enabled=true
   ```

   Confluent Platform checks for the claim in your token.

   ### Entra ID

   Microsoft AD and Azure AD issue JWT tokens that typically do not contain
   a `jti` value. If you are running Confluent Platform version 7.7.2, 7.8.2, or later,
   use the default value in your server properties file:
   ```properties
   confluent.oidc.idp.jti.validation.enabled=false
   ```
5. Add the IdP TLS certificates to the MDS truststore

   If you have configured the MDS truststore using
   `confluent.metadata.ssl.truststore.*` properties, you must add the identity
   provider TLS certificates to the truststore.
6. Add the IdP root CA certificate to the MDS truststore. For example, if you
   use the `keytool` command, run the following command:
   ```bash
   keytool -import -alias <alias> -file <idp-certificate> -keystore
   <mds-truststore> -storepass <mds-truststore-password> -noprompt
   ```

   * The alias for the certificate in the truststore (`<alias>`)
   * The IdP TLS root CA certificate (`<idp-certificate>`)
   * The MDS truststore (`<mds-truststore>`)
   * The password for the MDS truststore (`<mds-truststore-password>`)

   #### WARNING
   If you are using Entra ID, you might need to import multiple root CA
   certificates. For information about this, see the [Microsoft certificate
   authority details](https://learn.microsoft.com/en-us/azure/security/fundamentals/azure-CA-details?tabs=root-and-subordinate-cas-list#what-changed).

## 3 - Customize security and usability

Depending on your requirements, you can optionally configure the session
management experience on Confluent Platform to customize additional security and usability.
Customizations to the following two parameters are optional. For the majority of
use cases, the default values should work.

1. Verify that refresh tokens are enabled.

   Refresh tokens enable session renewal and automatically refresh your claims.
   RBAC requires refresh tokens to be enabled for OIDC.

   The `confluent.oidc.idp.refresh.token.enabled` setting controls this
   feature and defaults to `true`. Keep this setting at `true` for RBAC
   to function properly.
   ```properties
   confluent.oidc.idp.refresh.token.enabled=true
   ```

   When `confluent.oidc.idp.refresh.token.enabled=false` is set, the bearer
   token issued during login cannot renew itself beyond the duration set by the
   `confluent.controlcenter.auth.bearer.token.max.lifetime.ms` value. When the
   lifetime is exceeded, the user must reauthenticate. The best practice is to
   set the `confluent.controlcenter.auth.bearer.token.max.lifetime.ms` to a
   value less than 24 hours and greater than or equal to the value of the MDS
   `confluent.metadata.server.token.max.lifetime.ms` property.
2. Set the expiration period for JWT session tokens

   To change the JWT session token expiration period, add the following
   configuration parameter and set the duration, in milliseconds. The value
   here impacts the time to enforce reauthentication. The default value is
   `900000` (15 minutes). This value should be less than the value for the
   maximum session timeout.
   ```properties
   confluent.oidc.session.token.expiry.ms=900000
   ```
3. Set the maximum session timeout period

   To change the maximum session timeout, after which the session cannot be
   extended, add the following configuration parameter and set the duration, in
   milliseconds. The default value is `21600000` (6 hours).
   ```properties
   confluent.oidc.session.max.timeout.ms=21600000
   ```

## 4 - Restart the Confluent Server brokers

After you have configured SSO for Confluent Control Center using OIDC, do the following:

1. Restart the Confluent Server brokers and Control Center for the changes to take effect.

   You might experience downtime on the Confluent Platform cluster while the cluster upgrades
   to enable SSO for Confluent Control Center.
2. Sign in to Control Center using the SSO credentials from your IdP.

## 5 - Test your configuration

Test your configuration to ensure that it works correctly.

1. Edit your SSO client application configuration.
2. On the identity provider (IdP), temporarily add the following URL to the
   client’s redirect URIs section:
   ```none
   https://oidcdebugger.com/debug
   ```
3. Paste this template URL into a text editor:
   ```none
   <idpAuthorizeEndpoint>?client_id=<clientId>&redirect_uri=https%3A%2F%2Foidcdebugger.com%2Fdebug&scope=openid offline_access&response_type=code&response_mode=form_post&state=cmx0vexznl8&nonce=6su4cyjmr7v
   ```
4. Edit the template to replace these sections of the URL:

   | Parameter                     | Value                                                                                                                                     |
   |-------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------|
   | `<idpAuthorizeEndpoint>`      | IdP authorization endpoint URL (`authorization_endpoint`)                                                                                 |
   | `clientId`                    | The client application’s ID.                                                                                                              |
   | `scope=openid offline_access` | Replace the `offline_access` value with the access level your client<br/>application requests from the IdP at the `idpAuthorizeEndpoint`. |

   For example:
   ```none
   http://keycloak:8080/realms/cp/protocol/openid-connect/auth?client_id=c3_sso_login&redirect_uri=https%3A%2F%2Foidcdebugger.com%2Fdebug&scope=openid
   offline_access&response_type=code&response_mode=form_post&state=cmx0vexznl8&nonce=6su4cyjmr7v
   ```
5. Copy and paste the completed URL from the editor into your browser address
   bar.

   The browser returns a page with the authorization code.
6. Keep this browser page with the code open.
7. Paste the following `curl` URL template into a text editor:
   ```none
   curl --request POST \
   --url <idpTokenEndpoint> \
   --header 'Content-Type: application/x-www-form-urlencoded' \
   --data-urlencode 'grant_type=authorization_code' \
   --data-urlencode 'client_id=<clientId>' \
   --data-urlencode 'client_secret=<clientSecret>' \
   --data-urlencode 'redirect_uri=https://oidcdebugger.com/debug' \
   --data-urlencode 'code=<authorization_code>'
   ```
8. Edit the `curl` template making sure to replace these values:

   | Parameter              | Value                                            |
   |------------------------|--------------------------------------------------|
   | `<idpTokenEndpoint>`   | IdP token endpoint URL (`token_endpoint`)        |
   | `clientId`             | The client application’s ID.                     |
   | `<clientSecret>`       | The client application’s secret.                 |
   | `<authorization_code>` | The authorization code in the open browser page. |

   For example, this POST request to the Keycloak IdP:
   ```bash
   curl --request POST \
      --url http://keycloak:8080/realms/cp/protocol/openid-connect/token \
      --header 'Content-Type: application/x-www-form-urlencoded' \
      --data-urlencode 'grant_type=authorization_code' \
      --data-urlencode 'client_id=c3_sso_login' \
      --data-urlencode 'client_secret=c3_sso_login_secret' \
      --data-urlencode 'redirect_uri=https://oidcdebugger.com/debug' \
      --data-urlencode
      'code=356c1086-1e10-4bd6-9a2f-7041154c00be.69ac7395-5dc8-4036-82ab-d8e90472ef36.852d46eb-dff0-4834-826f-acf9de533ea6'
   ```
9. Copy your updated `curl` command from the editor and enter it into your
   terminal.

   If your POST succeeds, the server returns a JSON object containing at least
   an access token, for example:
   ```bash
   {
      "access_token": "eyJhbGciOiJSUzI1NiIsInR5cCIgOiA... (truncated)",
      "token_type": "Bearer",
      "expires_in": 300,
      "refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXV... (truncated)",
      "refresh_expires_in": 1800,
      "id_token": "eyJhbGciOiJSUzI1NiIsInR5cCIgOiA... (truncated)",
      ...
   }
   ```
