Configuring Apicurio Registry security options

This chapter explains how to set configuration options for Apicurio Registry security. For example, this includes authentication in Keycloak, Microsoft Azure Active Directory, or Dex, and role-based authorization in Apicurio Registry.

For a list of all available configuration options, see Apicurio Registry configuration reference.

Configuring Apicurio Registry authentication and authorization with Keycloak

This section explains how to manually configure authentication and authorization options for Apicurio Registry and Keycloak.

Alternatively, for details on how to configure these settings automatically, see the Apicurio Registry Operator documentation.

The Apicurio Registry web console and core REST API support authentication in Keycloak based on OAuth and OpenID Connect (OIDC). The same Keycloak realm and users are federated across the Apicurio Registry web console and core REST API using OpenID Connect so that you only require one set of credentials.

Apicurio Registry provides role-based authorization for default admin, write, and read-only user roles. Apicurio Registry provides content-based authorization at the schema or API level, where only the creator of the registry artifact can update or delete it. Apicurio Registry authentication and authorization settings are disabled by default.

Prerequisites
  • Keycloak is installed and running. For more details, see the Keycloak user documentation.

  • Apicurio Registry is installed and running.

Procedure
  1. In the Keycloak Admin Console, create a Keycloak realm for Apicurio Registry. By default, Apicurio Registry expects a realm name of registry. For details on creating realms, see the Keycloak user documentation.

  2. Create a Keycloak client for the Apicurio Registry API. By default, Apicurio Registry expects the following settings:

    • Client ID: registry-api

    • Client Protocol: openid-connect

    • Access Type: bearer-only

      You can use the defaults for the other client settings.

      If you are using Keycloak service accounts, the client Access Type must be confidential instead of bearer-only.
  3. Create a Keycloak client for the Apicurio Registry web console. By default, Apicurio Registry expects the following settings:

    • Client ID: apicurio-registry

    • Client Protocol: openid-connect

    • Access Type: public

    • Valid Redirect URLs: http://my-registry-url:8080/*

    • Web Origins: +

      You can use the defaults for the other client settings.

  4. In your Apicurio Registry deployment on OpenShift, set the following Apicurio Registry environment variables to configure authentication using Keycloak:

    Table 1. Configuration for Apicurio Registry authentication with Keycloak
    Environment variable Description Type Default

    QUARKUS_OIDC_TENANT_ENABLED

    Enables authentication for Apicurio Registry. When set to true, the environment variables that follow are required for authentication using Keycloak.

    String

    false

    QUARKUS_OIDC_AUTH_SERVER_URL

    The URL of the Keycloak authentication server. For example, http://localhost:8080.

    String

    -

    QUARKUS_OIDC_CLIENT_ID

    The client ID for the Apicurio Registry REST API.

    String

    registry-api

    APICURIO_UI_AUTH_OIDC_CLIENT_ID

    The client ID for the Apicurio Registry web console.

    String

    apicurio-registry

    For an example of setting environment variables on OpenShift, see [configuring-liveness-readiness-probes_registry].
  5. Set the following option to true to enable Apicurio Registry user roles in Keycloak:

    Table 2. Configuration for Apicurio Registry role-based authorization
    Environment variable Java system property Type Default value

    APICURIO_AUTH_ROLE_BASED_AUTHORIZATION

    apicurio.auth.role-based-authorization

    Boolean

    false

  6. When Apicurio Registry user roles are enabled, you must assign Apicurio Registry users to at least one of the following default user roles in your Keycloak realm:

    Table 3. Default user roles for registry authentication and authorization
    Role Read artifacts Write artifacts Global rules Summary

    sr-admin

    Yes

    Yes

    Yes

    Full access to all create, read, update, and delete operations.

    sr-developer

    Yes

    Yes

    No

    Access to create, read, update, and delete operations, except configuring global rules. This role can configure artifact-specific rules.

    sr-readonly

    Yes

    No

    No

    Access to read and search operations only. This role cannot configure any rules.

  7. Set the following to true to enable owner-only authorization for updates to schema and API artifacts in Apicurio Registry:

    Table 4. Configuration for owner-only authorization
    Environment variable Java system property Type Default value

    APICURIO_AUTH_OWNER_ONLY_AUTHORIZATION_LIMIT_GROUP_ACCESS

    apicurio.auth.owner-only-authorization.limit-group-access

    Boolean

    false

Additional resources

Configuring Apicurio Registry authentication and authorization with Microsoft Azure Active Directory

This section explains how to manually configure authentication and authorization options for Apicurio Registry and Microsoft Azure Active Directory (Azure AD).

The Apicurio Registry web console and core REST API support authentication in Azure AD based on OpenID Connect (OIDC) and the OAuth Authorization Code Flow. Apicurio Registry provides role-based authorization for default admin, write, and read-only user roles. Apicurio Registry authentication and authorization settings are disabled by default.

To secure Apicurio Registry with Azure AD, you require a valid directory in Azure AD with specific configuration. This involves registering the Apicurio Registry application in the Azure AD portal with recommended settings and configuring environment variables in Apicurio Registry.

