Skip to content

Availability

This feature is available in Enterprise Edition and Cloud. Not available in Open Source.

Keycloak SSO with Dex

Configure Keycloak as the upstream identity provider for OpenObserve Enterprise, brokered through Dex.

Overview

OpenObserve Enterprise uses Dex as its built-in OIDC broker for SSO. Dex does not store users itself. It delegates authentication to upstream connectors. In this setup, Keycloak is that upstream: users clicking Login with SSO on the OpenObserve login page are redirected through Dex to the Keycloak login page, then back into OpenObserve once authenticated.

User > OpenObserve UI > Dex > Keycloak (login page)
              ^           ^          |
              |_ session _|_ token _|

OpenObserve only talks to Dex, so the OpenObserve configuration stays the same regardless of the IdP behind it. Keycloak remains the source of truth for users, groups, and MFA policies. Additional connectors (e.g. Google) can coexist on the same Dex login screen.

Environment used in this guide

This guide uses cluster1.example.com as the example domain. Replace hostnames, realm name, and secrets with your own values throughout.

Component URL
OpenObserve https://dev.cluster1.example.com
Dex (issuer) https://dev-dex.cluster1.example.com/dex
Keycloak https://keycloak.cluster1.example.com
Keycloak realm o2

Note: The Dex issuer includes the /dex path. This matters for both the Keycloak redirect URI and the OpenObserve O2_DEX_BASE_URL.

Prerequisites

  • OpenObserve Enterprise deployed (SSO/Dex is an Enterprise feature), https://dev.cluster1.example.com
  • A Dex deployment reachable by both the user's browser and OpenObserve
  • A Keycloak instance (v26.x in this setup) with admin access
  • Local dev: docker-compose.yml (Keycloak 26.1 + Postgres 16)
  • Cluster: k8s-deployment.yml + httproute.yml (Envoy Gateway route on keycloak.cluster1.example.com)
  • All three endpoints served over HTTPS with mutually trusted certificates
  • Network connectivity: browser to all three; Dex to Keycloak; OpenObserve to Dex

Part 1: Deploy Keycloak

Skip this part if Keycloak is already running.

Local (Docker Compose):

cd keycloak/
docker compose up -d          # uses .env for admin credentials and hostname

Kubernetes (dev cluster):

kubectl apply -f k8s-deployment.yml   # namespace, Postgres, Keycloak Deployment/Service
kubectl apply -f httproute.yml        # Envoy Gateway HTTPRoute for the internal hostname

Keycloak runs with --proxy-headers=xforwarded and KC_HOSTNAME=keycloak.cluster1.example.com so that issued tokens carry the public URL, not the in-cluster one. Verify readiness:

kubectl -n keycloak get pods
kubectl -n keycloak logs deploy/keycloak | grep "Listening"

Part 2: Configure Keycloak

Step 2.1: Log in to the Keycloak Admin Console

  1. Open https://keycloak.cluster1.example.com in your browser.
  2. Click Administration Console and log in with the admin credentials (from the keycloak-secret or .env).
  3. In the top-left realm selector, choose the o2 realm. If it does not exist, click Create realm, name it o2, and save.

Step 2.2: Create a client for Dex

  1. In the left menu, go to Clients → click Create client.
  2. On the General settings page:
  3. Client type: OpenID Connect
  4. Client ID: dex
  5. Click Next.
  6. On the Capability config page:
  7. Enable Client authentication (confidential client).
  8. Ensure Standard flow is checked (Authorization Code flow).
  9. Click Next.
  10. On the Login settings page:
  11. Valid redirect URIs: https://dev-dex.cluster1.example.com/dex/callback (note the /dex path segment; Dex's callback lives under its issuer path)
  12. Web origins: https://dev-dex.cluster1.example.com (optional)
  13. Click Save.

Step 2.3: Copy the client secret

  1. Open the newly created dex client.
  2. Go to the Credentials tab.
  3. Copy the Client Secret. Paste it into the Dex connector config in Part 3.

Step 2.4: Expose group membership in tokens (optional, for OpenObserve RBAC)

Skip this step if all SSO users land in the default org with the default role (O2_DEX_DEFAULT_ORG / O2_DEX_DEFAULT_ROLE).

  1. Go to Client scopesCreate client scope.
  2. Name: groups
  3. Type: Default
  4. Protocol: OpenID Connect
  5. Save.
  6. Open the new groups scope → Mappers tab → Configure a new mapper → choose Group Membership.
  7. Name: groups
  8. Token Claim Name: groups
  9. Full group path: OFF
  10. Add to ID token: ON
  11. Add to userinfo: ON
  12. Save.
  13. Go back to ClientsdexClient scopes tab → Add client scope → select groups → add as Default.

