How to Set Up Google Workspace SSO for Apache Superset

For a company on Google Workspace, “Sign in with Google” is the SSO users already expect, and Flask-AppBuilder has a Google handler built in, so the first version of this integration takes fifteen minutes. The reason it deserves a full guide is what the first version gets wrong.

Two things. First, Google’s OAuth flow will happily authenticate any Google account, including personal Gmail, unless you restrict it. Second, Google’s user info response contains a name and an email address and nothing about groups, so Superset’s AUTH_ROLES_MAPPING has nothing to map from and every user lands in the default role. Both are fixable in superset_config.py, and the second has three solutions of increasing power.

The general mechanics of Superset OAuth, proxy settings and the break-glass procedure are in our Okta guide and are not repeated here. Written against Superset 4.x.

Step 1: Create the Google OAuth client

In the Google Cloud console, in a project owned by your organisation:

  • Go to APIs & Services, OAuth consent screen. Choose Internal as the user type. This alone restricts sign-in to accounts in your Workspace organisation and is the single most important setting in this guide. Fill in the app name (“Superset BI”), support email and developer contact. Add the scopes openid, .../auth/userinfo.email and .../auth/userinfo.profile. Save.
  • Go to Credentials, Create credentials, OAuth client ID. Application type Web application. Name it “Superset”.
  • Under Authorised JavaScript origins, add https://superset.example.com.
  • Under Authorised redirect URIs, add https://superset.example.com/oauth-authorized/google. The last segment must match the provider name in Superset’s config.
  • Create, and copy the Client ID and Client secret.

If your consent screen must be External (for example, contractors on a different Workspace domain need access), you lose the automatic organisation restriction, and the domain enforcement in Step 2 becomes mandatory rather than belt-and-braces.

Step 2: Configure Superset

python
import os
from flask_appbuilder.security.manager import AUTH_OAUTH

AUTH_TYPE = AUTH_OAUTH

WORKSPACE_DOMAIN = "example.com"

OAUTH_PROVIDERS = [
    {
        "name": "google",
        "icon": "fa-google",
        "token_key": "access_token",
        "remote_app": {
            "client_id": os.environ["GOOGLE_CLIENT_ID"],
            "client_secret": os.environ["GOOGLE_CLIENT_SECRET"],
            "api_base_url": "https://www.googleapis.com/oauth2/v2/",
            "client_kwargs": {
                "scope": "openid email profile",
                # hd is a hint that pre-selects the Workspace account picker.
                # It is NOT enforcement; enforcement is in the security manager below.
                "hd": WORKSPACE_DOMAIN,
                "prompt": "select_account",
            },
            "server_metadata_url": "https://accounts.google.com/.well-known/openid-configuration",
            "request_token_url": None,
            "access_token_url": "https://accounts.google.com/o/oauth2/token",
            "authorize_url": "https://accounts.google.com/o/oauth2/auth",
        },
    }
]

AUTH_USER_REGISTRATION = True
AUTH_USER_REGISTRATION_ROLE = "Gamma"
AUTH_ROLES_SYNC_AT_LOGIN = True

ENABLE_PROXY_FIX = True
SESSION_COOKIE_SECURE = True
SESSION_COOKIE_SAMESITE = "Lax"

With only this, any user your consent screen allows can log in and will receive the Gamma role. The next section adds domain enforcement and role assignment in a custom security manager.

SSO is the easy half of access control

Mapping Google Groups to Superset roles, keeping that sync from drifting, and layering row-level security on top is where most SSO setups quietly rot. A Superset Architecture Review covers all three.

Book a Superset Architecture Review

Step 3: Enforce the domain and assign roles

FAB’s Google handler returns username, first_name, last_name and email, and no role_keys. Override oauth_user_info to add both checks. Three options follow; pick one.

Option A: Roles by domain rule

The simplest useful version. Everyone in the domain is a viewer; an explicit list of emails are analysts or admins.

python
from flask import flash, redirect, url_for
from superset.security import SupersetSecurityManager

ADMINS = {"data.lead@example.com", "platform@example.com"}
ANALYSTS = {"finance.analyst@example.com", "ops.analyst@example.com"}


class GoogleSecurityManager(SupersetSecurityManager):
    def oauth_user_info(self, provider, response=None):
        if provider != "google":
            return super().oauth_user_info(provider, response)

        me = self.appbuilder.sm.oauth_remotes[provider].get("userinfo").json()
        email = (me.get("email") or "").lower()

        # Enforcement: refuse anything outside the Workspace domain.
        if not email.endswith("@" + WORKSPACE_DOMAIN) or not me.get("verified_email", True):
            return {}

        if email in ADMINS:
            role_keys = ["admin"]
        elif email in ANALYSTS:
            role_keys = ["analyst"]
        else:
            role_keys = ["viewer"]

        return {
            "username": email,
            "first_name": me.get("given_name", ""),
            "last_name": me.get("family_name", ""),
            "email": email,
            "role_keys": role_keys,
        }


CUSTOM_SECURITY_MANAGER = GoogleSecurityManager

AUTH_ROLES_MAPPING = {
    "admin": ["Admin"],
    "analyst": ["Alpha", "sql_lab"],
    "viewer": ["Gamma"],
}

Returning an empty dict makes FAB refuse the login. The allow-lists live in configuration, which is fine for a team of thirty and unpleasant for a company of three hundred. That is when you move to Option C.