Prerequisites
Procedure
  1. Log in to the Azure AD portal using your email address or GitHub account.

  2. In the navigation menu, select Manage > App registrations > New registration, and complete the following settings:

    • Name: Enter your application name. For example: apicurio-registry-example

    • Supported account types: Click Accounts in any organizational directory.

    • Redirect URI: Select Single-page application from the list, and enter your Apicurio Registry web console application host. For example: https://test-registry.com/ui/

      You must register your Apicurio Registry application host as a Redirect URI. When logging in, users are redirected from Apicurio Registry to Azure AD for authentication, and you want to send them back to your application afterwards. Azure AD does not allow any redirect URLs that are not registered.
  3. Click Register. You can view your app registration details by selecting Manage > App registrations > apicurio-registry-example.

  4. Select Manage > Authentication and ensure that the application is configured with your redirect URLs and tokens as follows:

    • Redirect URIs: For example: https://test-registry.com/ui/

    • Implicit grant and hybrid flows: Click ID tokens (used for implicit and hybrid flows)

  5. Select Azure AD > Admin > App registrations > Your app > Application (client) ID. For example: 123456a7-b8c9-012d-e3f4-5fg67h8i901

  6. Select Azure AD > Admin > App registrations > Your app > Directory (tenant) ID. For example: https://login.microsoftonline.com/1a2bc34d-567e-89f1-g0hi-1j2kl3m4no56/v2.0

  7. In Apicurio Registry, configure the following environment variables with your Azure AD settings:

    Table 5. Configuration for Azure AD settings in Apicurio Registry
    Environment variable Description Setting

    QUARKUS_OIDC_CLIENT_ID

    The client application ID for the Apicurio Registry REST API

    Your Azure AD Application (client) ID obtained in step 5. For example: 123456a7-b8c9-012d-e3f4-5fg67h8i901

    APICURIO_UI_AUTH_OIDC_CLIENT_ID

    The client application ID for the Apicurio Registry web console.

    Your Azure AD Application (client) ID obtained in step 5. For example: 123456a7-b8c9-012d-e3f4-5fg67h8i901

    QUARKUS_OIDC_AUTH_SERVER_URL

    The URL for authentication in Azure AD.

    Your Azure AD Application (tenant) ID obtained in step 6. For example: https://login.microsoftonline.com/1a2bc34d-567e-89f1-g0hi-1j2kl3m4no56/v2.0.

  8. In Apicurio Registry, configure the following environment variables for Apicurio Registry-specific settings:

    Table 6. Configuration for Apicurio Registry-specific settings
    Environment variable Description Setting

    QUARKUS_OIDC_TENANT_ENABLED

    Enables authentication for Apicurio Registry.

    true

    QUARKUS_HTTP_CORS_ORIGINS

    The host for your Apicurio Registry deployment for cross-origin resource sharing (CORS).

    For example: https://test-registry.com

    APICURIO_UI_AUTH_OIDC_REDIRECT_URI

    The host for your Apicurio Registry web console.

    For example: https://test-registry.com/ui

    APICURIO_AUTH_ROLE_BASED_AUTHORIZATION

    Enables role-based authorization in Apicurio Registry.

    true

    QUARKUS_OIDC_ROLES_ROLE_CLAIM_PATH

    The name of the claim in which Azure AD stores roles.

    roles

    When you enable roles in Apicurio Registry, you must also create the same roles in Azure AD as application roles. The default roles expected by Apicurio Registry are sr-admin, sr-developer, and sr-readonly.
  9. In Apicurio Registry UI, configure the following environment variables:

Environment variable Description Setting

REGISTRY_AUTH_TYPE

oidc

REGISTRY_AUTH_TOKEN_TYPE

Which token to use. Azure AD has non-standard access tokens, so we need to use id tokens.

id

Multiple role mappings for Azure Entra ID

When using Azure Entra ID (formerly Azure AD), you may need to map multiple Azure AD groups or app roles to a single Apicurio Registry role. This is useful when:

  • Interactive users authenticate via Azure AD groups

  • Service principals authenticate via app roles

  • Both should grant the same Apicurio Registry permissions

To configure multiple role mappings, use comma-separated values:

# Map multiple Azure AD groups/roles to registry roles
APICURIO_AUTH_ROLES_ADMIN=sr-admin,12345678-aaaa-bbbb-cccc-ddddeeeefffff,AppRole.Admin
APICURIO_AUTH_ROLES_DEVELOPER=sr-developer,87654321-aaaa-bbbb-cccc-ddddeeeefffff,AppRole.Developer
APICURIO_AUTH_ROLES_READONLY=sr-readonly,abcd1234-aaaa-bbbb-cccc-ddddeeeefffff,AppRole.Reader

With this configuration, a user with any of the listed roles or group memberships will be granted the corresponding Apicurio Registry authorization level.

Client-specific OAuth scopes for Azure Entra ID

Azure Entra ID requires that each app registration uses a unique scope. When you have multiple app registrations (for example, one for developers and one for read-only access), you can configure client-specific scopes:

# Default scope for all clients (fallback)
APICURIO_AUTHN_BASIC_SCOPE=api://default-app-id/.default

# Client-specific scope overrides
# Replace dots in client IDs with underscores for environment variables
APICURIO_AUTHN_BASIC_SCOPE_SR_DEVELOPER_CLIENT=api://developer-app-id/.default
APICURIO_AUTHN_BASIC_SCOPE_SR_READONLY_CLIENT=api://readonly-app-id/.default

When a client authenticates, Apicurio Registry will:

  1. Check for a client-specific scope configuration matching the client ID

  2. If not found, use the default scope

  3. If no scope is configured, proceed without a scope parameter

Multiple scopes can be specified using comma separation. They will be converted to space-separated format per the OAuth2 specification.
Additional resources

Configuring Apicurio Registry authentication and authorization with Dex

This section explains how to configure authentication and authorization for Apicurio Registry by using Dex as a federated OpenID Connect (OIDC) identity provider.

Dex acts as an OIDC-compliant bridge in front of identity providers that do not fully implement the OIDC specification. This is useful when your platform uses an OAuth server that does not expose standard OIDC endpoints, such as OpenShift’s built-in OAuth server, LDAP directories, or SAML-based providers. Dex has connectors for 15+ identity providers including OpenShift, LDAP, SAML, GitHub, GitLab, Google, and Microsoft.

Without an OIDC-compliant provider, the following Apicurio Registry features do not work:

Table 7. Features requiring OIDC compliance
Feature Without OIDC bridge Why it breaks

UI login flow

Not available

The Apicurio Registry web console uses oidc-client-ts, which requires OIDC discovery (/.well-known/openid-configuration).

Principal identity

Not available

