Search code examples
flaskapache-supersetazure-authenticationflask-oauthlib

Get proper usernames to populate on Superset with Azure SSO instead of ID string


I've finally gotten Azure Single Sign-On (SSO) connected to Apache Superset running via docker-compose, following the Flask docs. Users in my company's Azure group can create and access Superset accounts by logging in with Azure and they are assigned roles based on their identity. This is good.

The usernames they get assigned, however, are long Azure ID strings. These are undesirable in displays. Here's what my account looks like on the List Users screen and on my profile:

enter image description here enter image description here

How can I modify either my Azure application SSO setup or my Superset config to have Superset populate usernames like SFirke for the account usernames, instead of values like 3ee660ff-a274 ... ?

The security part of my config.py looks like this, almost identical to the Flask template:

OAUTH_PROVIDERS = [
{
    "name": "azure",
    "icon": "fa-windows",
    "token_key": "access_token",
    "remote_app": {
        "client_id": "CLIENT_ID",
        "client_secret": "CLIENT_SECRET",
        "api_base_url": "https://login.microsoftonline.com/TENANT_ID/oauth2",
        "client_kwargs": {
            "scope": "User.read name preferred_username email profile upn groups",
            "resource": "RESOURCE_ID",
        },
        "request_token_url": None,
        "access_token_url": "https://login.microsoftonline.com/TENANT_ID/oauth2/token",
        "authorize_url": "https://login.microsoftonline.com/TENANT_ID/oauth2/authorize",
    },
},
]

EDIT: Looks like the way to go is writing a custom userinfo retrieval method, there's a template on the Flask page linked above and an example used for Superset in this Github comment. I think I would use a line like "id": me["preferred_username"] or "id": me["upn"], based on the field names in the Microsoft docs.

But Microsoft notes that this value can change over time and should not be used for authorization changes. Since the oid value is immutable, and it is hardly visible to the typical user, I plan to just stick to it.


Solution

  • I know this question was originally posted 1.5 years ago, but I was having a variety of problems and found the answer to this while I was fixing my own. I am posting it here just in case someone has the same issue or has questions about the same things. The question here was "How can I modify either my Azure application SSO setup or my Superset config to have Superset populate usernames like SFirke for the account usernames, instead of values like 3ee660ff-a274 ... ?" First of all, the default value being used as the username is the user's Object ID, not just some arbitrary value. When using Azure, the default info being used is:

     if provider == "azure":
            me = self._decode_and_validate_azure_jwt(resp["id_token"])
            log.debug("User info from Azure: %s", me)
            # https://learn.microsoft.com/en-us/azure/active-directory/develop/id-token-claims-reference#payload-claims
            return {
                # To keep backward compatibility with previous versions
                # of FAB, we use upn if available, otherwise we use email
                "email": me["upn"] if "upn" in me else me["email"],
                "first_name": me.get("given_name", ""),
                "last_name": me.get("family_name", ""),
                "username": me["oid"],
                "role_keys": me.get("roles", []),
            }
    

    You can see that "oid" is used for the username. You can find the default settings used for each of the authentication provides in Flask App Builders source code. To choose some other option for the details used in each you can use this list of user details for Azure. It is all the details returned when using the User.Read scope. family_name, given_name, name, oid, unique_name, upn There are others returned, but they aren't details you would use for users in Superset. You will use this info when setting up your custom_sso_security_manager.py file. You can't use the default file details they have in the Superset documentation. Here is one you can use for Azure.

    import logging
    from superset.security import SupersetSecurityManager
    
    class CustomSsoSecurityManager(SupersetSecurityManager):
    
    def oauth_user_info(self, provider, response=None):
        logging.debug("Oauth2 provider: {0}.".format(provider))
        if provider == 'azure':
            # As example, this line request a GET to base_url + '/' + userDetails with Bearer  Authentication,
    # and expects that authorization server checks the token, and response with user details
            me = self._decode_and_validate_azure_jwt(response["id_token"])
            logging.debug("user_data: {0}".format(me))
            return { 
                'name' : me['unique_name'],
                'email' : me['upn'] if "upn" in me else me['email'],
                'id' : me['oid'],
                'username' : me['upn'] if "upn" in me else me['email'],
                'first_name': me['given_name'],
                'last_name': me['family_name'],
                'role_keys': me.get('roles', []),
            }
    

    I hope this helps someone.