Option B: Roles by email pattern

A middle step for organisations with structured addresses or aliases: a mapping from regex to role key.

python
import re

ROLE_PATTERNS = [
    (re.compile(r"^(data|bi)-admin@example\.com$"), "admin"),
    (re.compile(r"^.+\.analyst@example\.com$"), "analyst"),
]

def role_keys_for(email):
    for pattern, key in ROLE_PATTERNS:
        if pattern.match(email):
            return [key]
    return ["viewer"]

Substitute role_keys_for(email) into Option A. It scales a little further and is still visible in config.

Option C: Roles from Google Groups via the Directory API

The correct long-term answer: Google Groups are the source of truth, and Superset looks up the user’s groups at login. This uses the Admin SDK Directory API with a service account that has domain-wide delegation.

Setup in Google Cloud and Workspace admin:

  • Enable the Admin SDK API in the Cloud project.
  • Create a service account, create a JSON key for it, and note its client ID (the numeric one).
  • In the Workspace admin console, Security, Access and data control, API controls, Domain-wide delegation, add the service account’s client ID with the scope https://www.googleapis.com/auth/admin.directory.group.readonly.
  • Pick a Workspace admin account the service account will impersonate (a dedicated, low-privilege admin role that can read groups is enough).

Then in Superset:

python
import os
from google.oauth2 import service_account
from googleapiclient.discovery import build
from superset.security import SupersetSecurityManager

DIRECTORY_SCOPES = ["https://www.googleapis.com/auth/admin.directory.group.readonly"]
IMPERSONATE = os.environ["GOOGLE_DIRECTORY_ADMIN"]          # e.g. directory-reader@example.com
SA_KEY_PATH = os.environ["GOOGLE_DIRECTORY_SA_KEY"]          # mounted JSON key

_directory = None

def directory():
    global _directory
    if _directory is None:
        creds = service_account.Credentials.from_service_account_file(SA_KEY_PATH, scopes=DIRECTORY_SCOPES)
        creds = creds.with_subject(IMPERSONATE)
        _directory = build("admin", "directory_v1", credentials=creds, cache_discovery=False)
    return _directory


def google_groups_for(email):
    groups = []
    page_token = None
    while True:
        resp = directory().groups().list(userKey=email, pageToken=page_token).execute()
        groups.extend(g["email"] for g in resp.get("groups", []))
        page_token = resp.get("nextPageToken")
        if not page_token:
            return groups


class GoogleGroupsSecurityManager(SupersetSecurityManager):
    def oauth_user_info(self, provider, response=None):
        if provider != "google":
            return super().oauth_user_info(provider, response)
        me = self.appbuilder.sm.oauth_remotes[provider].get("userinfo").json()
        email = (me.get("email") or "").lower()
        if not email.endswith("@" + WORKSPACE_DOMAIN):
            return {}
        try:
            role_keys = google_groups_for(email)
        except Exception as exc:  # directory outage should not lock everyone out
            self.appbuilder.get_app.logger.error("Directory lookup failed for %s: %s", email, exc)
            role_keys = []
        return {
            "username": email,
            "first_name": me.get("given_name", ""),
            "last_name": me.get("family_name", ""),
            "email": email,
            "role_keys": role_keys,
        }


CUSTOM_SECURITY_MANAGER = GoogleGroupsSecurityManager

# Google Group email -> Superset roles
AUTH_ROLES_MAPPING = {
    "superset-admins@example.com": ["Admin"],
    "superset-analysts@example.com": ["Alpha", "sql_lab"],
    "superset-viewers@example.com": ["Gamma"],
}

Install google-api-python-client and google-auth into the Superset image. The lookup adds a few hundred milliseconds to login, once per session, which nobody notices. Decide deliberately what happens when the Directory API is unreachable: the code above falls back to the registration role so people can still see public dashboards, which is usually the right trade-off. If you would rather fail closed, return {} in the exception handler.

Step 4: Test

  • Restart Superset. The login page shows “Sign in with google”.
  • Sign in with an admin account. Check Settings, List Users for the expected role.
  • In a private window, attempt sign-in with a personal Gmail account. With an Internal consent screen, Google refuses before Superset is involved. With an External screen, the security manager refuses and you land back on the login page.
  • For Option C, add a user to superset-analysts@example.com, wait a minute for Google to propagate, sign out and in, and confirm SQL Lab appears.

Troubleshooting

Symptom Cause Fix
Error 400: redirect_uri_mismatch Redirect URI not registered exactly Add https://superset.example.com/oauth-authorized/google; enable ENABLE_PROXY_FIX
Error 403: org_internal Consent screen is Internal and the account is outside the organisation Expected for outsiders; for legitimate external users switch to External and rely on the security manager
Personal Gmail accounts can log in Consent screen External and no domain check Add the domain enforcement from Step 3
Everyone is Gamma No role_keys returned Implement Option A, B or C; the built-in handler returns none
Option C: 403 Not Authorized to access this resource/api Domain-wide delegation missing or wrong scope Add the service account client ID with the group read-only scope in Workspace admin
Option C: 400 Bad Request: Invalid Input: memberKey Impersonated account lacks group read permission Impersonate an account with a Groups Reader admin role
mismatching_state Session cookie not returned on callback SESSION_COOKIE_SAMESITE = "Lax", SESSION_COOKIE_SECURE matching the scheme