The username cannot be extracted because it may be nested in a non-standard location in the token. Apicurio Registry expects flat top-level claims.

Owner-based authorization

Not available

Depends on principal identity, which is empty when the provider does not expose standard claims.

By deploying Dex between Apicurio Registry and your identity provider, all three features work because Dex exposes standard OIDC discovery, issues JWTs with standard claims (sub, name, email, groups), and provides a standard JWKS endpoint.

Prerequisites
  • A Kubernetes cluster with Helm 3.x installed.

  • Apicurio Registry is installed and running.

  • Access to the upstream identity provider you want to integrate (for example, OpenShift, LDAP, or GitHub).

Procedure
  1. Deploy Dex on your cluster by using Helm:

    helm repo add dex https://charts.dexidp.io
    helm repo update
    {kubernetes-client} create namespace dex
  2. Create a Helm values file named dex-values.yaml with the following content. Replace the placeholder values with your actual configuration.

    Apicurio Registry needs two OAuth clients: a public client for the browser-based UI (which cannot hold a secret) and a confidential client for the backend and CLI. You must also configure allowedOrigins so the UI can fetch the Dex OIDC discovery endpoint via CORS.

    config:
      issuer: https://dex.<cluster-domain>
    
      storage:
        type: kubernetes
        config:
          inCluster: true
    
      web:
        http: 0.0.0.0:5556
        # CORS: Required so the Apicurio UI (oidc-client-ts) can fetch
        # /.well-known/openid-configuration from a different origin.
        allowedOrigins:
          - "https://<registry-ui-route>"
    
      oauth2:
        skipApprovalScreen: true
        responseTypes: [code, token, id_token]
    
      # Two clients are needed:
      # - A PUBLIC client for the browser UI (oidc-client-ts cannot hold secrets)
      # - A CONFIDENTIAL client for CLI/API token exchange
      staticClients:
        - id: apicurio-registry-ui
          name: Apicurio Registry UI
          public: true
          redirectURIs:
            - "https://<registry-ui-route>/"
            - "https://<registry-ui-route>/dashboard"
        - id: apicurio-registry
          name: Apicurio Registry API
          secret: <GENERATE_A_SECURE_SECRET>
          redirectURIs:
            - "https://<registry-ui-route>/"
            - "https://<registry-app-route>/"
    
      connectors: []  # Configure in the next step
    
    ingress:
      enabled: true
      hosts:
        - host: dex.<cluster-domain>
          paths:
            - path: /
              pathType: Prefix
    The UI client must be public: true because the browser-based authorization code flow with PKCE cannot securely store a client secret. If you use a single confidential client for both UI and backend, the UI login will fail with invalid_client errors. The /dashboard redirect URI is needed because the Apicurio Registry UI redirects there after login.
    When generating the client secret, avoid special characters such as +, /, or = that may cause URL encoding mismatches. Use openssl rand -base64 32 | tr -d '+/=' | head -c 32 to generate a safe secret.

    On OpenShift, create a Route instead of using Ingress:

    oc create route edge dex --service=dex --port=5556 --hostname=dex.<cluster-domain> --namespace=dex
  3. Install Dex by using Helm:

    helm install dex dex/dex -n dex -f dex-values.yaml
  4. Verify that OIDC discovery is available:

    curl -sk https://dex.<cluster-domain>/.well-known/openid-configuration | jq .issuer
    # Expected: "https://dex.<cluster-domain>"
  5. Configure a connector for your upstream identity provider. Add the connector configuration to the connectors section in your dex-values.yaml file. See Dex connector examples for configuration examples.

  6. After updating the connector configuration, upgrade the Dex Helm release:

    helm upgrade dex dex/dex -n dex -f dex-values.yaml
  7. In your Apicurio Registry deployment, set the following environment variables to configure authentication using Dex:

    Table 8. Configuration for Apicurio Registry authentication with Dex
    Environment variable Description Type Default

    QUARKUS_OIDC_TENANT_ENABLED

    Enables authentication for Apicurio Registry. When set to true, the environment variables that follow are required.

    String

    false

    QUARKUS_OIDC_AUTH_SERVER_URL

    The URL of the Dex OIDC server. For example, https://dex.example.com.

    String

    -

    QUARKUS_OIDC_CLIENT_ID

    The client ID for the Apicurio Registry REST API. Must match the public client ID (apicurio-registry-ui) so the backend accepts tokens issued by Dex for the UI.

    String

    registry-api

    APICURIO_UI_AUTH_OIDC_CLIENT_ID

    The client ID for the Apicurio Registry web console. Must match the public Dex static client.

    String

    apicurio-registry

    QUARKUS_OIDC_AUTHENTICATION_SCOPES

    The OIDC scopes to request. Must include groups so that Dex includes group membership in the token.

    String

    openid

    QUARKUS_OIDC_ROLES_ROLE_CLAIM_PATH

    The JWT claim path where roles are stored. Dex passes upstream group membership in the groups claim.

    String

    -

    QUARKUS_OIDC_TOKEN_PRINCIPAL_CLAIM

    The JWT claim to use as the principal identity for artifact ownership. Set to email because Dex populates this as a flat top-level claim.

    String

    sub

    APICURIO_UI_AUTH_OIDC_SCOPE

    The OIDC scopes requested by the Apicurio Registry web console. Must include groups so that the UI token contains group membership for role-based authorization.

    String

    openid profile email

    For example, when using Dex, set these values:

    QUARKUS_OIDC_TENANT_ENABLED=true
    QUARKUS_OIDC_AUTH_SERVER_URL=https://dex.example.com
    QUARKUS_OIDC_CLIENT_ID=apicurio-registry-ui
    APICURIO_UI_AUTH_OIDC_CLIENT_ID=apicurio-registry-ui
    QUARKUS_OIDC_AUTHENTICATION_SCOPES=openid,email,profile,groups
    QUARKUS_OIDC_ROLES_ROLE_CLAIM_PATH=groups
    QUARKUS_OIDC_TOKEN_PRINCIPAL_CLAIM=email
    APICURIO_UI_AUTH_OIDC_SCOPE=openid profile email groups
    Both QUARKUS_OIDC_CLIENT_ID and APICURIO_UI_AUTH_OIDC_CLIENT_ID must be set to the public client ID (apicurio-registry-ui). If they do not match, the backend will reject tokens issued for a different audience with InvalidJwtSignatureException errors.
  8. Configure role-based authorization. Dex passes upstream group membership in the groups claim. Map your upstream groups to Apicurio Registry roles:

    Table 9. Configuration for Apicurio Registry role-based authorization with Dex
    Environment variable Description Type Default

    APICURIO_AUTH_ROLE_BASED_AUTHORIZATION

    Enables role-based authorization in Apicurio Registry.

    Boolean

    false

    APICURIO_AUTH_ROLE_SOURCE

    Where to get user roles. Set to token to read from the JWT.

    String

    token

    APICURIO_AUTH_ROLES_ADMIN

    The upstream group name that grants admin access.

    String

    sr-admin

    APICURIO_AUTH_ROLES_DEVELOPER

    The upstream group name that grants developer access.

    String

    sr-developer

    APICURIO_AUTH_ROLES_READONLY

    The upstream group name that grants read-only access.

    String

    sr-readonly

    The mapping between upstream provider groups and Apicurio Registry roles works as follows:

    Table 10. Group-to-role mapping example
    Upstream group Dex passes as CR role config Apicurio Registry role

    registry-admins

    registry-admins

    admin: registry-admins

    Admin

    registry-developers

    registry-developers

    developer: registry-developers

    Developer

    registry-readers

    registry-readers

    readOnly: registry-readers

    Read-Only

  9. Enable owner-based authorization. Because Dex provides proper principal identity through the email claim, owner-based authorization works correctly:

    APICURIO_AUTH_OWNER_ONLY_AUTHORIZATION=true

    With this setting, only the user who created an artifact can modify or delete it. For more details on owner-only authorization, see Apicurio Registry owner-only authorization.

  10. If you are using the Apicurio Registry Operator on Kubernetes, you can configure all of the above in the ApicurioRegistry3 custom resource. The spec.app.auth section covers the standard OIDC settings, and Dex-specific properties must be set using spec.app.env. Use spec.app.ingress with TLS annotations instead of the bare host field:

    apiVersion: registry.apicur.io/v1
    kind: ApicurioRegistry3
    metadata:
      name: apicurio-registry
      namespace: apicurio-registry
    spec:
      app:
        ingress:
          host: apicurio-registry-app-apicurio-registry.apps.<cluster-domain>
          # On OpenShift, this annotation enables edge TLS termination on the Route,
          # so the app is served over HTTPS with automatic HTTP -> HTTPS redirect.
          annotations:
            route.openshift.io/termination: edge
        auth:
          enabled: true
          # Both must point to the PUBLIC Dex client so the backend accepts
          # tokens with aud=apicurio-registry-ui issued by the browser flow.
          appClientId: apicurio-registry-ui
          uiClientId: apicurio-registry-ui
          authServerUrl: "https://dex.<cluster-domain>"
          # Explicit redirect URI prevents oidc-client-ts from using the current
          # page URL (which may contain stale ?code=...&state=... query params),
          # fixing silent token refresh failures.
          redirectUri: "https://<registry-ui-route>/"
          anonymousReadsEnabled: true
          tls:
            tlsVerificationType: "none"
          authz:
            enabled: true
            readAccessEnabled: true
            ownerOnlyEnabled: true
            groupAccessEnabled: false
            roles:
              source: token
              admin: "registry-admins"
              developer: "registry-developers"
              readOnly: "registry-readers"
            adminOverride:
              enabled: true
              from: token
              type: role
              role: "registry-admins"
        env:
          - name: QUARKUS_OIDC_AUTHENTICATION_SCOPES
            value: "openid,email,profile,groups"
          - name: QUARKUS_OIDC_ROLES_ROLE_CLAIM_PATH
            value: "groups"
          - name: QUARKUS_OIDC_TOKEN_PRINCIPAL_CLAIM
            value: "email"
          # The UI scope defaults to "openid profile email" which does NOT include
          # groups. Without this, Dex won't include group membership in the token
          # and RBAC will deny all write operations.
          - name: APICURIO_UI_AUTH_OIDC_SCOPE
            value: "openid profile email groups"
      ui:
        ingress:
          host: apicurio-registry-ui-apicurio-registry.apps.<cluster-domain>
          annotations:
            route.openshift.io/termination: edge
        env:
          # The operator hardcodes http:// for REGISTRY_API_URL.
          # Override it to use HTTPS since we enabled edge TLS on the app route.
          - name: REGISTRY_API_URL
            value: "https://apicurio-registry-app-apicurio-registry.apps.<cluster-domain>/apis/registry/v3"

    There are a few important details in this CR:

    Two-client architecture — Both appClientId and uiClientId are set to apicurio-registry-ui (the public Dex client). The browser-based UI uses oidc-client-ts, which cannot securely hold a client secret. If you use a confidential client for the UI, the token exchange will fail with Invalid client credentials. The backend validates JWT signatures via the JWKS endpoint and does not need a client secret. The separate confidential client (apicurio-registry) is kept for CLI/API token exchange via curl or scripts.

    UI scope with groups — The APICURIO_UI_AUTH_OIDC_SCOPE env var adds groups to the scope the UI requests from Dex. Without this, the default scope (openid profile email) does not include groups, and Dex will not put group membership in the token. RBAC then denies all write operations because it cannot determine the user’s role. This has been verified on a live cluster with Dex v2.45.1 and the OpenShift connector: tokens include the groups claim with the user’s OpenShift group memberships, and Apicurio Registry successfully resolves roles for RBAC authorization.

    Explicit redirectUri — Without this, oidc-client-ts uses window.location.href as the redirect URI for silent token refresh. After the initial login, the URL may still contain ?code=…​&state=…​ query parameters, which do not match any registered redirect URI in Dex, causing a 400 error.

    A complete example CR is available in the Dex example CR.
  11. Verify that all features work correctly:

    # Verify OIDC discovery
    curl -sk https://dex.<cluster-domain>/.well-known/openid-configuration | jq .issuer
    
    # Verify anonymous read access (if enabled)
    curl -sk https://<registry-app-route>/apis/registry/v3/system/info
    
    # Verify UI login — open in browser and authenticate via your upstream provider
    # https://<registry-ui-route>
    
    # Verify principal identity — artifacts should have an owner
    curl -sk https://<registry-app-route>/apis/registry/v3/groups/my-group \
      -H "Authorization: Bearer $TOKEN" | jq .owner
    # Expected: the user's email address, NOT empty

