---
title: "Keycloak Integration"
description: "This guide demonstrates how to integrate the OIDC Bridge with Keycloak, enabling users to log in with Verifiable Credentials."
stack: "Enterprise Stack (commercial) — version 0.21.0"
stack_version: "0.21.0"
stack_comparison: https://docs.walt.id/community-vs-enterprise.md
canonical_url: https://docs.walt.id/enterprise-stack/services/oidc-bridge-service/iam-integrations/keycloak
generated: 2026-07-20
---
# Keycloak Integration

This guide demonstrates how to integrate the OIDC Bridge with Keycloak, enabling users to log in with Verifiable Credentials.

## Overview

Keycloak will treat the OIDC Bridge as an external OIDC Identity Provider. When users click "Login with Verifiable Credential," Keycloak redirects them to the OIDC Bridge, which handles credential presentation and returns OIDC tokens.

## Prerequisites

- OIDC Bridge service created, together with its KMS and Verifier2 dependencies (see [Setup](https://docs.walt.id/enterprise-stack/services/oidc-bridge-service/setup.md)).
- **The bridge's KMS contains at least one key.** The bridge signs ID tokens with the first key in its attached KMS; if the KMS is empty, the `jwks` endpoint returns no keys and token issuance fails. Generate one with the [KMS Create Key](https://docs.walt.id/enterprise-stack/services/key-management-service/local.md) endpoint.
- Keycloak instance running, with admin access.
- **Keycloak can reach the bridge's issuer URL.** Keycloak fetches the discovery document and exchanges the code server-to-server, so the bridge hostname must resolve from wherever Keycloak runs. If Keycloak runs in Docker, add a host mapping (e.g. `extra_hosts`) so the bridge hostname resolves to the host from inside the container.

## Step 1: Create the OIDC Bridge with a Keycloak Client

Create the bridge with a client entry for Keycloak. Each enabled flow includes its own `verificationSetup` (the credential request), so the service works as soon as it is created.

**Option: CURL**

Endpoint: `/v1/{target}/resource-api/services/create` | [API Reference](https://enterprise.sandbox.walt.id/swagger/index.html#/Service%20%7C%20Resource/post_v1__target__resource_api_services_create)

**Example Request**

```bash
curl -X 'POST' \
  'https://{orgID}.enterprise-sandbox.waltid.dev/v1/{org}.{tenant}.oidc-bridge/resource-api/services/create' \
  -H 'Authorization: Bearer {yourToken}' \
  -H 'Content-Type: application/json' \
  -d '{
  "type": "oidc-bridge",
  "issuerUrl": "https://enterprise.example.com",
  "clients": {
    "keycloak": {
      "clientId": "keycloak",
      "clientSecret": "keycloak-client-secret",
      "redirectUris": [
        "https://keycloak.example.com/realms/{realm}/broker/oidc-bridge/endpoint"
      ],
      "allowedScopes": ["openid", "profile", "email"]
    }
  },
  "defaultClaimMappings": [
    { "oidcClaim": "sub", "credentialPath": "$[\"org.iso.18013.5.1\"][\"document_number\"]", "transform": "NONE" },
    { "oidcClaim": "given_name", "credentialPath": "$[\"org.iso.18013.5.1\"][\"given_name\"]", "transform": "NONE" },
    { "oidcClaim": "family_name", "credentialPath": "$[\"org.iso.18013.5.1\"][\"family_name\"]", "transform": "NONE" },
    { "oidcClaim": "birthdate", "credentialPath": "$[\"org.iso.18013.5.1\"][\"birth_date\"]", "transform": "NONE" }
  ],
  "flows": {
    "qr": {
      "enabled": true,
      "buttonLabel": "Scan QR Code",
      "verificationSetup": {
        "flow_type": "cross_device",
        "core_flow": {
          "dcql_query": {
            "credentials": [
              {
                "id": "mdl",
                "format": "mso_mdoc",
                "meta": { "doctype_value": "org.iso.18013.5.1.mDL" },
                "claims": [
                  { "path": ["org.iso.18013.5.1", "given_name"] },
                  { "path": ["org.iso.18013.5.1", "family_name"] },
                  { "path": ["org.iso.18013.5.1", "birth_date"] },
                  { "path": ["org.iso.18013.5.1", "document_number"] }
                ]
              }
            ]
          }
        }
      }
    },
    "dc_api": {
      "enabled": true,
      "buttonLabel": "Browser Wallet",
      "verificationSetup": {
        "flow_type": "cross_device",
        "core_flow": {
          "dcql_query": {
            "credentials": [
              {
                "id": "mdl",
                "format": "mso_mdoc",
                "meta": { "doctype_value": "org.iso.18013.5.1.mDL" },
                "claims": [
                  { "path": ["org.iso.18013.5.1", "given_name"] },
                  { "path": ["org.iso.18013.5.1", "family_name"] },
                  { "path": ["org.iso.18013.5.1", "birth_date"] },
                  { "path": ["org.iso.18013.5.1", "document_number"] }
                ]
              }
            ]
          }
        }
      }
    }
  },
  "dependencies": [
    "{org}.{tenant}.kms",
    "{org}.{tenant}.verifier2"
  ]
}'
```

**info:**

**The redirect URI must exactly match Keycloak's broker endpoint** for the provider alias you configure in Step 2:
`https://{keycloak-host}/realms/{realm-name}/broker/{provider-alias}/endpoint`
This example uses the alias `oidc-bridge`.

## Step 2: Configure Keycloak Identity Provider

### 2.1 Access Keycloak Admin Console

1. Navigate to: `https://keycloak.example.com/admin`
2. Log in with admin credentials
3. Select your realm

### 2.2 Add the Identity Provider

1. Go to **Identity Providers** in the left menu.
2. Click **Add provider** → **OpenID Connect v1.0**.
3. Fill in the add form:

| Field | Value |
|-------|-------|
| **Alias** | `oidc-bridge` (or your preferred alias — it must match the redirect URI you allowed in [Step 1](#step-1-create-the-oidc-bridge-with-a-keycloak-client)) |
| **Display name** | `Login with Verifiable Credential` |
| **Use discovery endpoint** | On |
| **Discovery endpoint** | `https://enterprise.example.com/v1/{org}.{tenant}.oidc-bridge/oidc-bridge-api/.well-known/openid-configuration` |
| **Client authentication** | `Client secret sent in the request body` |
| **Client ID** | `keycloak` |
| **Client Secret** | `keycloak-client-secret` |

When the discovery endpoint validates (green check), Keycloak fetches the bridge's metadata and auto-fills the Authorization URL, Token URL, JWKS URL, and Issuer.

4. Click **Add**.

**info:**

**PKCE and Scopes can't be set while adding the provider.** On the Add screen the **Use PKCE** toggle is disabled and there is no PKCE Method or Scopes field — Keycloak only exposes them on the provider's **Settings** page. Configure them in the next step.

### 2.3 Enable PKCE and Scopes

Clicking **Add** opens the new provider's **Settings** page. Set the following, then click **Save**:

| Field | Value | Location on the Settings page |
|-------|-------|-------------------------------|
| **Use PKCE** | On | *OpenID Connect settings* |
| **PKCE Method** | `S256` | appears only after **Use PKCE** is switched On |
| **Scopes** | `openid profile email` | *OpenID Connect settings* → expand **Advanced** |

The **Redirect URI** shown at the top of this page (`https://{keycloak-host}/realms/{realm}/broker/{alias}/endpoint`) must exactly match one of the bridge client's `redirectUris` from Step 1 — with the alias `oidc-bridge` it already does.

### 2.4 Configure Claim Mappers

The claim mappings in Step 1 emit `sub`, `given_name`, `family_name`, and `birthdate`. Keycloak uses `sub` as the federated user identity automatically, so you add mappers for the remaining claims. `given_name` and `family_name` map onto Keycloak's built-in profile fields; `birthdate` has no built-in field, so it becomes a **custom attribute**:

1. Go to **Identity Providers** → **oidc-bridge** → **Mappers**
2. Click **Add mapper**

Create these mappers:

#### Given Name Mapper
- **Name:** `given-name-mapper`
- **Mapper Type:** `Attribute Importer`
- **Claim:** `given_name`
- **User Attribute Name:** `firstName`

#### Family Name Mapper
- **Name:** `family-name-mapper`
- **Mapper Type:** `Attribute Importer`
- **Claim:** `family_name`
- **User Attribute Name:** `lastName`

#### Birthdate Mapper (custom attribute)
- **Name:** `birthdate-mapper`
- **Mapper Type:** `Attribute Importer`
- **Claim:** `birthdate`
- **User Attribute Name:** `birthdate`

Because `birthdate` is not a built-in Keycloak field, it lands as a custom attribute on the user's **Attributes** tab.

**Keycloak 24+ drops unmanaged attributes by default.** Custom attributes (like `birthdate`) that aren't defined in the realm's user profile are silently discarded — the mapper runs, but the attribute never appears on the user. To keep them, do one of the following:

- **Enable unmanaged attributes:** *Realm settings → General → Unmanaged Attributes* → set to `Enabled` (or `Enabled (admin only)`).
- **Define the attribute:** add `birthdate` under *Realm settings → User profile*, which also gives it a proper label and validation.

**Note:**

Add a mapper for any additional claim the OIDC Bridge emits — the **Claim** name must match an `oidcClaim` in the bridge's claim mappings. To surface a new claim (e.g. `email`), add it to the bridge's `defaultClaimMappings` (and the flow's DCQL) first, then map it here. Any claim without a built-in Keycloak field appears as a custom attribute (subject to the unmanaged-attributes note above).

## Step 3: Test the Integration

### 3.1 Navigate to Login Page

Go to your Keycloak-protected application or the Keycloak account page:
```
https://keycloak.example.com/realms/{realm}/account
```

### 3.2 Initiate VC Login

Click **"Login with Verifiable Credential"** (or your configured display name).

You'll be redirected to the OIDC Bridge presentation page.

### 3.3 Present Credential

Choose your preferred presentation method:

![OIDC Bridge Presentation UI](https://docs.walt.id/img/enterprise-stack/services/OIDC-Bridge-Credential-Presentation-UI.png)

- **Scan QR Code** — Use your mobile wallet
- **Browser Wallet** — If using Chrome with DC API
- **Open Mobile Wallet** — For same-device flow
- **Open Web Wallet** — Redirect to web wallet

Present your mDL or other credential when prompted.

### 3.4 Complete Login

After successful presentation:
- OIDC Bridge validates the credential
- Extracts claims based on your mappings
- Issues OIDC tokens
- Redirects back to Keycloak
- Keycloak creates/updates the user account
- You're logged in!

## Step 4: Verify User Data in Keycloak

1. Go to **Users** in Keycloak Admin
2. Find the newly created user
3. Check the user's details and **Attributes** tab

You should see:
- `firstName`, `lastName` — from the mappers, on the built-in profile fields.
- `birthdate` — under the **Attributes** tab, as a custom attribute.
- The federated identity is keyed on `sub` (the mDL `document_number`), shown under the user's **Identity provider links**.

## Advanced Configuration

### Auto-Create Users

By default, Keycloak creates users on first login. To customize:

1. Go to **Identity Providers** → **oidc-bridge**
2. Under **First Login Flow**, select a flow (default: `first broker login`)
3. Customize the flow in **Authentication** → **Flows**

### Require Email Verification

To require email verification after VC login:

1. Edit the `first broker login` flow
2. Add a **Verify Email** step
3. Set to **Required**

**warning:**

**Note:** mDL credentials don't contain email addresses by default. You may need to use a different credential type or set email manually.

### Link Existing Accounts

To allow users to link their VC login to existing Keycloak accounts:

1. Go to **Identity Providers** → **oidc-bridge**
2. Set **First Login Flow** to `first broker login`
3. This flow prompts users to link or create accounts

### Trust Only Specific Issuers

Integrate with the [Trust Registry Service](https://docs.walt.id/enterprise-stack/services/trust-registry-service/overview.md):

1. Link Trust Registry to your Verifier2 service
2. Enable the `etsi-trust-list` policy in Verifier2
3. Only credentials from trusted issuers will be accepted

## Next Steps

- [Claim Mappings](https://docs.walt.id/enterprise-stack/services/oidc-bridge-service/claim-mappings.md) — Customize attribute extraction
- [Multi-Flow Configuration](https://docs.walt.id/enterprise-stack/services/oidc-bridge-service/multi-flow-config.md) — Configure presentation methods
- [Trust Registry Integration](https://docs.walt.id/enterprise-stack/services/trust-registry-service/verifier-integration.md) — Add issuer trust verification