Operational notes

  • Offboarding. Suspending a user in Workspace blocks new logins immediately. Existing Superset sessions last until PERMANENT_SESSION_LIFETIME; keep it short.
  • Multiple domains. Workspace organisations with secondary domains should check membership of a set rather than a single suffix.
  • Service account key hygiene (Option C). Mount it as a secret, never in the image, and rotate on the same schedule as other credentials.
  • 2-Step Verification and Google’s context-aware access policies apply to Superset automatically, which is a good part of the reason to do this.

Frequently Asked Questions

Can I restrict Apache Superset sign-in to my Google Workspace domain?

Yes, and it is worth doing in two places. Setting the OAuth consent screen to Internal restricts sign-in to accounts in your Workspace organisation and is the single most important setting in the setup. Then check the email domain again server-side in oauth_user_info, returning an empty dict for anything outside it, so the restriction survives a consent screen that later has to change to External for contractors.

Is the hd parameter enough to block personal Gmail accounts?

No. hd is a hint that pre-selects the right account in Google’s account picker; it is not enforcement, and the flow can still be completed with a personal account. Enforcement is the Internal consent screen plus the explicit domain check in the security manager. Treat hd purely as a usability improvement.

How do I map Google Groups to Superset roles?

Google’s user info response carries a name and an email address and nothing about group membership, so AUTH_ROLES_MAPPING has nothing to match and every user lands in the default role. The long-term answer is to look the user’s groups up at login through the Admin SDK Directory API, using a service account with domain-wide delegation and the group read-only scope. For a team of thirty, allow-lists or email patterns in configuration are a reasonable interim step.

Do I need a service account to use Google Workspace SSO with Superset?

Only for group-based role assignment. Authentication itself needs nothing more than the OAuth client ID and secret. The service account, its JSON key and the domain-wide delegation grant exist solely so Superset can ask the Directory API which groups a user belongs to. If roles are assigned by allow-list or email pattern instead, skip it and avoid the key-rotation burden that comes with it.

Build It With Andolasoft

Getting the sign-in right is the first step; deciding what each role can see is the second. A Superset Architecture Review covers authentication, role design and row-level security together with caching, async queries and upgrade readiness, in five working days with a written report. If you want the whole platform run for you, service-account key rotation included, that is Managed Support. For the broader set of controls, see governance and security best practices for Superset.

If your Workspace organisation is on Superset and everyone is still landing in the same role, talk to us. We will map your Google Groups to a Superset role model that holds up, and give you a fixed-scope plan to get there.

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.

How to Set Up Microsoft Entra ID (Azure AD) SSO for Apache Superset

If your organisation runs on Microsoft 365, Entra ID (the service formerly called Azure Active Directory) is already the source of truth for who works there and what they may access. Superset should read from it rather than keeping its own list of passwords. Flask-AppBuilder, the framework under Superset, has an Azure OAuth handler built in, so the setup is configuration rather than code.

This guide is the Entra-specific companion to our Okta SSO guide. The general mechanics of Superset authentication, the reverse-proxy settings and the break-glass advice are the same and are not repeated in full here. What differs with Entra is the app registration, the way roles reach Superset, and the errors you will see.

The configuration applies to Superset 2.1 through 4.x, written against 4.x.

Groups or app roles?

Entra can put either the user’s group memberships or their app roles into the token. Flask-AppBuilder’s Azure handler reads the roles claim, which is app roles, and uses it as the list of keys to match against AUTH_ROLES_MAPPING.

App roles are also the better design for Superset:

  • No overage problem. Entra stops emitting the groups claim when a user is in more than a couple of hundred groups and replaces it with an overage indicator. Large organisations hit this routinely. App roles are specific to your application and never overflow.
  • Assignment lives on the application. You assign a group or a user to an app role in the Superset enterprise application. The Entra admin sees exactly who can reach Superset and as what, without decoding group names.
  • Role names are yours. Superset.Admin, Superset.Analyst, Superset.Viewer are clearer than a security group created five years ago for something else.

So this guide uses app roles. If you must use groups, the section near the end explains what to change.

Step 1: Register the application

In the Entra admin centre:

  • Go to Identity, Applications, App registrations and choose New registration.
  • Name it “Superset BI”. Under Supported account types choose Accounts in this organizational directory only (single tenant).
  • Under Redirect URI select Web and enter https://superset.example.com/oauth-authorized/azure. The final segment must match the provider name in superset_config.py, which is azure in this guide. Entra compares redirect URIs case-sensitively and exactly, including trailing slashes.
  • Register, then note the Application (client) ID and Directory (tenant) ID from the Overview page.
  • Under Certificates & secrets, create a client secret. Copy the value immediately; it is shown once. Set a calendar reminder for its expiry, because an expired secret is a total login outage.
  • Under Authentication, confirm ID tokens is ticked under implicit grant and hybrid flows. Superset uses the authorization-code flow, but FAB decodes the ID token for the user’s profile, so it must be issued.
  • Under Token configuration, add the optional claims email, family_name, given_name, preferred_username and upn to the ID token. This ensures FAB has a username and a display name even for guest or unusual accounts.

Define app roles

  • Under App roles, choose Create app role.
  • Create three roles with Allowed member types set to Users/Groups:
Display name Value Description
Superset Admin Superset.Admin Full administration
Superset Analyst Superset.Analyst Build charts and dashboards, run SQL
Superset Viewer Superset.Viewer View permitted dashboards