Dex connector examples

Dex supports many upstream identity providers via pluggable connectors. The following examples show how to configure the most common connectors.

OpenShift connector

First, create an OAuthClient for Dex on your OpenShift cluster:

CLIENT_SECRET=$(openssl rand -base64 32 | tr -d '=' | head -c 32)

cat <<EOF | oc apply -f -
apiVersion: oauth.openshift.io/v1
kind: OAuthClient
metadata:
  name: dex
grantMethod: auto
secret: "$CLIENT_SECRET"
redirectURIs:
  - "https://dex.<cluster-domain>/callback"
EOF

Then add the connector to your dex-values.yaml:

connectors:
  - type: openshift
    id: openshift
    name: OpenShift
    config:
      issuer: https://api.<cluster-domain>:6443
      clientID: dex
      clientSecret: "<CLIENT_SECRET>"
      redirectURI: https://dex.<cluster-domain>/callback
      groups:
        - registry-admins
        - registry-developers
        - registry-readers
      insecureCA: true  # Set to false in production and configure proper CA

LDAP connector

connectors:
  - type: ldap
    id: ldap
    name: LDAP
    config:
      host: ldap.example.com:636
      bindDN: cn=admin,dc=example,dc=com
      bindPW: <bind-password>
      userSearch:
        baseDN: ou=users,dc=example,dc=com
        filter: "(objectClass=person)"
        username: uid
        idAttr: uid
        emailAttr: mail
        nameAttr: cn
      groupSearch:
        baseDN: ou=groups,dc=example,dc=com
        filter: "(objectClass=groupOfNames)"
        userMatchers:
          - userAttr: DN
            groupAttr: member
        nameAttr: cn

