How to Set Up Keycloak SSO for Apache Superset (OpenID Connect)

Keycloak shows up wherever an organisation wants to own its identity layer: on-premise deployments, regulated environments, platforms that broker several upstream directories into one login, or simply teams that prefer open source end to end. Superset fits that stack naturally, and Flask-AppBuilder ships a Keycloak handler so the integration is configuration rather than code.

There is one trap that catches almost everyone. FAB’s Keycloak handler reads a groups claim from the user info endpoint to decide which Superset roles to assign, and a fresh Keycloak client does not include that claim. Logins succeed, everyone gets the default role, and the failure is silent. This guide fixes that in Step 1 so you never see it.

For the general mechanics of Superset authentication, reverse-proxy settings and the break-glass procedure, see our Okta guide; they apply here unchanged. This post covers what is specific to Keycloak. Written against Superset 4.x and Keycloak 24, applicable to Keycloak 17 and later (the versions without the /auth URL prefix).

Step 1: Create the Keycloak client

In the Keycloak admin console, inside the realm your users live in:

  • Go to Clients and choose Create client.
  • Client type: OpenID Connect. Client ID: superset. Give it a name and description. Next.
  • Client authentication: On (this makes it a confidential client with a secret). Authorization: Off. Under Authentication flow, keep Standard flow ticked and untick everything else; Superset only needs the authorization-code flow. Next.
  • Root URL: https://superset.example.com. Valid redirect URIs: https://superset.example.com/oauth-authorized/keycloak. Valid post logout redirect URIs: https://superset.example.com/login/. Web origins: https://superset.example.com. Save.
  • Open the Credentials tab and copy the Client secret.

The last segment of the redirect URI, keycloak, must match the provider name in superset_config.py.

Add the groups mapper

This is the step that makes role mapping work.

  • On the client, open Client scopes and click the dedicated scope named superset-dedicated.
  • Choose Add mapper, then By configuration, then Group Membership.
  • Name: groups. Token Claim Name: groups. Full group path: Off (so the claim contains superset-admins, not /bi/superset-admins). Add to ID token: On. Add to access token: On. Add to userinfo: On. Save.

FAB reads groups from the userinfo endpoint, so “Add to userinfo” is the one that matters. Turning the other two on costs nothing and helps debugging.

If you prefer Keycloak realm roles or client roles to groups, add a User Realm Role or User Client Role mapper instead, with Token Claim Name set to groups so FAB picks it up without a custom security manager. The mapping keys in Superset then become role names rather than group names.

Create groups and assign users

Under Groups, create superset-admins, superset-analysts and superset-viewers, and add users. If Keycloak brokers an upstream directory (LDAP, Active Directory, another OIDC provider), map the upstream groups into these through the identity provider’s mappers, so the Superset-facing group names stay stable even if the upstream changes.

Optionally, restrict who can log in at all: under the client’s Advanced tab, or through a client policy, require membership of a superset-users group. Users outside it are refused by Keycloak, which is preferable to being created in Superset with no useful role.

Step 2: Configure Superset

python
import os
from flask_appbuilder.security.manager import AUTH_OAUTH

KEYCLOAK_BASE_URL = os.environ["KEYCLOAK_BASE_URL"]   # e.g. https://sso.example.com
KEYCLOAK_REALM = os.environ["KEYCLOAK_REALM"]         # e.g. corp
KEYCLOAK_REALM_URL = f"{KEYCLOAK_BASE_URL}/realms/{KEYCLOAK_REALM}"

AUTH_TYPE = AUTH_OAUTH

OAUTH_PROVIDERS = [
    {
        "name": "keycloak",
        "icon": "fa-key",
        "token_key": "access_token",
        "remote_app": {
            "client_id": "superset",
            "client_secret": os.environ["KEYCLOAK_CLIENT_SECRET"],
            "client_kwargs": {"scope": "openid email profile"},
            "api_base_url": f"{KEYCLOAK_REALM_URL}/protocol/",
            "server_metadata_url": f"{KEYCLOAK_REALM_URL}/.well-known/openid-configuration",
            "access_token_url": f"{KEYCLOAK_REALM_URL}/protocol/openid-connect/token",
            "authorize_url": f"{KEYCLOAK_REALM_URL}/protocol/openid-connect/auth",
            "request_token_url": None,
        },
    }
]

AUTH_USER_REGISTRATION = True
AUTH_USER_REGISTRATION_ROLE = "Gamma"

# Keycloak group (or role) name -> Superset roles
AUTH_ROLES_MAPPING = {
    "superset-admins": ["Admin"],
    "superset-analysts": ["Alpha", "sql_lab"],
    "superset-viewers": ["Gamma"],
}
AUTH_ROLES_SYNC_AT_LOGIN = True

ENABLE_PROXY_FIX = True
SESSION_COOKIE_SECURE = True
SESSION_COOKIE_SAMESITE = "Lax"

Details worth knowing:

  • The provider name keycloak selects FAB’s handler for Keycloak 17 and later. For Keycloak 16 and earlier, whose URLs include /auth/realms/..., use the provider name keycloak_before_17 and add the /auth prefix to every URL.
  • api_base_url ends at /protocol/ because FAB’s handler requests openid-connect/userinfo relative to it. Get this wrong and the login completes but the profile fetch 404s.
  • The scope does not need groups; the mapper puts groups into userinfo regardless of scope. If you configured the mapper on a separate client scope rather than the dedicated one, add that scope’s name here.
  • FAB’s Keycloak handler returns preferred_username as the Superset username. Keycloak usernames are stable, so this is a safe default.