The Dex connector already has insecureEnableGroups: true and getUserInfo: true (Part 3), so the groups claim flows through Dex into the token OpenObserve receives.

Step 2.5: Create a test user

  1. Go to UsersAdd user.
  2. Set a Username and Email (the email becomes the OpenObserve user identity), then save.
  3. Open the Credentials tab → Set password → disable Temporary → save.

Step 2.6: Verify the realm's OIDC discovery endpoint

curl https://keycloak.cluster1.example.com/realms/o2/.well-known/openid-configuration

Confirm the JSON loads and note the issuer value. The Dex connector must use it exactly as shown:

https://keycloak.cluster1.example.com/realms/o2

Part 3: Configure Dex

Step 3.1: Add the Keycloak connector

The dev configuration lives in dev-dex-config.yaml. The relevant pieces:

issuer: https://dev-dex.cluster1.example.com/dex

storage:
  type: kubernetes
  config:
    inCluster: true

web:
  http: 0.0.0.0:5556

frontend:
  issuer: OpenObserve
  logoURL: https://cloud.openobserve.ai/web/src/assets/images/common/open_observe_logo.svg

oauth2:
  responseTypes: [code]
  skipApprovalScreen: true

expiry:
  idTokens: 18000m
  refreshTokens:
    validIfNotUsedFor: 30m

connectors:
  - type: oidc
    id: keycloak
    name: O2Keycloak            # label shown on the Dex login screen
    config:
      # Must exactly match the issuer from Step 2.6
      issuer: https://keycloak.cluster1.example.com/realms/o2
      clientID: dex
      clientSecret: <KEYCLOAK_CLIENT_SECRET_FROM_STEP_2.3>
      redirectURI: https://dev-dex.cluster1.example.com/dex/callback
      scopes:
        - profile
        - email
      insecureEnableGroups: true      # forward the groups claim from Keycloak
      getUserInfo: true               # fetch extra claims from the userinfo endpoint
      userNameKey: preferred_username # use the Keycloak username as identity

# OpenObserve is the (only) downstream client of Dex
staticClients:
  - id: o2-client
    name: o2-client
    secret: <O2_CLIENT_SECRET>
    redirectURIs:
      - https://dev.cluster1.example.com/config/redirect

Two values here are OpenObserve conventions:

  • staticClients.id: o2-client must match O2_DEX_CLIENT_ID in the OpenObserve config (Part 4).
  • Redirect URI /config/redirect is OpenObserve's fixed OIDC callback path. Only the host changes per environment.

Tip: Generate the static client secret with openssl rand -hex 32. Store secrets in Kubernetes Secrets rather than version control. The checked-in dev config is for the internal dev cluster only.

Step 3.2: Deploy or restart Dex

In the OpenObserve Enterprise Helm deployment, this config goes under the Dex section of the chart values. After updating:

helm upgrade -n openobserve o2 openobserve/openobserve -f values.yaml
# or, if Dex is deployed standalone:
kubectl -n dex rollout restart deploy/dex

Step 3.3: Verify Dex is up

curl https://dev-dex.cluster1.example.com/dex/.well-known/openid-configuration

You should get JSON with "issuer": "https://dev-dex.cluster1.example.com/dex". Check logs:

kubectl logs -n dex deploy/dex | grep -i connector

A healthy startup lists the keycloak connector with no failed to initialize connector errors.

Part 4: Configure OpenObserve

Set the Dex-related environment variables on the OpenObserve deployment (in Helm: the enterprise/auth values; standalone: container env):

O2_DEX_ENABLED=true
O2_DEX_CLIENT_ID=o2-client
O2_DEX_CLIENT_SECRET=<O2_CLIENT_SECRET>            # matches staticClients secret in Dex
O2_DEX_BASE_URL=https://dev-dex.cluster1.example.com/dex
O2_CALLBACK_URL=https://dev.cluster1.example.com/web/cb
O2_DEX_REDIRECT_URL=https://dev.cluster1.example.com/config/redirect

# Where SSO users land if no group mapping applies
O2_DEX_DEFAULT_ORG=default
O2_DEX_DEFAULT_ROLE=user