GitHub connector

connectors:
  - type: github
    id: github
    name: GitHub
    config:
      clientID: <github-oauth-app-client-id>
      clientSecret: <github-oauth-app-client-secret>
      redirectURI: https://dex.<cluster-domain>/callback
      orgs:
        - name: your-organization
          teams:
            - registry-admins
            - registry-developers

Microsoft Azure AD connector

connectors:
  - type: microsoft
    id: microsoft
    name: Microsoft
    config:
      clientID: <azure-app-client-id>
      clientSecret: <azure-app-client-secret>
      redirectURI: https://dex.<cluster-domain>/callback
      tenant: <azure-tenant-id>
      groups:
        - <registry-admins-group-uuid>
        - <registry-developers-group-uuid>

SAML 2.0 connector

connectors:
  - type: saml
    id: saml
    name: Corporate SSO
    config:
      ssoURL: https://idp.example.com/sso
      ca: /etc/dex/saml-ca.crt
      redirectURI: https://dex.<cluster-domain>/callback
      usernameAttr: name
      emailAttr: email
      groupsAttr: groups
Dex supports using multiple connectors simultaneously. This enables federation across multiple identity providers, allowing users from different organizations or systems to authenticate to Apicurio Registry through a single Dex instance.

Machine-to-machine (M2M) access and Kafka SerDes

The configuration above covers the browser-based UI flow. For CI/CD pipelines, Kafka SerDes, and services that need to interact with the Apicurio Registry API without a browser, additional considerations apply.

Dex client_credentials support (unreleased as of v2.45.1)

Dex added client_credentials support via PR #4583 (merged March 3, 2026), but this feature is not included in Dex v2.45.1 — the latest stable release at time of writing. It will ship in v2.46.0 or later.

To test before a stable release, use the dexidp/dex:master image from Docker Hub (rebuilt daily by Dex CI). For production, wait for v2.46.0+.

Enable it by setting an environment variable on the Dex deployment:

# In the Dex Deployment spec (requires Dex master or >= v2.46.0)
env:
  - name: DEX_CLIENT_CREDENTIAL_GRANT_ENABLED_BY_DEFAULT
    value: "true"

Then use the confidential client to obtain a token:

TOKEN_RESPONSE=$(curl -s -X POST "https://dex.<cluster-domain>/token" \
  --data-urlencode "grant_type=client_credentials" \
  --data-urlencode "client_id=apicurio-registry" \
  --data-urlencode "client_secret=<CLIENT_SECRET>" \
  --data-urlencode "scope=openid profile")

ACCESS_TOKEN=$(echo $TOKEN_RESPONSE | jq -r .access_token)
Even with client_credentials working, the tokens do not include the groups claim. The groups scope is accepted but no groups are populated because there is no upstream connector or user in the client_credentials flow — the token is built solely from the static client definition, which has no group membership. Since Apicurio Registry uses groups for RBAC, M2M clients authenticating via client_credentials will have no assigned role.
Table 11. M2M access with Dex client_credentials
Scenario Works? Why

SerDes reading schemas (with anonymousReadsEnabled: true)

Yes

Anonymous reads bypass authentication entirely.

SerDes registering schemas

No

No role in token means write access is denied.

CI/CD creating artifacts

No

No role in token means write access is denied.