The Value is what appears in the token and what AUTH_ROLES_MAPPING matches.

Assign users and groups

  • Go to Identity, Applications, Enterprise applications, open “Superset BI”, then Users and groups.
  • Add your existing security groups, assigning each to one app role. A user can hold several roles by being in several groups.
  • Under Properties, set Assignment required to Yes. Anyone not assigned is refused by Entra before Superset is involved, which is the behaviour you want.

Step 2: Configure Superset

python
import os
from flask_appbuilder.security.manager import AUTH_OAUTH

AZURE_TENANT_ID = os.environ["AZURE_TENANT_ID"]
AZURE_AUTHORITY = f"https://login.microsoftonline.com/{AZURE_TENANT_ID}"

AUTH_TYPE = AUTH_OAUTH

OAUTH_PROVIDERS = [
    {
        "name": "azure",
        "icon": "fa-windows",
        "token_key": "access_token",
        "remote_app": {
            "client_id": os.environ["AZURE_CLIENT_ID"],
            "client_secret": os.environ["AZURE_CLIENT_SECRET"],
            "api_base_url": f"{AZURE_AUTHORITY}/oauth2",
            "client_kwargs": {
                "scope": "openid profile email User.Read",
            },
            "request_token_url": None,
            "access_token_url": f"{AZURE_AUTHORITY}/oauth2/v2.0/token",
            "authorize_url": f"{AZURE_AUTHORITY}/oauth2/v2.0/authorize",
            "jwks_uri": f"{AZURE_AUTHORITY}/discovery/v2.0/keys",
        },
    }
]

AUTH_USER_REGISTRATION = True
AUTH_USER_REGISTRATION_ROLE = "Gamma"

# Entra app role value -> Superset roles
AUTH_ROLES_MAPPING = {
    "Superset.Admin": ["Admin"],
    "Superset.Analyst": ["Alpha", "sql_lab"],
    "Superset.Viewer": ["Gamma"],
}
AUTH_ROLES_SYNC_AT_LOGIN = True

# Proxy and cookie settings (same as any OAuth deployment behind TLS termination)
ENABLE_PROXY_FIX = True
SESSION_COOKIE_SECURE = True
SESSION_COOKIE_SAMESITE = "Lax"

What is specific to Entra here:

  • The provider name must be azure for FAB’s built-in handler to recognise it and decode the ID token correctly.
  • The URLs are the v2.0 endpoints for your tenant. Do not use the common tenant unless you intend to accept users from any Entra directory, which for an internal BI tool you almost certainly do not.
  • jwks_uri is what FAB uses to verify the ID token signature. It must be the tenant’s discovery keys endpoint.
  • The roles claim is read automatically from the ID token by FAB’s Azure handler; nothing extra to configure.

App roles chosen — RBAC design is next

Entra ID settles who can log in; a Superset Architecture Review settles what capability and data-domain roles they land in, so access control doesn’t get bolted on ad hoc later.

Book a Superset Architecture Review

Step 3: Test

Restart Superset. The login page shows a single “Sign in with azure” button.

  • Sign in as a member of the admin group. Confirm the Admin menu is visible and the user appears in Settings, List Users with the Admin role.
  • In a private window, sign in as a viewer. Confirm only permitted dashboards appear.
  • Move a user between groups in Entra, sign them out and in. Roles should change, which confirms AUTH_ROLES_SYNC_AT_LOGIN.
  • Try a user who is not assigned to the application. Entra should refuse with an “assignment required” message before reaching Superset.

Entra-specific troubleshooting

Symptom Cause Fix
AADSTS50011: The redirect URI ... does not match Redirect URI mismatch, often case or trailing slash Copy the URI exactly; enable ENABLE_PROXY_FIX so Superset sends https://
AADSTS7000215: Invalid client secret Secret value copied wrongly, or the secret ID instead of the value Create a new secret and copy the value column
AADSTS700016: Application not found in the directory Wrong tenant ID in the URLs Use the Directory (tenant) ID from the app Overview page
AADSTS50105: The signed in user is not assigned to a role Assignment required is on and the user is unassigned Assign the user or their group under Enterprise applications
Everyone gets the Gamma role App roles not defined, or user assigned to the app without a role Define app roles and assign groups to specific roles
Login works, user has no name or email Optional claims not configured Add email, given_name, family_name, preferred_username under Token configuration
mismatching_state error Session cookie not returned on callback SESSION_COOKIE_SAMESITE = "Lax", SESSION_COOKIE_SECURE matching the scheme
Logins fail on a Monday morning Client secret expired over the weekend Rotate the secret; set the expiry reminder you skipped last time

If you must use groups instead of app roles

Some organisations mandate group-based access for all applications. Two changes:

  • In the app registration under Token configuration, add a groups claim to the ID token. Choose Groups assigned to the application rather than all security groups, which sidesteps the overage problem for most tenants.
  • Override oauth_user_info in a custom security manager so that role_keys reads the groups claim. Entra emits group object IDs, not names, so the keys in AUTH_ROLES_MAPPING become GUIDs:
python
import jwt
from superset.security import SupersetSecurityManager


class EntraGroupsSecurityManager(SupersetSecurityManager):
    def oauth_user_info(self, provider, response=None):
        if provider != "azure":
            return super().oauth_user_info(provider, response)
        id_token = response["id_token"]
        claims = jwt.decode(id_token, options={"verify_signature": False})
        return {
            "username": claims.get("preferred_username") or claims.get("upn") or claims["oid"],
            "first_name": claims.get("given_name", ""),
            "last_name": claims.get("family_name", ""),
            "email": claims.get("email") or claims.get("preferred_username", ""),
            "role_keys": claims.get("groups", []),
        }


