Migrate to RunxBuild today!! Deploy More while Spending Less

Calculate your savings
unxBuild

Apache Superset and Keycloak: SSO With OIDC, Role Mapping and Proxy Fixes

Sean

Platform Writer

Sep 30, 2026
11 min read

To put Keycloak in front of Apache Superset, create a confidential OpenID Connect client in Keycloak, then set AUTH_TYPE to AUTH_OAUTH in superset_config.py with a Keycloak provider entry and a role mapping, so users sign in through Keycloak and land in the Superset role you assigned them there.

Apache Superset and Keycloak: SSO With OIDC, Role Mapping and Proxy Fixes

Superset is built on Flask AppBuilder, and Flask AppBuilder already knows how to speak OAuth and OpenID Connect. That is the good news. The bad news is that the failure modes are quiet: a redirect URI mismatch that only appears behind a proxy, a role claim that never reaches Superset, and a logout button that leaves the Keycloak session standing. This guide builds the integration in the order that avoids those, with the configuration you can paste and the reasons behind each line. It assumes you already have a running Superset and a Keycloak realm, and ends with deployment notes for running both.

Table of contents

How the login flow works

Superset does not talk to Keycloak directly through a plugin. Flask AppBuilder implements an OAuth client using Authlib. When a user clicks the Keycloak button on the login page, Superset redirects the browser to Keycloak’s authorize endpoint. The user signs in there, Keycloak redirects back to Superset at a callback URL with a code, and Superset exchanges that code for tokens, then asks Keycloak who the user is.

The callback path is fixed by Flask AppBuilder and includes the provider name you choose: /oauth-authorized/keycloak if your provider is called keycloak. That URL must be registered in Keycloak exactly, and it must be the URL as the browser sees it. Most of the trouble people report comes down to that sentence.

The other thing to understand is what happens to identity after login. Superset keeps its own user table. On first login it can create a user from the claims Keycloak returns, and on every login it can sync that user’s roles from a claim. So there are three separate things to get right: authentication (the redirect dance), user creation (whether Superset registers new users automatically), and authorisation (which Superset role the person ends up with).

Decide the authorisation model before you configure anything. The clean approach is to manage roles in Keycloak and let Superset follow: you create roles such as superset_admin, superset_alpha and superset_gamma there, assign users or groups to them, and map those names to Superset’s built-in Admin, Alpha and Gamma roles in configuration. Managing roles in both places invites drift.

Create the Keycloak client

In the Keycloak admin console, select your realm, open Clients and create a new one. Use OpenID Connect as the client type and give it a client ID such as superset. Turn Client authentication on, which makes it a confidential client with a secret, and leave Standard flow enabled. You do not need the implicit or direct access grant flows, so switch them off.

Then set the URLs. Get these exactly right, because a mismatch produces an Invalid parameter: redirect_uri error page from Keycloak.

Root URL:              https://superset.example.com
Valid redirect URIs:   https://superset.example.com/oauth-authorized/keycloak
Valid post logout URIs: https://superset.example.com/*
Web origins:           https://superset.example.com

Copy the client secret from the Credentials tab. It goes into an environment variable in Superset, not into a file you commit.

Now the roles. Under the client’s Roles tab, or as realm roles, create the three names you decided on. Assign them to users or, better, to groups. Then make sure the role list is actually put into the token, which is not the default. Open Client scopes, find the dedicated scope for your client, and add a mapper: User Client Role if you used client roles, or User Realm Role if you used realm roles. Set the token claim name to roles and enable Add to ID token, Add to access token and Add to userinfo. If the last one is off, Superset asks for user info and gets no roles back, which is exactly the situation described in the Superset GitHub discussion about assigning roles to Keycloak users.

Configure superset_config.py

Superset reads its authentication settings from superset_config.py, which it finds through the SUPERSET_CONFIG_PATH environment variable or from the default location in the container. Start with the provider definition.

import os
from flask_appbuilder.security.manager import AUTH_OAUTH

KC_BASE = os.environ["KEYCLOAK_URL"]   # https://auth.example.com
KC_REALM = os.environ["KEYCLOAK_REALM"]
OIDC = f"{KC_BASE}/realms/{KC_REALM}/protocol/openid-connect"

