---
title: "Setup"
description: "This guide walks through creating, configuring, and verifying an OIDC Bridge service. We will set up the service inside a tenant. If you don't have a tenant yet, you can learn how to create one here."
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/setup
generated: 2026-07-20
---
# Setup

This guide walks through creating, configuring, and verifying an OIDC Bridge service. We will set up the service inside a tenant. If you don't have a tenant yet, you can learn how to create one [here](https://docs.walt.id/enterprise-stack/administration/tenants/create.md).

## Prerequisites

The OIDC Bridge depends on two other services, which must exist before it can serve logins. You reference them in the `dependencies` array of the create request ([Step 2](#step-2-create-the-oidc-bridge-service)):

- [Key Management Service (KMS)](https://docs.walt.id/enterprise-stack/services/key-management-service/overview.md) — signs the issued ID tokens.
- [Verifier2 Service](https://docs.walt.id/enterprise-stack/services/verifier2-service/overview.md) — requests and validates the credential presentation.

You will also need:
- Your IAM system's redirect URI.

## Step 1: Enable the Feature

Add `oidc-bridge` to `enabledFeatures` in your [`_features.conf`](https://docs.walt.id/enterprise-stack/setup/configurations/config-files/features.md):

```hocon
enabledFeatures = [
  # ... other features
  oidc-bridge
]
```

Restart the Enterprise Stack for the feature to take effect.

## Step 2: Create the OIDC Bridge Service

**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/{target}/resource-api/services/create' \
  -H 'accept: application/json' \
  -H 'Authorization: Bearer {yourToken}' \
  -H 'Content-Type: application/json' \
  -d '{
  "type": "oidc-bridge",
  "issuerUrl": "https://enterprise.example.com",
  "clients": {
    "my-iam": {
      "clientId": "my-iam",
      "clientSecret": "***",
      "redirectUris": ["https://iam.example.com/callback"],
      "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" }
  ],
  "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", "document_number"] }
                ]
              }
            ]
          }
        }
      }
    }
  },
  "presentationTimeoutSeconds": 300,
  "uiConfig": {
    "brandName": "My Company",
    "primaryColor": "#3B82F6",
    "webWalletBaseUrl": "https://wallet.example.com"
  },
  "dependencies": [
    "{organizationID}.{tenantID}.kms",
    "{organizationID}.{tenantID}.verifier2"
  ]
}'
```

**warning:**

**Every enabled flow needs a `verificationSetup`.** This is the credential request (the DCQL query / OID4VP Authorization Request) the OIDC bridge sends to the wallet.

**Path Parameters**

- `orgID`: _String_ (required) - Your organization's base URL or a valid host alias. For example, if your organization is named `test`, your default base URL is `test.enterprise-sandbox.waltid.dev` on the sandbox environment.
- `target`: _resourceIdentifier_ (required) - The organization + tenant to create the service in, plus the new service's ID: `{organizationID}.{tenantID}.oidc-bridge`, e.g. `waltid.tenant1.oidc-bridge`.

**Header Parameters**

- `Authorization`: _String_ (required) - Bearer token for Enterprise Stack authentication. Format: `Bearer {token}`.

**Body Parameters**

- `type`: _serviceType_ (required) - The type of service to create. Must be `"oidc-bridge"`.
- `issuerUrl`: _String_ (required) - Base URL of your Enterprise Stack. Used to build the OIDC `issuer` (see [Step 3](#step-3-verify-oidc-endpoints)).
- `clients`: _Object_ (required) - Map of client ID to client configuration. One entry per IAM system.

   <details><summary>Expand to see client configuration properties</summary>

  - `clientId`: _String_ - OIDC client identifier.
  - `clientSecret`: _String_ - Client secret for authentication.
  - `clientName` (optional): _String_ - Human-readable client name.
  - `redirectUris`: _Array of Strings_ - Allowed redirect URIs (exact match).
  - `allowedScopes` (optional): _Array of Strings_ - Allowed OIDC scopes. Defaults to `["openid", "profile", "email"]`.
  - `claimMappings` (optional): _Array_ - Overrides `defaultClaimMappings` for this client.
  - `verificationSetup` (optional): _Object_ - Overrides the flow's verification setup for this client. See [Verification Setup and DCQL](#verification-setup-and-dcql).
   </details>

- `defaultClaimMappings` (optional): _Array_ - Claim mappings applied when a client has none of its own. Defaults to `[]`. Each mapping has:

   <details><summary>Expand to see claim mapping properties</summary>

  - `oidcClaim`: _String_ - Target OIDC claim name (e.g. `given_name`).
  - `credentialPath`: _String_ - JSONPath to the credential attribute.
  - `transform` (optional): _String_ - One of `NONE`, `LOWERCASE`, `UPPERCASE`, `hash_sha256`. Defaults to `NONE`.
  - `credentialId` (optional): String - For multi-credential (DCQL) queries, the credential-query `id` in the flow's `dcql_query` to extract this claim from. If omitted, the first presented credential whose credentialPath resolves is used. If set but no matching credential was presented, the mapping is skipped (the claim is omitted).

   See [Claim Mappings](https://docs.walt.id/enterprise-stack/services/oidc-bridge-service/claim-mappings.md) for details.
   </details>

- `flows` (optional): _Object_ - Presentation flow configuration, keyed by `qr`, `dc_api`, `deep_link`, and `web_wallet`.

   <details><summary>Expand to see flow configuration properties</summary>

  Each flow accepts:
  - `enabled`: _Boolean_ - Enable/disable this flow. Defaults to `false`.
  - `buttonLabel` (optional): _String_ - Text shown on the presentation-selection button.
  - `targetUrl` (optional): _String_ - (`web_wallet` only) URL to redirect to.
  - `verificationSetup`: _Object_ - The credential request (DCQL query) for this flow. **Required for the flow to work** unless a client-level or service-level setup covers it (see the resolution order in the callout under [Step 2](#step-2-create-the-oidc-bridge-service)). Its value is a Verifier2 verification request. See [Verification Request](#verification-setup-and-dcql) and [Multi-Flow Configuration](https://docs.walt.id/enterprise-stack/services/oidc-bridge-service/multi-flow-config.md).
   </details>

- `tokenLifetime` (optional): _Object_ - Token lifetimes.

   <details><summary>Expand to see token lifetime properties</summary>

  - `idTokenExpirySeconds`: _Number_ - ID token lifetime. Defaults to `3600`.
  - `accessTokenExpirySeconds`: _Number_ - Access token lifetime. Defaults to `3600`.
  - `authCodeExpirySeconds`: _Number_ - Authorization code lifetime. Defaults to `300`.
   </details>

- `presentationTimeoutSeconds` (optional): _Number_ - How long a presentation session stays valid. Defaults to `300`.
- `uiConfig` (optional): _Object_ - Presentation-page branding.

   <details><summary>Expand to see UI configuration properties</summary>

  - `brandName` (optional): _String_ - Organization name shown on the presentation page.
  - `primaryColor` (optional): _String_ - Hex color for branding.
  - `logoUrl` (optional): _String_ - Logo URL.
  - `webWalletBaseUrl` (optional): _String_ - Base URL for the web wallet flow.
   </details>

- `dependencies` (optional): _Array of Strings_ - Resource paths of the services this bridge depends on: the [KMS](https://docs.walt.id/enterprise-stack/services/key-management-service) and [Verifier2](https://docs.walt.id/enterprise-stack/services/verifier2-service) services. Each listed service must already exist — otherwise the whole create request fails with `400`.

**Response Codes**

- `201` - Service created successfully. The response body is the full created configuration, including server-applied defaults (`enabled`, `allowedScopes`, `tokenLifetime`, `presentationTimeoutSeconds`).

**Example Response**

```json
{
  "type": "oidc-bridge",
  "traversable": true,
  "_id": "{target}",
  "issuerUrl": "https://enterprise.example.com",
  "enabled": true,
  "clients": {
    "my-iam": {
      "clientId": "my-iam",
      "clientSecret": "***",
      "redirectUris": ["https://iam.example.com/callback"],
      "allowedScopes": ["openid", "profile", "email"]
    }
  },
  "defaultClaimMappings": [ ... ],
  "tokenLifetime": {
    "idTokenExpirySeconds": 3600,
    "accessTokenExpirySeconds": 3600,
    "authCodeExpirySeconds": 300
  },
  "presentationTimeoutSeconds": 300,
  "parent": "{organizationID}.{tenantID}",
  "createdAt": "2026-01-01T00:00:00Z",
  "updatedAt": "2026-01-01T00:00:00Z"
}
```

### Verification Setup

The `verificationSetup` included in each flow in [Create the OIDC Bridge Service](#step-2-create-the-oidc-bridge-service) above is the same object you would send to Verifier2 directly.

```json
{
  "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", "document_number"] }
          ]
        }]
      }
    }
  }
}
```

For the full `VerificationSessionSetup` shape (flow types, policies, signing/encryption keys, `expectedOrigins` for DC API) and DCQL syntax, see the Verifier2 docs — this is the authoritative reference:

- [Verifier2 credential verification](https://docs.walt.id/enterprise-stack/services/verifier2-service/credential-verification/mdl-oid4vp.md)
- [DCQL query language](https://docs.walt.id/concepts/data-exchange-protocols/dcql.md) (and the [DCQL builder](https://dcql.walt.id/))

## Step 3: Verify OIDC Endpoints

All OIDC endpoints are exposed under `/v1/{target}/oidc-bridge-api/`:

| Endpoint | Description |
|----------|-------------|
| `.well-known/openid-configuration` | OIDC discovery document |
| `authorize` | Authorization endpoint (user-facing) |
| `token` | Token endpoint (backend) |
| `jwks` | JSON Web Key Set (public keys) |
| `userinfo` | UserInfo endpoint |

Check that the bridge is serving them:

```bash
# Discovery document
curl 'https://{orgID}.enterprise-sandbox.waltid.dev/v1/{target}/oidc-bridge-api/.well-known/openid-configuration'