CUSTOM_SECURITY_MANAGER = EntraGroupsSecurityManager

AUTH_ROLES_MAPPING = {
    "5d2f9c1a-...-admins-group-object-id": ["Admin"],
    "8b7e4a3c-...-analysts-group-object-id": ["Alpha", "sql_lab"],
}

The signature is not re-verified here because Authlib has already validated the token during the exchange; the decode is only to read claims. Keep a comment saying so, or the next reviewer will flag it.

Operational notes

  • Secret rotation is the main recurring task. Entra client secrets expire at 6, 12 or 24 months. Put the expiry in the same calendar as your TLS certificates.
  • Conditional Access policies in Entra (MFA, compliant device, location) apply to Superset automatically, which is a strong argument for SSO in regulated environments.
  • Deactivating a user in Entra blocks new logins immediately. Existing Superset sessions last until PERMANENT_SESSION_LIFETIME; keep it to a working day or less.
  • Service accounts that used password login against the Superset API need another path once AUTH_TYPE changes. Plan for it before the cutover.

Frequently Asked Questions

Should Superset roles come from Entra ID groups or app roles?

App roles, in almost every case. Flask-AppBuilder’s Azure handler reads the roles claim, which is populated by app roles, and matches it against AUTH_ROLES_MAPPING without any custom code. App roles also avoid the groups overage problem: Entra stops emitting the groups claim entirely once a user belongs to more than a couple of hundred groups, and replaces it with an overage indicator that Superset cannot use. Groups remain possible, but they need a custom security manager and group object IDs as mapping keys.

Why do all my users land in the Gamma role after Entra ID SSO?

Either no app roles are defined on the registration, or users are assigned to the enterprise application without being assigned to a specific role. Superset is doing what it was told: no roles claim arrives, nothing matches AUTH_ROLES_MAPPING, and AUTH_USER_REGISTRATION_ROLE applies. Check the claim first, by decoding the ID token from a test login, before changing any Superset configuration.

Why does Entra return AADSTS50011 when the redirect URI looks correct?

Entra compares redirect URIs exactly, including case and trailing slashes, and the URI Superset sends is built from the request it received. Behind a TLS-terminating proxy, Superset sends http:// unless ENABLE_PROXY_FIX = True is set, so a URI registered as https://superset.example.com/oauth-authorized/azure never matches. Set the proxy fix, then copy the URI character for character.

Do Entra Conditional Access policies apply to Apache Superset?

Yes. Because the login happens at Entra rather than in Superset, MFA, compliant-device and named-location policies are enforced before a token is ever issued, and Superset inherits them without configuration. That inheritance is one of the stronger arguments for putting Superset behind SSO in a regulated environment. The limit is session lifetime: revoking access in Entra blocks new logins immediately but does not terminate an existing Superset session, so keep PERMANENT_SESSION_LIFETIME short.

Build It With Andolasoft

Entra ID settles who can log in. The role design it feeds, row-level security, the caching and async query setup and upgrade readiness are the rest of the platform, and a Superset Architecture Review checks all of them in five working days with a written report. If the platform should simply be someone else’s problem, Managed Support covers patching, monitoring and the secret-rotation reminders that prevent a Monday-morning outage. For the wider set of controls around a deployment, see governance and security best practices for Superset.

If you are putting Superset behind Entra ID and would rather settle the role model at the same time than six months later, talk to us. We will review your app registration, the claims it emits and your current Superset roles, and give you a fixed-scope plan to a deployment your security team can sign off.

How to Set Up Okta Single Sign-On for Apache Superset

Password logins are the first thing an auditor asks about and the last thing anyone wants to manage by hand. If your team already uses Okta for everything else, Superset should not be the exception: one identity, one place to offboard, and roles that follow the person rather than the login.

Apache Superset supports this out of the box through Flask-AppBuilder (FAB), the framework Superset is built on. The pieces are all there. What is missing is a single, current walkthrough that covers the Okta side, the Superset side, the role mapping, and the handful of reverse-proxy and cookie settings that turn a working configuration into a redirect loop. This is that walkthrough.

By the end, you will have:

  • Okta as the only login path for Superset, with the local password form gone.
  • Users created automatically on first login, with their Okta groups mapped to Superset roles.
  • Role changes in Okta reflected in Superset at next login.
  • A troubleshooting table for the errors this setup produces in practice.

The configuration shown here applies to Superset 2.1 through 4.x. The examples were written against Superset 4.x with the Flask-AppBuilder 4 series it ships with.

How Superset authentication actually works

Superset does not implement authentication itself. FAB does, through its SecurityManager, and Superset extends that with SupersetSecurityManager. FAB supports five authentication types, selected with AUTH_TYPE in superset_config.py:

AUTH_TYPE What it does Typical use
AUTH_DB Username and password stored in Superset’s metadata database Default, small teams, demos
AUTH_LDAP Bind against an LDAP or Active Directory server On-premises corporate directories
AUTH_OAUTH OAuth 2.0 / OpenID Connect against one or more providers Okta, Azure AD, Google, Keycloak, Auth0
AUTH_OID Legacy OpenID 2.0 Rarely used today
AUTH_REMOTE_USER Trust a header set by an upstream proxy Behind an SSO-terminating gateway