AUTH_TYPE = AUTH_OAUTH
SECRET_KEY = os.environ["SUPERSET_SECRET_KEY"]

OAUTH_PROVIDERS = [
    {
        "name": "keycloak",
        "icon": "fa-key",
        "token_key": "access_token",
        "remote_app": {
            "client_id": "superset",
            "client_secret": os.environ["KEYCLOAK_CLIENT_SECRET"],
            "server_metadata_url": f"{KC_BASE}/realms/{KC_REALM}/.well-known/openid-configuration",
            "api_base_url": OIDC,
            "client_kwargs": {"scope": "openid email profile"},
        },
    }
]

The provider name here must match the last segment of the redirect URI you registered. The server_metadata_url lets Authlib discover the authorize and token endpoints, so you do not have to list them by hand. Keycloak 17 and later dropped the /auth prefix from URLs, so if you are on an older Keycloak, the base path includes it.

Next, tell Superset what to do with people it has not seen before, and how to keep their roles in sync.

AUTH_USER_REGISTRATION = True
AUTH_USER_REGISTRATION_ROLE = "Public"
AUTH_ROLES_SYNC_AT_LOGIN = True
AUTH_ROLES_MAPPING = {
    "superset_admin": ["Admin"],
    "superset_alpha": ["Alpha"],
    "superset_gamma": ["Gamma", "sql_lab"],
}

AUTH_USER_REGISTRATION creates the user on first login. AUTH_USER_REGISTRATION_ROLE is the fallback when no mapped role arrives, and Public is a safe choice because it grants nothing useful by default. AUTH_ROLES_SYNC_AT_LOGIN means a role change in Keycloak takes effect at the user’s next login, rather than sticking with whatever they had on day one.

Passing roles through a custom security manager

Configuration alone is often enough for authentication but not for roles, because Superset has to be told how to turn Keycloak’s response into a user record. That is the job of oauth_user_info on a custom security manager. Writing it yourself makes the mapping visible and removes guesswork about which claim is used.

from superset.security import SupersetSecurityManager

class KeycloakSecurityManager(SupersetSecurityManager):
    def oauth_user_info(self, provider, response=None):
        if provider == "keycloak":
            remote = self.appbuilder.sm.oauth_remotes[provider]
            data = remote.get("userinfo").json()
            return {
                "username": data.get("preferred_username"),
                "first_name": data.get("given_name", ""),
                "last_name": data.get("family_name", ""),
                "email": data.get("email", ""),
                "role_keys": data.get("roles", []),
            }
        return {}

CUSTOM_SECURITY_MANAGER = KeycloakSecurityManager

The role_keys value is the list that AUTH_ROLES_MAPPING is matched against. If you named your mapper claim roles, this reads it. If you used groups instead, change the key. When roles seem to be ignored, the debugging order is short: decode the access token and confirm the claim is present, call the userinfo endpoint with it and confirm the claim is there too, then log data inside oauth_user_info to see what Superset actually receives.

Version behaviour differs between Flask AppBuilder releases, and newer ones handle Keycloak-flavoured providers with built-in logic. If your version already maps roles correctly with just the configuration, you can drop the custom class. If it does not, the class above is the reliable route, and the Superset discussion on assigning roles from Keycloak documents the same approach.

Redirect URI pitfalls behind a proxy

This is where most real deployments break. Superset builds the redirect_uri it sends to Keycloak from the request it received. Behind a TLS-terminating reverse proxy, Superset sees a plain HTTP request to an internal hostname, so it sends redirect_uri=http://superset:8088/oauth-authorized/keycloak. Keycloak compares that with your registered https URL, and rejects it.

The fix has two halves. First, make the proxy send the original scheme and host. For a typical proxy that means forwarding the X-Forwarded-Proto, X-Forwarded-Host and X-Forwarded-For headers. Second, tell Superset to trust them.

ENABLE_PROXY_FIX = True
PROXY_FIX_CONFIG = {
    "x_for": 1,
    "x_proto": 1,
    "x_host": 1,
    "x_port": 1,
    "x_prefix": 1,
}