For the most common Kafka use case — consumers fetching schemas — you do not need client_credentials at all. Set anonymousReadsEnabled: true in the Apicurio Registry CR and the SerDes can read schemas without any token. This is the recommended approach for read-heavy workloads.

For schema registration (typically done by developers or CI/CD, not by Kafka producers at runtime), use the authorization code flow with the confidential client and a service user that has the right groups.

Upcoming fix: groups on static clients

The lack of groups in client_credentials tokens has been reported in dexidp/dex#4690, and a fix has been submitted in dexidp/dex#4691. The fix adds a clientCredentialsClaims sub-struct to static client definitions, keeping identity attributes separate from core client fields:

staticClients:
  - id: apicurio-registry
    secret: "..."
    clientCredentialsClaims:
      groups: ["registry-admins"]   # included in client_credentials tokens

Track the PR for when this lands in a stable Dex release.

When to use Keycloak instead

If your deployment requires M2M write access with proper RBAC — for example, if Kafka producers register schemas at runtime, or if you have many CI/CD pipelines that need different permission levels — use Keycloak (or Auth0, Azure AD) instead of Dex. These providers support service account roles natively, so client_credentials tokens include all the claims Apicurio Registry needs.

Dex is the right choice when:

  • Your upstream IdP is non-OIDC-compliant (OpenShift OAuth, LDAP, SAML)

  • You primarily need user-facing auth (UI login, principal identity, owner-based authz)

  • M2M workloads are read-only (SerDes consumers with anonymous reads)

Dex production considerations

When running Dex in production, consider the following:

  • TLS: Configure Dex with proper TLS via Ingress termination or its own certificate. In the Apicurio Registry CR, set spec.app.auth.tls.tlsVerificationType to certificate and provide a truststore if using a private CA.

  • High availability: Run multiple Dex replicas. For better HA, switch Dex’s storage backend from Kubernetes CRDs to PostgreSQL.

  • Network policies: Restrict access so that only Apicurio Registry pods and the ingress controller can reach Dex. Ensure Dex can reach your upstream identity providers.

  • Secret management: Store the Dex client secret securely using Kubernetes Secrets or a secrets manager such as HashiCorp Vault.

Dex troubleshooting

  • "Invalid client credentials" on UI login: The UI (oidc-client-ts) runs in the browser and cannot send a client secret. Use a public Dex client (public: true, no secret) for the UI. A confidential client will always fail.

  • 403 Forbidden on API calls after UI login: Two common causes: (1) Audience mismatch — the UI gets tokens with aud: "apicurio-registry-ui" but the backend expects aud: "apicurio-registry". Fix: set both appClientId and uiClientId to the same public client ID. (2) Missing groups scope — the default UI scope does not include groups. Fix: set APICURIO_UI_AUTH_OIDC_SCOPE to openid profile email groups.

  • Groups not appearing in the token: Verify that both QUARKUS_OIDC_AUTHENTICATION_SCOPES (backend) and APICURIO_UI_AUTH_OIDC_SCOPE (UI) include groups. Decode the JWT to inspect: echo $TOKEN | cut -d. -f2 | base64 -d | jq .

  • "Unregistered redirect_uri" on page refresh: The UI redirects to /dashboard after login. Add https://<registry-ui-route>/dashboard to the Dex client’s redirect URIs.

  • Silent token refresh returns 400: oidc-client-ts uses the current page URL (with stale ?code=…​&state=…​ params) as the redirect URI. Fix: set redirectUri explicitly in the Apicurio Registry CR auth config.

  • "No end session endpoint" on logout: Dex does not implement OIDC RP-Initiated Logout. The local session is still cleared, but signoutRedirect fails. This is a known Dex limitation — the error is non-blocking.

  • CORS errors in browser console: The UI cannot fetch /.well-known/openid-configuration from Dex. Add the UI route origin to web.allowedOrigins in the Dex config.

  • InvalidJwtSignatureException after Dex restart: Dex generates new signing keys on restart. Old tokens become invalid. Users must re-authenticate. For production, configure persistent signing keys.

  • invalid_client when exchanging authorization code: If the client secret contains special characters (+, /, =), use --data-urlencode instead of -d in curl.

  • Owner field is empty: Check that QUARKUS_OIDC_TOKEN_PRINCIPAL_CLAIM is set to a claim that Dex populates, such as email, name, or sub.

  • UI redirects to Dex but gets an error: Check Dex logs (kubectl logs -n dex deploy/dex). This is usually a redirect URI mismatch or connector misconfiguration.

  • client_credentials returns unsupported_grant_type: This grant type was added in Dex PR #4583 (merged March 3, 2026) but is not included in v2.45.1 or earlier. You need Dex >= v2.46.0 or a build from master.

  • "OIDC Server is not available": The Apicurio Registry pod cannot reach Dex. Check DNS resolution, network policies, and TLS trust configuration.

Additional resources

Apicurio Registry authentication and authorization configuration options

Apicurio Registry provides authentication options for OpenID Connect with Keycloak and HTTP basic authentication.

Apicurio Registry provides authorization options for role-based and content-based approaches:

  • Role-based authorization for default admin, write, and read-only user roles.

  • Content-based authorization for schema or API artifacts, where only the owner of the artifacts or artifact group can update or delete artifacts.

All authentication and authorization options in Apicurio Registry are disabled by default. Before enabling any of these options, you must first set the QUARKUS_OIDC_TENANT_ENABLED option to true.

This chapter provides details on the following configuration options:

Apicurio Registry authentication by using OpenID Connect with Keycloak

You can set the following environment variables to configure authentication for the Apicurio Registry web console and API with Keycloak:

Table 12. Configuration for Apicurio Registry authentication with Keycloak
Environment variable Description Type Default

QUARKUS_OIDC_TENANT_ENABLED

Enables authentication for Apicurio Registry. When set to true, the environment variables that follow are required for authentication using Keycloak.

String

false