Okta is an AUTH_OAUTH provider. Under the hood, FAB uses the Authlib client library to run the authorization-code flow, fetch the user’s profile from the provider’s userinfo endpoint, and hand the result to a method called oauth_user_info. FAB ships a handler for Okta, so a standard setup works without a custom security manager — though as Step 2 explains, there is one good reason to write one anyway.

Prerequisites

  • Admin access to your Okta org, or someone who has it and twenty minutes.
  • A running Superset instance reachable over HTTPS on a stable hostname. OAuth callbacks to localhost work for testing; callbacks to plain HTTP in production do not.
  • The ability to edit superset_config.py and restart Superset.
  • Authlib installed in the Superset environment. The official Docker image includes it. For a pip install, run pip install authlib if python -c "import authlib" fails.
  • A short list of Okta groups you want to map to Superset roles. Three is a good start: administrators, analysts who can write SQL, and viewers.

Step 1: Create the Okta application integration

In the Okta Admin Console:

  1. Go to Applications, then Applications, and click Create App Integration.
  2. Choose OIDC – OpenID Connect as the sign-in method and Web Application as the application type.
  3. Name it something your users will recognise, for example “Superset BI”.
  4. Under Grant type, leave Authorization Code ticked. Superset does not need implicit or hybrid flows.
  5. Set the Sign-in redirect URI to https://superset.example.com/oauth-authorized/okta. The last path segment must match the provider name you will use in superset_config.py, which in this guide is okta.
  6. Set the Sign-out redirect URI to https://superset.example.com/login/.
  7. Under Assignments, assign the groups that should be allowed to log in. Anyone not assigned will be refused by Okta before Superset ever sees them, which is the behaviour you want.
  8. Save, then copy the Client ID and Client secret from the General tab.

Choose the authorization server and expose groups

Okta has two kinds of authorization servers. The org authorization server at https://<your-org>.okta.com issues tokens for Okta’s own APIs and does not let you add custom claims. The default custom authorization server at https://<your-org>.okta.com/oauth2/default does, and it is what you need if you want group-based role mapping. Every URL in this guide uses the custom server.

To make Okta include the user’s groups in the response Superset reads:

  1. Go to Security, then API, open the default authorization server, and select the Claims tab.
  2. Click Add Claim. Name it groups, set the value type to Groups, filter Matches regex with the value .*, and include it in Any scope.
  3. Add the claim for both the ID Token and the Access Token, with type Always. FAB does not read the ID token; it calls Okta’s userinfo endpoint with the access token and maps roles from that payload. Configuring only the ID token is the most common reason group mapping silently does nothing.
  4. On the Scopes tab, confirm a groups scope exists, or add one. Superset will request it.

The regex .* returns every group the user belongs to. In large orgs, that can be a long list. Filter by a prefix such as ^superset_ if you want to keep the token small; everything else in this guide still works.

Step 2: Configure Superset

Add the following to superset_config.py. Keep the client secret out of the file and read it from the environment or your secrets manager.

python
import os
from flask_appbuilder.security.manager import AUTH_OAUTH

OKTA_BASE_URL = os.environ["OKTA_BASE_URL"]  # e.g. https://acme.okta.com
OKTA_ISSUER = f"{OKTA_BASE_URL}/oauth2/default"

AUTH_TYPE = AUTH_OAUTH

OAUTH_PROVIDERS = [
    {
        "name": "okta",
        "icon": "fa-circle-o",
        "token_key": "access_token",
        "remote_app": {
            "client_id": os.environ["OKTA_CLIENT_ID"],
            "client_secret": os.environ["OKTA_CLIENT_SECRET"],
            "api_base_url": f"{OKTA_ISSUER}/v1/",
            "client_kwargs": {"scope": "openid profile email groups"},
            "server_metadata_url": f"{OKTA_ISSUER}/.well-known/openid-configuration",
            "access_token_url": f"{OKTA_ISSUER}/v1/token",
            "authorize_url": f"{OKTA_ISSUER}/v1/authorize",
            "jwks_uri": f"{OKTA_ISSUER}/v1/keys",
        },
    }
]

# Create a Superset user the first time someone signs in through Okta.
AUTH_USER_REGISTRATION = True

# Role for users whose groups match nothing in AUTH_ROLES_MAPPING.
AUTH_USER_REGISTRATION_ROLE = "Gamma"

# Okta group name -> list of Superset roles.
AUTH_ROLES_MAPPING = {
    "superset_admins": ["Admin"],
    "superset_analysts": ["Alpha", "sql_lab"],
    "superset_viewers": ["Gamma"],
}

# Re-evaluate roles from the groups claim on every login, so a change in
# Okta takes effect the next time the user signs in.
AUTH_ROLES_SYNC_AT_LOGIN = True