Set the counts to the number of proxies in front of Superset. Too high and headers can be spoofed; too low and the scheme is wrong. If you serve Superset under a sub-path, x_prefix also matters.

The mirror image exists on the Keycloak side. Keycloak behind a proxy needs to know its public hostname and to trust forwarded headers, or it generates internal URLs in its metadata document and the browser is sent somewhere unreachable. Set its hostname option and its proxy headers option so its discovery document advertises public addresses.

A last container-specific trap: Superset fetches the metadata URL server-side, while the browser follows the authorize URL. In Docker networks the two can need different hostnames, and Keycloak refuses tokens whose issuer differs from what it advertises. Use one public hostname for both, reachable from inside the network.

Logout, testing and deployment notes

Logging out of Superset ends the Superset session only. The Keycloak session is still alive, so clicking the login button again signs you straight back in without a password. For shared machines, that is a problem. The fix is a custom logout that redirects to Keycloak’s end_session_endpoint, with post_logout_redirect_uri pointing back at Superset, and it is worth adding for any deployment where more than one person uses a browser.

Test with three accounts, one per role, in private windows. Confirm each lands with the right permissions, then remove a role in Keycloak, log in again and confirm access shrank. That last check proves role sync works, which is the part nobody tests until an auditor asks.

For deployment, both products are containers with real state. Superset needs a metadata database, and Postgres is the standard choice rather than the SQLite default, plus a stable SECRET_KEY that never changes between restarts, or existing sessions and encrypted fields break. Keycloak needs its own database too. Put every secret in environment variables. The Metabase deployment post shows the same pattern for another BI tool, and the SAML authentication troubleshooting post is useful if your identity provider speaks SAML rather than OIDC.

How this fits the rest of the stack

Running Superset and Keycloak yourself means two Docker services and two Postgres databases, and that is a real bill worth estimating first. The RunxBuild hosting calculator prices services and managed databases as separate line items, so you can see the pair before you commit. Superset is a Docker image, so deploying it is a matter of pointing a service at the image, setting SUPERSET_CONFIG_PATH and the secrets, and giving it a managed Postgres for metadata. Create the services and database from the RunxBuild dashboard, read the Docker services documentation for ports and environment variables, and the database documentation for the connection string. Custom domains and certificates are handled on the platform, which removes a whole class of redirect URI mistakes: the address you register in Keycloak is the address users actually see.

Useful related references:

FAQ

How do I set up SSO for Apache Superset with Keycloak?

Create a confidential OpenID Connect client in Keycloak with the redirect URI /oauth-authorized/keycloak on your Superset domain. Then set AUTH_TYPE to AUTH_OAUTH in superset_config.py, add a keycloak entry to OAUTH_PROVIDERS with the client ID, secret and metadata URL, and map Keycloak roles to Superset roles.

Why do I get Invalid parameter: redirect_uri from Keycloak?

The redirect URI Superset sent does not match one registered in the client. Behind a TLS proxy Superset often sends an internal http address. Forward the X-Forwarded headers from the proxy, set ENABLE_PROXY_FIX and PROXY_FIX_CONFIG in superset_config.py, and register the exact https callback URL.

How do I map Keycloak roles to Superset roles?

Add a role mapper to the Keycloak client scope with claim name roles, included in the userinfo response. In superset_config.py set AUTH_ROLES_MAPPING to link those names to Admin, Alpha or Gamma, enable AUTH_ROLES_SYNC_AT_LOGIN, and make sure oauth_user_info returns them as role_keys.

Why does Superset not log me out of Keycloak?

Superset logout clears only the Superset session. The Keycloak session remains, so the next login is silent. Add a custom logout route that redirects the browser to Keycloak’s end_session_endpoint with a post logout redirect back to Superset.

Does Superset support OIDC directly?

Superset supports OAuth and OpenID Connect through Flask AppBuilder, using AUTH_OAUTH with a provider entry. There is no separate Keycloak plugin. You configure the provider in superset_config.py, and for role handling you may add a custom security manager that reads the claims.

#apache superset keycloak#Superset SSO#OIDC#Flask AppBuilder#Keycloak roles