QUARKUS_OIDC_AUTH_SERVER_URL

The URL of the Keycloak authentication server. For example, http://localhost:8080.

String

-

QUARKUS_OIDC_CLIENT_ID

The client ID for the Apicurio Registry REST API.

String

registry-api

APICURIO_UI_AUTH_OIDC_CLIENT_ID

The client ID for the Apicurio Registry web console.

String

apicurio-registry

QUARKUS_OIDC_TLS_TRUST_STORE_FILE

Specifies the file path to the TLS trust store used by Quarkus for securing OpenID Connect (OIDC) communications. The trust store can be populated with the trusted certificates needed to establish secure TLS connections with the OIDC provider.

String

-

QUARKUS_OIDC_TLS_TRUST_STORE_PASSWORD

The password required to access the TLS trust store file.

String

-

APICURIO_AUTH_ROLE_BASED_AUTHORIZATION

Enables or disables role-based authorization.

Boolean

False

Apicurio Registry authentication by using HTTP basic

By default, Apicurio Registry supports authentication by using OpenID Connect. Users or API clients must obtain an access token to make authenticated calls to the Apicurio Registry REST API. However, because some tools do not support OpenID Connect, you can also configure Apicurio Registry to support HTTP basic authentication by setting the following configuration options to true:

Table 13. Configuration for Apicurio Registry HTTP basic authentication
Environment variable Java system property Type Default value

QUARKUS_OIDC_TENANT_ENABLED

quarkus.oidc.tenant-enabled

Boolean

false

APICURIO_AUTHN_BASIC_CLIENT_CREDENTIALS_ENABLED

apicurio.authn.basic-client-credentials.enabled

Boolean

false

Apicurio Registry HTTP basic client credentials cache expiry

You can also configure the HTTP basic client credentials cache expiry time. By default, when using HTTP basic authentication, Apicurio Registry caches JWT tokens, and does not issue a new token when there is no need. You can configure the cache expiry time for JWT tokens, which is set to 10 mins by default.

When using Keycloak, it is best to set this configuration to your Keycloak JWT expiry time minus one minute. For example, if you have the expiry time set to 5 mins in Keycloak, you should set the following configuration option to 4 mins:

Table 14. Configuration for HTTP basic client credentials cache expiry
Environment variable Java system property Type Default value

APICURIO_AUTHN_BASIC_CLIENT_CREDENTIALS_CACHE_EXPIRATION

apicurio.authn.basic-client-credentials.cache-expiration

Integer

10

Apicurio Registry role-based authorization

You can set the following options to true to enable role-based authorization in Apicurio Registry:

Table 15. Configuration for Apicurio Registry role-based authorization
Environment variable Java system property Type Default value

QUARKUS_OIDC_TENANT_ENABLED

quarkus.oidc.tenant-enabled

Boolean

false

APICURIO_AUTH_ROLE_BASED_AUTHORIZATION

apicurio.auth.role-based-authorization

Boolean

false

You can then configure role-based authorization to use roles included in the user’s authentication token (for example, granted when authenticating by using Keycloak), or to use role mappings managed internally by Apicurio Registry.

Use roles assigned in Keycloak

To enable using roles assigned by Keycloak, set the following environment variables:

Table 16. Configuration for Apicurio Registry role-based authorization by using Keycloak
Environment variable Description Type Default

APICURIO_AUTH_ROLE_SOURCE

When set to token, user roles are taken from the authentication token.

String

token

APICURIO_AUTH_ROLES_ADMIN

The name of the role that indicates a user is an admin.

String

sr-admin

APICURIO_AUTH_ROLES_DEVELOPER

The name of the role that indicates a user is a developer.

String

sr-developer

APICURIO_AUTH_ROLES_READONLY

The name of the role that indicates a user has read-only access.

String

sr-readonly

When Apicurio Registry is configured to use roles from Keycloak, you must assign Apicurio Registry users to at least one of the following user roles in Keycloak. However, you can configure different user role names by using the environment variables in Configuration for Apicurio Registry role-based authorization by using Keycloak.

Table 17. Apicurio Registry roles for authentication and authorization
Role name Read artifacts Write artifacts Global rules Description

sr-admin

Yes

Yes

Yes

Full access to all create, read, update, and delete operations.

sr-developer

Yes

Yes

No

Access to create, read, update, and delete operations, except configuring global rules and import/export. This role can configure artifact-specific rules only.

sr-readonly

Yes

No

No

Access to read and search operations only. This role cannot configure any rules.

Manage roles directly in Apicurio Registry

To enable using roles managed internally by Apicurio Registry, set the following environment variable:

Table 18. Configuration for Apicurio Registry role-based authorization by using internal role mappings
Environment variable Description Type Default

APICURIO_AUTH_ROLE_SOURCE

When set to application, user roles are managed internally by Apicurio Registry.

String

token

When using internally managed role mappings, users can be assigned a role by using the /admin/roleMappings endpoint in the Apicurio Registry REST API. For more details, see Apicurio Registry REST API documentation.

Users can be granted exactly one role: ADMIN, DEVELOPER, or READ_ONLY. Only users with admin privileges can grant access to other users.

Apicurio Registry admin-override configuration

Because there are no default admin users in Apicurio Registry, it is usually helpful to configure another way for users to be identified as admins. You can configure this admin-override feature by using the following environment variables:

Table 19. Configuration for Apicurio Registry admin-override
Environment variable Description Type Default

APICURIO_AUTH_ADMIN_OVERRIDE_ENABLED

Enables the admin-override feature.

String

false

APICURIO_AUTH_ADMIN_OVERRIDE_FROM

Where to look for admin-override information. Only token is currently supported.

String

token

APICURIO_AUTH_ADMIN_OVERRIDE_TYPE

The type of information used to determine if a user is an admin. Values depend on the value of the FROM variable, for example, role or claim when FROM is token.

String

role