# JWKS (public keys)
curl 'https://{orgID}.enterprise-sandbox.waltid.dev/v1/{target}/oidc-bridge-api/jwks'
```

**Example Response** (discovery document)

```json
{
  "issuer": "https://enterprise.example.com/v1/{target}/oidc-bridge-api",
  "authorization_endpoint": "https://enterprise.example.com/v1/{target}/oidc-bridge-api/authorize",
  "token_endpoint": "https://enterprise.example.com/v1/{target}/oidc-bridge-api/token",
  "userinfo_endpoint": "https://enterprise.example.com/v1/{target}/oidc-bridge-api/userinfo",
  "jwks_uri": "https://enterprise.example.com/v1/{target}/oidc-bridge-api/jwks",
  "response_types_supported": ["code"],
  "grant_types_supported": ["authorization_code"],
  "subject_types_supported": ["public"],
  "id_token_signing_alg_values_supported": ["ES256", "EdDSA"],
  "scopes_supported": ["openid", "profile", "email"],
  "token_endpoint_auth_methods_supported": ["client_secret_basic", "client_secret_post"],
  "code_challenge_methods_supported": ["S256"],
  "claims_supported": ["sub", "iss", "aud", "iat", "exp", "auth_time", "nonce", "name", "email", "given_name", "family_name"]
}
```

**info:**

The `issuer` value is your `issuerUrl` **plus the `/v1/{target}/oidc-bridge-api` path** — not the bare `issuerUrl`. Configure your IAM system with the full discovery URL and it derives the rest.

## Step 4: Configure Your IAM System

Use the discovery URL to configure your IAM system (Keycloak, Auth0, etc.):

**Discovery URL:**
```
https://{orgID}.enterprise-sandbox.waltid.dev/v1/{target}/oidc-bridge-api/.well-known/openid-configuration
```

**Client credentials:**
- Client ID and Client Secret — from your `clients` configuration.
- Client authentication: `client_secret_post` or `client_secret_basic`.

See [Keycloak Integration](https://docs.walt.id/enterprise-stack/services/oidc-bridge-service/iam-integrations/keycloak.md) for a complete example.

## Step 5: Test the Flow

1. Navigate to your IAM system's login page.
2. Click "Login with Verifiable Credential" (or similar).
3. You are redirected to the OIDC Bridge presentation page.
4. Choose a presentation method (QR, DC API, deep link, or web wallet).
5. Present your credential.
6. You are redirected back to the IAM system, logged in.

**Example presentation page:**

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

Users see all enabled flows with the button labels you configured.

---

## Credential Type Examples

Claim paths and DCQL metadata differ by credential format. The two most common are mDL (namespaced) and SD-JWT VC (flat).

### mDL (ISO 18013-5.1)

mDL credentials use **namespaced claim paths** under `org.iso.18013.5.1`:

```json
{
  "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" }
  ]
}
```

Matching DCQL for the QR flow:

```json
{
  "flows": {
    "qr": {
      "enabled": true,
      "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"] }
              ]
            }]
          }
        }
      }
    }
  }
}
```

### SD-JWT VC

SD-JWT VCs use **flat claim paths** (no namespace prefix):

```json
{
  "defaultClaimMappings": [
    { "oidcClaim": "sub", "credentialPath": "$.email", "transform": "hash_sha256" },
    { "oidcClaim": "given_name", "credentialPath": "$.given_name", "transform": "NONE" },
    { "oidcClaim": "family_name", "credentialPath": "$.family_name", "transform": "NONE" },
    { "oidcClaim": "email", "credentialPath": "$.email", "transform": "LOWERCASE" },
    { "oidcClaim": "birthdate", "credentialPath": "$.birthdate", "transform": "NONE" }
  ]
}
```

Matching DCQL for the QR flow:

```json
{
  "flows": {
    "qr": {
      "enabled": true,
      "verificationSetup": {
        "flow_type": "cross_device",
        "core_flow": {
          "dcql_query": {
            "credentials": [{
              "id": "identity-vc",
              "format": "dc+sd-jwt",
              "meta": { "vct_values": ["IdentityCredential"] },
              "claims": [
                { "path": ["given_name"] },
                { "path": ["family_name"] },
                { "path": ["email"] },
                { "path": ["birthdate"] }
              ]
            }]
          }
        }
      }
    }
  }
}
```

### mDL vs SD-JWT VC: Key Differences

| Aspect | mDL | SD-JWT VC |
|--------|-----|-----------|
| **Standard** | ISO 18013-5 | IETF SD-JWT VC |
| **Format** | `mso_mdoc` | `dc+sd-jwt` |
| **Claim structure** | Namespaced (`org.iso.18013.5.1`) | Flat (root level) |
| **Example path** | `$["org.iso.18013.5.1"]["given_name"]` | `$.given_name` |
| **DCQL metadata** | `doctype_value` | `vct_values` |
| **Typical use** | Government IDs (driver's license) | General verifiable credentials |

For multi-credential (OR) queries and per-credential claim mappings with `credentialId`, see [Claim Mappings](https://docs.walt.id/enterprise-stack/services/oidc-bridge-service/claim-mappings.md#multi-credential-queries).

## Updating Configuration

Update an existing OIDC Bridge by sending the **full** configuration to the update endpoint. The update replaces the stored configuration wholesale; attached dependencies are preserved.

**Option: CURL**

Endpoint: `/v1/{target}/oidc-bridge-api/configuration/update` | [API Reference](https://enterprise.sandbox.walt.id/swagger/index.html)

**Example Request**

```bash
curl -X 'PUT' \
  'https://{orgID}.enterprise-sandbox.waltid.dev/v1/{target}/oidc-bridge-api/configuration/update' \
  -H 'Authorization: Bearer {yourToken}' \
  -H 'Content-Type: application/json' \
  -d '{
  "_id": "{target}",
  "issuerUrl": "https://enterprise.example.com",
  "clients": { "...": "..." },
  "defaultClaimMappings": [ ... ],
  "flows": { "...": "..." }
}'
```

**Body Parameters**

- The full service configuration, using the same fields as [Step 2](#step-2-create-the-oidc-bridge-service), with two differences: the body **must include `_id`** and **must not include `type`**. (Create is the opposite: `type` is required and `_id` is taken from the path.)

**Response Codes**

- `200` - Configuration updated.

## Deleting the Service

**Option: CURL**

Endpoint: `/v1/{target}/resource-api/resources/delete-recursive` | [API Reference](https://enterprise.sandbox.walt.id/swagger/index.html)

**Example Request**

```bash
curl -X 'DELETE' \
  'https://{orgID}.enterprise-sandbox.waltid.dev/v1/{target}/resource-api/resources/delete-recursive' \
  -H 'Authorization: Bearer {yourToken}'
