<a id="keycloak-open-source-red-hat-sso"></a>

# Keycloak - Open Source Red Hat SSO

<a id="backend-class"></a>

## Backend class

For Django, add this class path to `AUTHENTICATION_BACKENDS`. For other
integrations, use the same class path in the framework-specific backend
setting.

| Backend name   | Class path                                     |
|----------------|------------------------------------------------|
| `keycloak`     | `social_core.backends.keycloak.KeycloakOAuth2` |

Keycloak is an open source IAM and SSO system.

<a id="idp-setup"></a>

## IdP Setup

To configure Keycloak:

1. Log into your Keycloak Admin Console and select your Realm
2. Navigate to **Clients** > **Create**
3. Configure the client:
   * **Client ID**: Choose a meaningful name (e.g., `django-app`)
   * **Client Protocol**: `openid-connect`
   * **Access Type**: `confidential`
   * **Valid Redirect URIs**: `https://your-domain.com/complete/keycloak/`
4. Save and go to the **Credentials** tab to get the **Client Secret**
5. Under **Fine Grain OpenID Connect Configuration** (found in the client’s Settings or Advanced Settings tab; location may vary depending on Keycloak version), set:
   * **User Info Signed Response Algorithm**: `RS256`
   * **Request Object Signature Algorithm**: `RS256`
6. Get the active public key from **Realm Settings** > **Keys** > **RS256**
7. Create an **Audience Mapper** for the client to ensure its **Client ID** is
   included in the access token’s `aud` claim. In recent Keycloak versions,
   navigate to **Client scopes** > `<client-id>-dedicated` > **Add mapper** >
   **Audience**, select the client under **Included Client Audience**, and enable
   **Add to access token**.
8. Note the **Authorization URL** and **Token URL** from the Realm OpenID Endpoint Configuration

<a id="application-configuration"></a>

## Application Configuration

Add Keycloak to your `AUTHENTICATION_BACKENDS`:

```default
AUTHENTICATION_BACKENDS = (
    ...
    'social_core.backends.keycloak.KeycloakOAuth2',
    'django.contrib.auth.backends.ModelBackend',
)
```

Configure with values from your Keycloak client:

```default
SOCIAL_AUTH_KEYCLOAK_KEY = 'test-django-oidc'
SOCIAL_AUTH_KEYCLOAK_SECRET = 'a7a41-245e-...'
SOCIAL_AUTH_KEYCLOAK_PUBLIC_KEY = \
    'MIIBIjANBxxxdSD'
SOCIAL_AUTH_KEYCLOAK_AUTHORIZATION_URL = \
    'https://iam.example.com/auth/realms/voxcloud-staff/protocol/openid-connect/auth'
SOCIAL_AUTH_KEYCLOAK_ACCESS_TOKEN_URL = \
    'https://iam.example.com/auth/realms/voxcloud-staff/protocol/openid-connect/token'
```

<a id="audience-validation"></a>

## Audience validation

`SOCIAL_AUTH_KEYCLOAK_KEY` is the OAuth **Client ID**. The backend also uses
this value as the expected audience when validating the access token. Therefore,
the exact value configured in `SOCIAL_AUTH_KEYCLOAK_KEY` must be present in the
token’s `aud` claim.

The `azp` (authorized party) claim does not satisfy audience validation. For
example, with `SOCIAL_AUTH_KEYCLOAK_KEY = 'test-django-oidc'`, the access token
should contain claims similar to:

```default
{
    "azp": "test-django-oidc",
    "aud": ["test-django-oidc"]
}
```

If the Client ID is missing from `aud`, configure the Audience Mapper described
above. Otherwise, authentication fails with an audience validation error even
when `azp` identifies the correct client.

<a id="signing-key-rotation"></a>

## Signing key rotation

The Keycloak backend verifies access tokens with the single static key configured
in `SOCIAL_AUTH_KEYCLOAK_PUBLIC_KEY`. It does not use the JWT header’s `kid`
(key ID) to select a key and does not fetch keys from Keycloak’s JWKS endpoint.

The configured key must therefore be the active RS256 signing key from the same
realm that issues the token. After a realm signing-key rotation, update
`SOCIAL_AUTH_KEYCLOAK_PUBLIC_KEY`; otherwise, authentication fails with a
signature verification error. The token’s `kid` can be compared with the key
ID shown under **Realm Settings** > **Keys** to diagnose a mismatch.

For automatic signing-key discovery and rotation, consider using the generic
[OIDC (OpenID Connect)](oidc.html.md) backend instead. It uses OpenID Connect discovery, selects keys from
the provider’s JWKS by `kid`, and refreshes the JWKS when a new key appears.

<a id="user-id-configuration"></a>

## User ID Configuration

The default behavior is to associate users via the `sub` (subject) field from the
JWT token. However, you can configure which field to use as the unique user identifier
by setting:

```default
SOCIAL_AUTH_KEYCLOAK_ID_KEY = 'email'
```

This can be useful if you want to use email, username, or another field as the unique
identifier instead of the `sub` field.

Associations created by older social-core releases used the normalized
`preferred_username` value and migrate to `sub` on the next successful
authentication.

#### WARNING
Usernames and email addresses can change or be reassigned. Selecting one as
`ID_KEY` can allow a different provider account to match a stale local
association.

See the [Configurable User ID Key](../configuration/settings.html#configurable-user-id-key) documentation for more information about this feature.

<a id="external-memberships"></a>

## External memberships

See [External groups](../groups.html.md) for opt-in extraction, group-based login restrictions, and
local group synchronization. No separate extraction pipeline step is needed.