Realm roles mapped — now what governs dashboards?

OIDC gets people logged in; role sync, token lifetime, and RBAC design decide what they can see once they’re in. A Superset Architecture Review covers the part after the login screen.

Book a Superset Architecture Review

Step 3: Test

Restart Superset and open the login page. You should see a single “Sign in with keycloak” button.

  • Log in as a member of superset-admins. Check Settings, List Users: the user should exist with the Admin role.
  • Confirm the groups claim is actually arriving. In Keycloak, open Clients, superset, Client scopes, Evaluate, pick a user and look at Generated user info. The groups array should be present. If it is not, the mapper is on the wrong scope or “Add to userinfo” is off.
  • Log in as a viewer from a private window and confirm the restricted view.
  • Move a user between groups, sign out and in, and confirm the role changes.

Troubleshooting

Symptom Cause Fix
Every user gets the Gamma role only No groups claim in userinfo Add the Group Membership mapper with “Add to userinfo” on; check with Client scopes, Evaluate
Group names in the claim look like /bi/superset-admins Full group path is on Turn it off, or use the full path as the key in AUTH_ROLES_MAPPING
Invalid parameter: redirect_uri from Keycloak Redirect URI not in the client’s valid list, or scheme mismatch Add the exact URI; enable ENABLE_PROXY_FIX
404 after login, log mentions userinfo api_base_url wrong, usually missing /protocol/ or using the pre-17 /auth prefix Correct the URL or switch to keycloak_before_17
invalid_client Client authentication off (public client), or wrong secret Turn client authentication on and copy the secret from Credentials
mismatching_state Session cookie not sent on callback SESSION_COOKIE_SAMESITE = "Lax", SESSION_COOKIE_SECURE matching the scheme
Login loops back to Keycloak Keycloak requires a consent or a required action (update password, verify email) the user has not completed Complete the action in Keycloak, or disable consent required on the client
Works for local Keycloak users, not for brokered users Upstream identity provider mappers do not populate the groups Add identity provider mappers to place brokered users into the Superset groups

Operational notes

  • Realm boundaries. Superset can only authenticate users from the realm in its URLs. If different business units live in different realms, either broker them into one realm or configure one provider entry per realm in OAUTH_PROVIDERS; the login page then shows one button per realm.
  • Session length. Keycloak’s SSO session settings and Superset’s PERMANENT_SESSION_LIFETIME are independent. Disabling a user in Keycloak stops new Superset logins but not the current session; keep Superset’s lifetime short.
  • Back-channel logout. Keycloak can notify clients when a user logs out centrally. FAB does not consume that notification, so central logout does not end the Superset session. Rely on session lifetime instead.
  • Secret rotation. Regenerate the client secret in Keycloak and update Superset’s environment in one change window. There is no grace period for two valid secrets on a single client.
  • Air-gapped deployments. Everything here works without internet access. Both Superset and Keycloak must resolve each other’s hostnames, and Superset must trust the certificate Keycloak presents. Mount your internal CA bundle into the Superset container and point REQUESTS_CA_BUNDLE at it.

Frequently Asked Questions

Why does every Superset user get the Gamma role after Keycloak SSO?

Because the groups claim is missing from the userinfo response. A fresh Keycloak client does not emit it, Flask-AppBuilder finds no role keys, nothing matches AUTH_ROLES_MAPPING, and AUTH_USER_REGISTRATION_ROLE applies to everyone. Add the Group Membership mapper to the client’s dedicated scope with Add to userinfo turned on, then confirm it under Clients, superset, Client scopes, Evaluate before changing anything in Superset.

Which provider name should I use for Keycloak in OAUTH_PROVIDERS?

Use keycloak for Keycloak 17 and later, whose URLs are /realms/<realm>/.... For Keycloak 16 and earlier, use keycloak_before_17 and add the /auth prefix to every URL. The provider name also has to match the last segment of the redirect URI registered on the client, so changing one means changing the other.

Can Superset authenticate users from more than one Keycloak realm?

Not through a single provider entry, because the realm is baked into the URLs. Either broker the other directories into one realm through Keycloak’s identity provider mappers, which is the cleaner option, or add one entry per realm to OAUTH_PROVIDERS. The second approach puts one sign-in button per realm on the Superset login page, which users find confusing beyond two.

Does Keycloak back-channel logout end the Superset session?

No. Keycloak can notify clients when a user logs out centrally, but Flask-AppBuilder does not consume that notification, so a central logout leaves the Superset session running until it expires. The same applies to disabling a user: new logins stop immediately, the current session does not. Keep PERMANENT_SESSION_LIFETIME to a working day or less and treat it as the real revocation window.

Build It With Andolasoft

Once Superset trusts Keycloak, the role mapping above becomes the front door to everything else: which roles reach which datasets, how row-level security narrows them, and who gets SQL Lab. A Superset Architecture Review looks at authentication, RBAC and RLS together, alongside caching, async queries and upgrade readiness, and delivers a written report in five working days. For a self-hosted platform that stays patched and monitored without your team on call, air-gapped deployments included, see Managed Support, and for the wider set of controls, our post on governance and security for Superset deployments.

If you are running Keycloak in front of a self-hosted Superset and the group mapping has never quite been right, talk to us. We will trace a real login end to end, show you where the claim is being lost, and give you a fixed-scope plan for the role model behind it.