```

**Response Codes**

- `202` - Service and all of its sessions deleted.

**warning:**

Deleting the OIDC Bridge breaks any IAM integrations using it. Ensure all relying parties are migrated before deletion.

## Troubleshooting

### `invalid_client` Error

**Cause:** Client credentials mismatch.

**Fix:** Verify `clientId` and `clientSecret` match exactly in both the OIDC Bridge and your IAM system.

### `redirect_uri` Mismatch Error

**Cause:** Redirect URI not in the allowlist.

**Fix:** Ensure the redirect URI in your IAM system matches one of the `redirectUris` exactly (protocol, host, port, and path).

### Discovery Document 404

**Cause:** OIDC Bridge not created, or the feature is not enabled.

**Fix:**
1. Check the feature is enabled: `oidc-bridge` in `_features.conf`.
2. Verify the service exists: `GET /v1/{target}/oidc-bridge-api/configuration/view`.
3. Check the path: `/oidc-bridge-api/.well-known/openid-configuration`.

### Service Creation Fails with a Cast Error

**Cause:** The create request was sent to the tenant path instead of the full service path, so the server tried to overwrite the tenant.

**Fix:** POST to the **full service target** — `/v1/{organizationID}.{tenantID}.oidc-bridge/resource-api/services/create` — not `/v1/{organizationID}.{tenantID}/...`.

### Presentation Timeout

**Cause:** The user didn't complete the presentation in time.

**Fix:** Increase `presentationTimeoutSeconds` (default: `300`).

## Next Steps

- [Keycloak Integration](https://docs.walt.id/enterprise-stack/services/oidc-bridge-service/iam-integrations/keycloak.md) — Complete Keycloak setup guide.
- [Claim Mappings](https://docs.walt.id/enterprise-stack/services/oidc-bridge-service/claim-mappings.md) — Advanced claim extraction.
- [Multi-Flow Configuration](https://docs.walt.id/enterprise-stack/services/oidc-bridge-service/multi-flow-config.md) — Configure presentation flows.