If you enabled the groups mapper (Step 2.4) and want Keycloak groups to drive OpenObserve org/role assignment, configure the group-claim variables (O2_DEX_GROUP_CLAIM, O2_DEX_GROUP_ATTRIBUTE, O2_DEX_ROLE_ATTRIBUTE) per the OpenObserve SSO docs. The parsing convention differs between LDAP-style DNs and flat group names, so match it to how the groups appear in the token.

Restart OpenObserve after changing env vars:

kubectl -n openobserve rollout restart statefulset/o2-openobserve-router   # or the relevant workload

Part 5: Test the Integration

  1. Open https://dev.cluster1.example.com and click Login with SSO.
  2. The Dex login screen appears (OpenObserve-branded). Choose O2Keycloak. With skipApprovalScreen: true and a single connector, Dex redirects straight through.
  3. You are redirected to the Keycloak login page for the o2 realm.
  4. Log in with the test user from Step 2.5.
  5. You land back in OpenObserve, authenticated, as a member of O2_DEX_DEFAULT_ORG with O2_DEX_DEFAULT_ROLE (or the mapped role).
  6. Verify identity: in OpenObserve, check the user menu or IAMUsers for the new user. Confirm the email matches Keycloak. To inspect claims, decode the ID token and confirm email, preferred_username, and (if configured) groups are present.

Troubleshooting

Symptom Likely cause Fix
failed to initialize connector: issuer did not match in Dex logs Keycloak's issuer differs from Dex config Copy the issuer verbatim from the discovery URL (Step 2.6). Check KC_HOSTNAME and proxy settings. With Envoy Gateway, Keycloak needs --proxy-headers=xforwarded.
"Invalid redirect_uri" on the Keycloak page Redirect URI mismatch Ensure https://dev-dex.cluster1.example.com/dex/callback (with /dex) is listed exactly in the Keycloak dex client.
OpenObserve shows an OAuth error after Keycloak login o2-client mismatch between Dex and OpenObserve O2_DEX_CLIENT_ID and O2_DEX_CLIENT_SECRET must match the Dex staticClients entry. O2_DEX_REDIRECT_URL must match the registered redirect URI.
Login with SSO button missing in OpenObserve Dex not enabled or not Enterprise Set O2_DEX_ENABLED=true and confirm the Enterprise image/license is in use. Restart the router.
x509: certificate signed by unknown authority in Dex logs Dex does not trust Keycloak's TLS cert Mount the CA into Dex and set rootCAs: [/etc/dex/ca.pem] in the connector config.
User lands in the wrong org/role Group mapping not applied Re-check Step 2.4, insecureEnableGroups: true, and the O2_DEX_GROUP_* variables. Decode the token to confirm the groups claim shape.
Login loops or immediately fails Wrong Keycloak client secret in Dex Re-copy the secret from Keycloak's Credentials tab into the connector config and restart Dex.
Keycloak issues tokens with the in-cluster URL Frontend hostname not set Set KC_HOSTNAME=keycloak.cluster1.example.com (already in k8s-deployment.yml).

Security Checklist

  • [ ] HTTPS enforced on OpenObserve, Dex, and Keycloak
  • [ ] Dex connector and o2-client secrets stored in Kubernetes Secrets, not committed to git
  • [ ] o2-client secret randomly generated (32+ bytes), not the default example-app-secret
  • [ ] Keycloak dex client restricted to the exact /dex/callback redirect URI (no wildcards)
  • [ ] Token lifetimes reviewed: Keycloak (Realm Settings > Tokens) and Dex expiry (idTokens: 18000m is dev-friendly; tighten for production)
  • [ ] MFA/OTP enabled in the Keycloak o2 realm for production (Authentication > Required actions)
  • [ ] Default Keycloak admin credentials (admin/admin in the dev manifests) rotated
  • [ ] Unused Dex connectors and static clients removed

Reference URLs

Component Endpoint
OpenObserve https://dev.cluster1.example.com
OpenObserve OIDC callback (register in Dex) https://dev.cluster1.example.com/config/redirect
Dex discovery https://dev-dex.cluster1.example.com/dex/.well-known/openid-configuration
Dex callback (register in Keycloak) https://dev-dex.cluster1.example.com/dex/callback
Keycloak discovery https://keycloak.cluster1.example.com/realms/o2/.well-known/openid-configuration
OpenObserve SSO docs https://openobserve.ai/docs/sso/
Dex OIDC connector docs https://dexidp.io/docs/connectors/oidc/
Keycloak docs https://www.keycloak.org/documentation

Need help:

Community Slack

GitHub issues