apache / superset

Apache Superset is a Data Visualization and Data Exploration Platform
https://superset.apache.org/
Apache License 2.0
61.34k stars 13.36k forks source link

Apache Superset & Keycloak integration #29539

Open kormpakis opened 1 month ago

kormpakis commented 1 month ago

Bug description

I am currently working on configuring OAuth authentication between Keycloak and Apache Superset. I am encountering an issue where, after entering the credentials on the Keycloak page, I am redirected back to Superset with the message: "The request to sign in was denied."

How to reproduce the bug

Below are the configurations I have used: Keycloak Configuration (Version 23.0.3)

Keycloak Client Settings Valid Redirect URIs: http://localhost:8089/oauth-authorized/keycloak Web Origins: http://localhost:8089/%5C*

Apache Superset Configuration (superset_config.py):

AUTH_TYPE = AUTH_OAUTH
LOGOUT_REDIRECT_URL = 'https://{my_keycloak_domain}/realms/{my_realm_name}/protocol/openid-connect/logout'
AUTH_USER_REGISTRATION = TrueAUTH_USER_REGISTRATION_ROLE = 'Gamma'

OAuth provider configuration for Keycloak in superset_config.py

OAUTH_PROVIDERS = [
{
'name': 'keycloak',
'icon': 'fa-key',
'token_key': 'access_token', # Keycloak uses 'access_token' for the access token
'remote_app': {
'client_id': 'test',
'client_secret': 'dElzstUQqUgQshK23IWWjmNuoe9GgIo8',
'api_base_url': 'https://{my_keycloak_domain}/auth/',
'client_kwargs': {
'scope': 'openid profile email',
},
'base_url': 'https://{my_keycloak_domain}/realms/{my_realm_name}/protocol/openid-connect/auth',
'request_token_url': None,
'access_token_url': 'https://{my_keycloak_domain}/realms/{my_realm_name}/protocol/openid-connect/token',
'authorize_url': 'https://{my_keycloak_domain}/realms/{my_realm_name}/protocol/openid-connect/auth',
},
}
]

Issue Description After entering the credentials in the Keycloak login page, the redirection to Superset results in the following message: "The request to sign in was denied."

Screenshots/recordings

No response

Superset version

master / latest-dev

Python version

I don't know

Node version

I don't know

Browser

Not applicable

Additional context

I am unsure what might be causing this error and would greatly appreciate your assistance in identifying and resolving the issue.

Has anyone had any similar issues?

I tried various configurations and similar issues for several days now with no result. It seems to be connected in the first place (it redirects to Keycloak sign-in page and comes back to Superset when correct credentials are provided) but it stucks in the final step.

Please help me and let's create a clear and working configuration, since the official docs are not so helpful...

Checklist

dosubot[bot] commented 1 month ago

To resolve the issue where, after entering credentials on the Keycloak page, you are redirected back to Apache Superset with the message: "The request to sign in was denied," you need to ensure that the OAuth2 flow is correctly configured and that the necessary permissions are granted.

Here are some steps you can take to troubleshoot and resolve the issue:

  1. Check Redirect URI: Ensure that the redirect_uri configured in Keycloak matches the one expected by Superset. The redirect_uri should be correctly set in both Keycloak and Superset configurations.

  2. Verify OAuth2 Configuration: Make sure that the OAuth2 client configuration in Keycloak is correct. This includes the client ID, client secret, and the allowed redirect URIs.

  3. Inspect Browser Console and Network Logs: Look at the browser console and network logs to see if there are any errors or warnings that can provide more details about why the sign-in request was denied.

  4. Check Superset Logs: Review the Superset logs for any error messages or warnings related to the OAuth2 authentication process. This can provide more insight into what might be going wrong.

  5. Ensure Correct Permissions: Verify that the user has the necessary permissions in Keycloak to access the application.

  6. Cross-Origin Requests: Ensure that cross-origin requests are properly handled and that the event.origin and event.source checks in the handleMessage function are correctly validating the message source.

Here is the relevant part of the code that handles the OAuth2 redirect and message validation:

const handleMessage = (event: MessageEvent) => {
  const redirectUrl = new URL(extra.redirect_uri);
  if (
    event.origin === redirectUrl.origin &&
    event.data.tabId === extra.tab_id &&
    event.source === oAuthTab.current
  ) {
    if (source === 'sqllab' && query) {
      dispatch(reRunQuery(query));
    } else if (source === 'explore' && chartId) {
      dispatch(triggerQuery(true, chartId));
    } else if (source === 'dashboard') {
      dispatch(onRefresh(chartList, true, 0, dashboardId));
    }
  }
};

Ensure that the redirect_uri, tab_id, and event.origin are correctly set and match the expected values. This validation is crucial for the OAuth2 flow to succeed [1].