APICURIO_AUTH_ADMIN_OVERRIDE_ROLE

The name of the role that indicates a user is an admin.

String

sr-admin

APICURIO_AUTH_ADMIN_OVERRIDE_CLAIM

The name of a JWT token claim to use for determining admin-override.

String

org-admin

APICURIO_AUTH_ADMIN_OVERRIDE_CLAIM_VALUE

The value that the JWT token claim indicated by the CLAIM variable must be for the user to be granted admin-override.

String

true

For example, you can use this admin-override feature to assign the sr-admin role to a single user in Keycloak, which grants that user the admin role. That user can then use the /admin/roleMappings REST API (or associated UI) to grant roles to additional users (including additional admins).

Apicurio Registry owner-only authorization

You can set the following options to true to enable owner-only authorization for updates to artifacts or artifact groups in Apicurio Registry:

Table 20. Configuration for owner-only authorization
Environment variable Java system property Type Default value

QUARKUS_OIDC_TENANT_ENABLED

quarkus.oidc.tenant-enabled

Boolean

false

APICURIO_AUTH_OWNER_ONLY_AUTHORIZATION

apicurio.auth.owner-only-authorization

Boolean

false

APICURIO_AUTH_OWNER_ONLY_AUTHORIZATION_LIMIT_GROUP_ACCESS

apicurio.auth.owner-only-authorization.limit-group-access

Boolean

false

When owner-only authorization is enabled, only the user who created an artifact can modify or delete that artifact.

When owner-only authorization and group owner-only authorization are both enabled, only the user who created an artifact group has write access to that artifact group, for example, to add or remove artifacts in that group.

Apicurio Registry authenticated read access

When the authenticated read access option is enabled, Apicurio Registry grants at least read-only access to requests from any authenticated user in the same organization, regardless of their user role.

To enable authenticated read access, you must first enable role-based authorization, and then ensure that the following options are set to true:

Table 21. Configuration for authenticated read access
Environment variable Java system property Type Default value

QUARKUS_OIDC_TENANT_ENABLED

quarkus.oidc.tenant-enabled

Boolean

false

APICURIO_AUTH_AUTHENTICATED_READ_ACCESS_ENABLED

apicurio.auth.authenticated-read-access.enabled

Boolean

false

Apicurio Registry anonymous read-only access

In addition to the two main types of authorization (role-based and owner-based authorization), Apicurio Registry supports an anonymous read-only access option.

To allow anonymous users, such as REST API calls with no authentication credentials, to make read-only calls to the REST API, set the following options to true:

Table 22. Configuration for anonymous read-only access
Environment variable Java system property Type Default value

QUARKUS_OIDC_TENANT_ENABLED

quarkus.oidc.tenant-enabled

Boolean

false

APICURIO_AUTH_ANONYMOUS_READ_ACCESS_ENABLED

apicurio.auth.anonymous-read-access.enabled

Boolean

false

Additional resources

Configuring Apicurio Registry Cross-Origin Resource Sharing (CORS)

Cross-Origin Resource Sharing (CORS) is a browser security feature that controls how web applications running at one origin can request resources from a different origin. Apicurio Registry includes built-in CORS support that you can configure to allow the Apicurio Registry web console or other client applications to access the Apicurio Registry REST API from different domains.

CORS configuration in Apicurio Registry is based on the underlying Quarkus HTTP CORS filter. By default, Apicurio Registry enables CORS and allows requests from http://localhost:8888 and http://127.0.0.1:8888 for local development.

Prerequisites
  • Apicurio Registry is installed and running.

Procedure

Configure the following environment variables to customize CORS behavior for your Apicurio Registry deployment:

Table 23. Configuration for Apicurio Registry CORS
Environment variable Description Default

QUARKUS_HTTP_CORS

Enables or disables the CORS filter. Set to true to enable CORS support.

true

QUARKUS_HTTP_CORS_ORIGINS

A comma-separated list of allowed origins for CORS requests. Use * to allow all origins, or specify explicit origins such as https://my-ui.example.com. For multiple origins, use a comma-separated list, for example: https://ui1.example.com,\https://ui2.example.com.

http://localhost:8888,http://127.0.0.1:8888

QUARKUS_HTTP_CORS_METHODS

A comma-separated list of HTTP methods allowed for CORS requests.

GET,PUT,POST,PATCH,DELETE,OPTIONS

QUARKUS_HTTP_CORS_HEADERS

A comma-separated list of HTTP headers allowed in CORS requests. This includes custom Apicurio Registry headers used for artifact metadata.

See note below

The default allowed headers include: x-registry-name, x-registry-name-encoded, x-registry-description, x-registry-description-encoded, x-registry-version, x-registry-artifactid, x-registry-artifacttype, x-registry-hash-algorithm, x-registry-content-hash, access-control-request-method, access-control-allow-credentials, access-control-allow-origin, access-control-allow-headers, authorization, content-type, content-encoding, user-agent.
Example: Allow all origins (development only)
environment:
  QUARKUS_HTTP_CORS_ORIGINS: "*"
Using * to allow all origins is not recommended for production deployments. Always specify explicit allowed origins in production environments.
Example: Allow specific origins (production)
environment:
  QUARKUS_HTTP_CORS_ORIGINS: "https://registry-ui.example.com,https://admin.example.com"
Example: Kubernetes operator configuration

If you are using the Apicurio Registry Operator, you can configure CORS in the ApicurioRegistry3 custom resource:

apiVersion: registry.apicur.io/v1
kind: ApicurioRegistry3
metadata:
  name: my-registry
spec:
  app:
    env:
      - name: QUARKUS_HTTP_CORS_ORIGINS
        value: https://ui.example.com
When using the Apicurio Registry Operator with an Ingress configured for the UI component, the operator automatically configures CORS allowed origins based on the Ingress host. If you explicitly set QUARKUS_HTTP_CORS_ORIGINS in the CR, your configuration takes precedence.
Additional resources