What each block does:

  • name is the provider key. It appears in the login button and, more importantly, in the callback path /oauth-authorized/okta. It must match the redirect URI you registered in Okta.
  • server_metadata_url lets Authlib discover the token endpoint, signing keys and supported scopes from Okta’s OpenID configuration document. The explicit access_token_url, authorize_url and jwks_uri entries are belt and braces; they let the flow keep working if discovery is ever blocked by an egress firewall.
  • api_base_url is where FAB fetches the user profile from. FAB’s built-in Okta handler calls userinfo relative to this URL, which resolves to https://acme.okta.com/oauth2/default/v1/userinfo.
  • client_kwargs.scope must include groups, or the claim you created in Step 1 will never arrive.
  • AUTH_USER_REGISTRATION is what turns a successful Okta login into a Superset user record. Without it, only users you created by hand can log in.
  • AUTH_ROLES_MAPPING keys are Okta group names, exactly as they appear in the token. Values are lists of Superset role names. Admin, Alpha, Gamma, sql_lab and Public are the roles Superset creates by default; any custom role you have created works too.
  • AUTH_ROLES_SYNC_AT_LOGIN is the setting most people miss. Without it, roles are assigned once, at registration, and removing someone from an Okta group has no effect on what they can see in Superset. Note the flip side: with sync on, Okta is the only source of truth, so a role you grant by hand in Superset’s UI is wiped at that user’s next login.

When you need a custom security manager

FAB’s built-in Okta handler works, but it derives the username from the token’s sub claim and prefixes it, so your user list fills up with entries like okta_00u1a2b3c4d5e6f7g8h9. That is unpleasant to administer and impossible to eyeball against an audit request. Overriding oauth_user_info is also the fix if your Okta org names the groups claim differently, nests it, or you want usernames to be email addresses regardless of what preferred_username says:

python
import logging

from superset.security import SupersetSecurityManager

log = logging.getLogger(__name__)


class OktaSecurityManager(SupersetSecurityManager):
    def oauth_user_info(self, provider, response=None):
        if provider != "okta":
            return super().oauth_user_info(provider, response)

        me = self.appbuilder.sm.oauth_remotes[provider].get("userinfo").json()

        # Logs which claims arrived without dumping personal data.
        log.debug("Okta userinfo claims: %s", sorted(me.keys()))

        return {
            "username": me["email"].lower(),
            "first_name": me.get("given_name", ""),
            "last_name": me.get("family_name", ""),
            "email": me["email"].lower(),
            "role_keys": me.get("superset_roles", me.get("groups", [])),
        }


# Also in superset_config.py, after the class is importable:
CUSTOM_SECURITY_MANAGER = OktaSecurityManager

The role_keys list is what FAB matches against AUTH_ROLES_MAPPING. Using the email address as the username is a deliberate choice: it is stable, unique, and survives a rename in Okta better than preferred_username does. Pick one convention before the first login, because changing it later creates duplicate user records.

That debug line is also your diagnostic for Step 1: if groups is missing from the logged claim list, the problem is the Okta claim configuration, not Superset.

Working behind a reverse proxy changes this next part

Proxy headers, session cookies, and role mapping are where Okta setups usually break in production, not in the OAuth handshake. A Superset Architecture Review checks the deployment, not just the login flow.

Book a Superset Architecture Review

Step 3: Settings that matter behind a reverse proxy

Almost every production Superset sits behind a load balancer, an ingress controller, or an Nginx proxy that terminates TLS. That changes what Superset thinks its own URL is, and OAuth is unforgiving about URLs.

python
from datetime import timedelta

# Trust X-Forwarded-* headers from the proxy so Superset builds https:// URLs.
ENABLE_PROXY_FIX = True
PROXY_FIX_CONFIG = {"x_for": 1, "x_proto": 1, "x_host": 1, "x_port": 1, "x_prefix": 1}

# Cookies must be sent on the callback from Okta.
SESSION_COOKIE_SECURE = True
SESSION_COOKIE_HTTPONLY = True
SESSION_COOKIE_SAMESITE = "Lax"  # "Strict" breaks the OAuth callback

# Log people out after a working day; Okta deactivation does not end live sessions.
PERMANENT_SESSION_LIFETIME = timedelta(hours=10)

Three points worth underlining:

  • Without ENABLE_PROXY_FIX, Superset sees the request as plain HTTP and constructs a redirect_uri starting with http://. Okta compares it to the https:// value you registered and rejects the login with a redirect URI mismatch.
  • SESSION_COOKIE_SAMESITE = "Strict" looks like the secure choice, and it is the one that breaks OAuth. The callback from Okta is a cross-site navigation. With Strict, the browser withholds the session cookie, Superset cannot find the state it stored before redirecting, and you get a mismatching_state error. Use Lax.
  • Deactivating someone in Okta stops new logins immediately but does not touch sessions Superset has already issued. PERMANENT_SESSION_LIFETIME bounds how long that window can be. For sensitive data, make it shorter.

Step 4: Restart and test

Restart Superset. The login page now shows a single “Sign in with Okta” button and no username or password fields.

  1. Sign in with a user who is in superset_admins. You should land on the welcome page with the Admin menu visible.
  2. Go to Settings, then List Users, and confirm the user was created with the expected username, email, and roles.
  3. Sign in as a user in superset_viewers from a private window. Confirm they see dashboards they are permitted to see and nothing else. Row-level security rules, if you have them, still apply exactly as before; SSO changes how people authenticate, not what the roles allow.
  4. Move that user into superset_analysts in Okta, sign them out and back in, and confirm SQL Lab appears. This proves AUTH_ROLES_SYNC_AT_LOGIN is working.
  5. Remove the user from every Superset group in Okta and sign in again. They should now hold only the AUTH_USER_REGISTRATION_ROLE. If you would rather they be refused entirely, remove them from the Okta application assignment instead.