Additionally, review the specific error messages or logs generated by Superset when the OAuth2 authentication fails. The relevant error messages are:

  1. OAuth2RedirectError:

    SupersetError(
       message="You don't have permission to access the data.",
       error_type=SupersetErrorType.OAUTH2_REDIRECT,
       level=ErrorLevel.WARNING,
       extra={"url": url, "tab_id": tab_id, "redirect_uri": redirect_uri},
    )
  2. OAuth2Error:

    SupersetError(
       message="Something went wrong while doing OAuth2",
       error_type=SupersetErrorType.OAUTH2_REDIRECT_ERROR,
       level=ErrorLevel.ERROR,
       extra={"error": error},
    )
    <sup>[[2]](https://github.com/apache/superset/blob/master/superset/exceptions.py)</sup><sup>[[3]](https://github.com/apache/superset/blob/master/superset/errors.py)</sup>

By following these steps and ensuring that the configurations are correct, you should be able to resolve the issue and successfully authenticate using Keycloak with Apache Superset.

To continue talking to Dosu, mention @dosu.

kormpakis commented 1 month ago

To resolve the issue where, after entering credentials on the Keycloak page, you are redirected back to Apache Superset with the message: "The request to sign in was denied," you need to ensure that the OAuth2 flow is correctly configured and that the necessary permissions are granted.

Here are some steps you can take to troubleshoot and resolve the issue:

  1. Check Redirect URI: Ensure that the redirect_uri configured in Keycloak matches the one expected by Superset. The redirect_uri should be correctly set in both Keycloak and Superset configurations.
  2. Verify OAuth2 Configuration: Make sure that the OAuth2 client configuration in Keycloak is correct. This includes the client ID, client secret, and the allowed redirect URIs.
  3. Inspect Browser Console and Network Logs: Look at the browser console and network logs to see if there are any errors or warnings that can provide more details about why the sign-in request was denied.
  4. Check Superset Logs: Review the Superset logs for any error messages or warnings related to the OAuth2 authentication process. This can provide more insight into what might be going wrong.
  5. Ensure Correct Permissions: Verify that the user has the necessary permissions in Keycloak to access the application.
  6. Cross-Origin Requests: Ensure that cross-origin requests are properly handled and that the event.origin and event.source checks in the handleMessage function are correctly validating the message source.

Here is the relevant part of the code that handles the OAuth2 redirect and message validation:

const handleMessage = (event: MessageEvent) => {
  const redirectUrl = new URL(extra.redirect_uri);
  if (
    event.origin === redirectUrl.origin &&
    event.data.tabId === extra.tab_id &&
    event.source === oAuthTab.current
  ) {
    if (source === 'sqllab' && query) {
      dispatch(reRunQuery(query));
    } else if (source === 'explore' && chartId) {
      dispatch(triggerQuery(true, chartId));
    } else if (source === 'dashboard') {
      dispatch(onRefresh(chartList, true, 0, dashboardId));
    }
  }
};

Ensure that the redirect_uri, tab_id, and event.origin are correctly set and match the expected values. This validation is crucial for the OAuth2 flow to succeed [1].

Additionally, review the specific error messages or logs generated by Superset when the OAuth2 authentication fails. The relevant error messages are:

  1. OAuth2RedirectError:
    SupersetError(
       message="You don't have permission to access the data.",
       error_type=SupersetErrorType.OAUTH2_REDIRECT,
       level=ErrorLevel.WARNING,
       extra={"url": url, "tab_id": tab_id, "redirect_uri": redirect_uri},
    )
  2. OAuth2Error:
    SupersetError(
       message="Something went wrong while doing OAuth2",
       error_type=SupersetErrorType.OAUTH2_REDIRECT_ERROR,
       level=ErrorLevel.ERROR,
       extra={"error": error},
    )
    <sup>[[2]](https://github.com/apache/superset/blob/master/superset/exceptions.py)</sup><sup>[[3]](https://github.com/apache/superset/blob/master/superset/errors.py)</sup>

By following these steps and ensuring that the configurations are correct, you should be able to resolve the issue and successfully authenticate using Keycloak with Apache Superset.

To continue talking to Dosu, mention @dosu.

This is like a ChatGPT reply, lol.

dosubot[bot] commented 1 month ago

To resolve the issue where, after entering credentials on the Keycloak page, you are redirected back to Apache Superset with the message: "The request to sign in was denied," you need to ensure that the OAuth2 flow is correctly configured and that the necessary permissions are granted.

Here are some steps you can take to troubleshoot and resolve the issue:

  1. Check Redirect URI: Ensure that the redirect_uri configured in Keycloak matches the one expected by Superset. The redirect_uri should be correctly set in both Keycloak and Superset configurations.

  2. Verify OAuth2 Configuration: Make sure that the OAuth2 client configuration in Keycloak is correct. This includes the client ID, client secret, and the allowed redirect URIs.

  3. Inspect Browser Console and Network Logs: Look at the browser console and network logs to see if there are any errors or warnings that can provide more details about why the sign-in request was denied.

  4. Check Superset Logs: Review the Superset logs for any error messages or warnings related to the OAuth2 authentication process. This can provide more insight into what might be going wrong.

  5. Ensure Correct Permissions: Verify that the user has the necessary permissions in Keycloak to access the application.

  6. Cross-Origin Requests: Ensure that cross-origin requests are properly handled and that the event.origin and event.source checks in the handleMessage function are correctly validating the message source.

Here is the relevant part of the code that handles the OAuth2 redirect and message validation:

const handleMessage = (event: MessageEvent) => {
  const redirectUrl = new URL(extra.redirect_uri);
  if (
    event.origin === redirectUrl.origin &&
    event.data.tabId === extra.tab_id &&
    event.source === oAuthTab.current
  ) {
    if (source === 'sqllab' && query) {
      dispatch(reRunQuery(query));
    } else if (source === 'explore' && chartId) {
      dispatch(triggerQuery(true, chartId));
    } else if (source === 'dashboard') {
      dispatch(onRefresh(chartList, true, 0, dashboardId));
    }
  }
};

Ensure that the redirect_uri, tab_id, and event.origin are correctly set and match the expected values. This validation is crucial for the OAuth2 flow to succeed [1].

Additionally, review the specific error messages or logs generated by Superset when the OAuth2 authentication fails. The relevant error messages are:

  1. OAuth2RedirectError:

    SupersetError(
       message="You don't have permission to access the data.",
       error_type=SupersetErrorType.OAUTH2_REDIRECT,
       level=ErrorLevel.WARNING,
       extra={"url": url, "tab_id": tab_id, "redirect_uri": redirect_uri},
    )
  2. OAuth2Error:

    SupersetError(
       message="Something went wrong while doing OAuth2",
       error_type=SupersetErrorType.OAUTH2_REDIRECT_ERROR,
       level=ErrorLevel.ERROR,
       extra={"error": error},
    )
    <sup>[[2]](https://github.com/apache/superset/blob/master/superset/exceptions.py)</sup><sup>[[3]](https://github.com/apache/superset/blob/master/superset/errors.py)</sup>

By following these steps and ensuring that the configurations are correct, you should be able to resolve the issue and successfully authenticate using Keycloak with Apache Superset.

To continue talking to Dosu, mention @dosu.

sfirke commented 1 month ago

Great timing @kormpakis -- can you please take a look at this pending addition to the documentation about how to set up Keycloak auth: https://github.com/apache/superset/pull/29487

PedroMartinSteenstrup commented 1 month ago

just FYI, you can make sure everything is well set up in Keycloak itself, and then just refer to the well-known endpoint. Start with allowing all origins and redirects, to make sure your code work, then you can restrict again.

Here is my working -but truncated- version, without installing any additional package such as Flask-OIDC:

class KeycloakSecurity(SupersetSecurityManager):
    """
    Create a new SecurityManager with own oauth_user_info to handle the information from Keycloak
    """

    def __init__(self, appbuilder):
        super(KeycloakSecurity, self).__init__(appbuilder)
        app = self.appbuilder.get_app
        app.config.setdefault("AUTH_ROLES_MAPPING", {})
        app.config.setdefault("AUTH_TYPE", AUTH_OAUTH)

    def oauth_user_info(self, provider, resp=None):
        if provider == "keycloak":
            log.debug("Keycloak response received : {0}".format(resp))
            log.debug("ID Token: %s", resp["id_token"])
            me = self.appbuilder.sm.oauth_remotes[provider].get(
                f'https://<sso.domain.name>/auth/realms/<realm-name>/protocol/openid-connect/userinfo'
            )
            me.raise_for_status()
            data = me.json()
            log.debug("User info from Keycloak: %s", data)
            return {
                "name": data["name"],
                "email": data["email"],
                "first_name": data["given_name"],
                "last_name": data["family_name"],
                "id": data["preferred_username"],
                "username": data["preferred_username"],
                "role_keys": data["groups"]
            }

CUSTOM_SECURITY_MANAGER = KeycloakSecurity

AUTH_TYPE = AUTH_OAUTH
OAUTH_PROVIDERS = [
    {
        "name": "keycloak",
        "icon": "fa-key",
        "token_key": "access_token",
        "remote_app": {
            "client_id": "superset",
            "client_secret": os.getenv("OAUTH_CLIENT_SECRET", None),
            "client_kwargs": {"scope": "openid email groups"},
            "server_metadata_url": "https://<sso.domain.name>/auth/realms/<realm-name>/.well-known/openid-configuration"
        }
    }]