Keep a break-glass path. Switching to AUTH_OAUTH removes the password form, so if Okta is unavailable, nobody can log in. Document the rollback: a copy of the previous superset_config.py with AUTH_DB, a known-good admin account in the metadata database, and the restart command. Test it once before you need it.

Troubleshooting

Symptom Likely cause Fix
Okta shows “The redirect_uri parameter must be a Login redirect URI” The callback URL Superset sent does not match Okta exactly Check the provider name, the scheme (http vs https), and trailing slashes. Enable ENABLE_PROXY_FIX behind a proxy.
mismatching_state: CSRF Warning! State not equal in request and response Session cookie not sent on the callback Set SESSION_COOKIE_SAMESITE = "Lax" and make sure SESSION_COOKIE_SECURE matches the scheme in use.
Login succeeds, but every user gets the Gamma role only Groups claim missing from the userinfo response Confirm the groups claim is configured for the Access Token as well as the ID Token, the groups scope is requested, and you are not using the org authorization server URLs.
“Access is Denied” after signing in User created without roles, or registration disabled Set AUTH_USER_REGISTRATION = True and a valid AUTH_USER_REGISTRATION_ROLE.
invalid_client in Superset logs Wrong client ID or secret, or the app is not a Web Application type Re-copy the credentials; recreate the integration as an OIDC Web Application if needed.
Usernames look like okta_00u1a2b3... FAB’s built-in handler derives the username from the sub claim Add the custom security manager from Step 2 before the first production login.
Two accounts for the same person Username convention changed between logins Pick one of preferred_username or email in oauth_user_info and keep it. Merge or delete the duplicate in List Users.
Roles do not update when Okta groups change Sync disabled Set AUTH_ROLES_SYNC_AT_LOGIN = True and have the user sign out and in.
Login button missing; password form still shown Config not loaded Confirm SUPERSET_CONFIG_PATH points to the file, or the file is on PYTHONPATH, and that Superset was actually restarted.

To see exactly what Okta returned, run Superset with debug logging for a single login using the log.debug line in the custom security manager above. If you need the claim values and not just their names, log the full payload once and remove it immediately afterwards; the response contains personal data.

Operational notes

  • Onboarding is now an Okta task. Add the person to the right group and assign them the application. There is nothing to do in Superset.
  • Offboarding is two steps. Deactivate in Okta, then either wait for PERMANENT_SESSION_LIFETIME to expire or, for immediate effect, set the user to inactive in Superset’s List Users.
  • Role grants must happen in Okta. With AUTH_ROLES_SYNC_AT_LOGIN enabled, editing a user’s roles in the Superset UI lasts only until their next login. Change the Okta group instead.
  • Service accounts and API clients that authenticate with a username and password against Superset’s REST API will stop working under AUTH_OAUTH. Move them to a token-based approach or keep a separate, restricted provider for them.
  • Multiple providers are allowed. OAUTH_PROVIDERS is a list. Some teams keep Okta for staff and a second provider for contractors or an embedded-analytics tenant.
  • Audit trail. Superset’s own logs record the username on each request. With SSO, those usernames are guaranteed to be real identities, which is what your auditor was after in the first place.

Related guides

The same AUTH_OAUTH mechanics apply to every identity provider; what changes is the provider block, the claims and how groups are exposed.

Once logins work, the next question is what each role can see. Superset RBAC design for a 500-user enterprise covers the roles your Okta groups should map to.

Frequently Asked Questions

Does Apache Superset support Okta SSO out of the box?

Yes. Okta is an AUTH_OAUTH provider, and Flask-AppBuilder, which handles Superset's authentication, ships a handler for it, so a standard setup works without custom code. It is still worth adding a small custom security manager before the first production login: the built-in handler derives usernames from the sub claim, which produces names like okta_00u1a2b3... instead of a readable username or email.

Why does Okta login fail with a redirect URI mismatch or mismatching_state error?

Both usually come from running behind a reverse proxy. Without ENABLE_PROXY_FIX, Superset builds an http:// callback URL that does not match the https:// one registered in Okta. A mismatching_state error means the session cookie was not sent on the callback, most often because SESSION_COOKIE_SAMESITE is set to Strict; use Lax.

Why does every user get only the Gamma role after signing in with Okta?

The groups claim is missing from the userinfo response, so there is nothing for AUTH_ROLES_MAPPING to map. Configure the groups claim for the Access Token as well as the ID Token, request the groups scope, and use a custom authorization server rather than the org authorization server URLs.

Does deactivating a user in Okta log them out of Superset?

No. It stops new logins immediately, but sessions Superset has already issued stay valid until PERMANENT_SESSION_LIFETIME expires. For immediate effect, also set the user to inactive in Superset's List Users.

Build It With Andolasoft

SSO is one control in a production Superset setup. It sits alongside role design, row-level security, network exposure, metadata database backups, and a patching cadence. If you are putting Superset in front of a wider audience, or an audit is on the calendar, it is worth reviewing all of those together rather than one at a time.

Our Superset Architecture Review is a fixed-scope, five-day review of exactly that: authentication and SSO, RBAC and RLS, caching and async queries, upgrade readiness and dashboard performance, delivered as a written report with prioritised fixes. If you would rather someone else kept the platform patched and monitored, that is what Managed Support is for.

For the wider set of controls, see our earlier post on data governance and security best practices for Superset deployments.

To put Superset behind Okta or another identity provider without the trial and error, contact us.