GitHub user serra edited a discussion: Use Authelia as OpenID Connect 
authorization provider in Superset

## Context

[Authelia] is a 2FA & SSO authentication server which is dedicated to the 
security of applications and users.
We use Authelia for SSO across several web applications, Superset amongst 
others.
In our case, Authelia is backed by [LLDAP] as a directory service.

## Requirements

- Given a user is in the directory, when they login successfully, they should 
be authorized with the `Gamma` role.
- Given a user is in the `superset_admin` group in LLDAP, then they should get 
the `Admin` role in superset.
- Given a user is in additional LLDAP groups and those groups are roles in 
Superset, then they should get these roles in Superset.

## Solution

Superset is built using [Flask App Builder] (FAB), which comes with 
authorization support for LDAP and OAuth2 amongst others.
We can configure an OpenID Connect (OIDC) client in Authelia, and then consume 
this service as an OAuth2 client from Superset.
OIDC is an extension of OAuth2. OIDC is not explicitly supported by FAB. We can 
however use the OIDC endpoint for more concise configuration (see [Superset 
OAuth2 configuration docs]). Using the OAuth2 implementation in FAB also gives 
us automatic user creation and role synchronization, if we need it.

FAB comes with support for several OAuth2 providers (Facebook, Github, and 
Authentik to name a few), but does not have built-in support for Authelia yet. 
So we have to define a function that maps the user info from Authelia to the 
`user_info` structure used by FAB. We do this by creating a custom 
`SecurityManager`:

```Python
from superset.security import SupersetSecurityManager
import logging

logger = logging.getLogger(__name__)


class AutheliaSecurityManager(SupersetSecurityManager):

    def oauth_user_info(self, provider, response=None):
        logger.debug("Oauth2 provider: {0}.".format(provider))
        if provider == "authelia":
            # Use the full userinfo endpoint URL from OIDC discovery
            userinfo_endpoint = self.appbuilder.sm.oauth_remotes[
                provider
            ].server_metadata["userinfo_endpoint"]
            resp = 
self.appbuilder.sm.oauth_remotes[provider].get(userinfo_endpoint)
            data = resp.json()
            logger.debug(data)
            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("groups", []),
            }
        return {}

```

Then use this security manager in `superset_config.py`:

```python
# ...
AUTH_TYPE = AUTH_OAUTH

OIDC_CLIENT_SECRETS = "/app/docker/pythonpath_dev/client_secret.json"

with open(OIDC_CLIENT_SECRETS) as f:
    oidc_secrets = json.load(f)
    authelia_secrets = oidc_secrets["authelia"]

remote_app = {
    "client_id": authelia_secrets["client_id"],
    "client_secret": authelia_secrets["client_secret"],
    "server_metadata_url": 
f"{authelia_secrets['issuer']}/.well-known/openid-configuration",
    "token_endpoint_auth_method": "client_secret_post",
    "scope": "openid email profile groups",
}

OAUTH_PROVIDERS = [
    {
        "name": "authelia",
        "token_key": "access_token",
        "icon": "fa-address-card",
        "remote_app": remote_app,
    }
]

CUSTOM_SECURITY_MANAGER = AutheliaSecurityManager

# These are all flask app builder settings:
AUTH_USER_REGISTRATION = True
AUTH_USER_REGISTRATION_ROLE = "Gamma"
AUTH_ROLES_SYNC_AT_LOGIN = True

# Role mapping (optional - you can customize this based on your needs)
AUTH_ROLES_MAPPING = {
    "superset_admin": ["Admin"],
    "superset": ["Gamma"],
    "custom_group": ["custom_matching_superset_role_A", 
"custom_matching_superset_role_B"],
    # ...
}

```



## Follow up

These are some ideas to follow-up:

- For some reason, `First Name` and `Last Name` fields are not filled in 
Superset, although they are in the mapped user info
- Change this implementation to allow for multiple OAuth login providers
- Add Authelia OAuth2 support to FAB
- Add this integration guide to the list of [OIDC integrations in the Authelia 
docs](https://www.authelia.com/integration/prologue/introduction/)


## Appendices

### Authelia configuration

I assume that you have Authelia setup and running, and that you know how to 
generate secrets for your clients.
I use the following client configuration:

```yml
identity_providers:
  oidc:
  #...
  clients:
    # ...
    - client_id: superset
      client_name: Apache Superset
      client_secret: '{{ fileContent "/config/secrets/oidc_superset_secret" | 
trim }}'
      public: false
      authorization_policy: one_factor
      redirect_uris:
        - 'http://superset.dev.serraict.me/oauth-authorized/authelia'
        - 'https://superset.dev.serraict.me/oauth-authorized/authelia'
        - 'http://superset.dev.serraict.me/authorize'
        - 'https://superset.dev.serraict.me/authorize'
        - 'https://superset.dev.serraict.me/oidc_callback'
      scopes:
        - openid
        - email
        - profile
        - groups
      userinfo_signed_response_alg: none
      token_endpoint_auth_method: client_secret_post
```

When generating the secret for superset, I also generate an OIDC 
`client_secret.json`:

```json
{
  "authelia": {
    "issuer": "https://auth.dev.serraict.me";,
    "client_id": "superset",
    "client_secret": "the_oidc_superset_secret",
    "redirect_uris": [
        "https://superset.dev.serraict.me/oidc_callback";
    ]
  }
}
```




---

  [LLDAP]: https://github.com/lldap/lldap
  [Flask App Builder]: https://flask-appbuilder.readthedocs.io/en/latest/
  [Authelia]: https://www.authelia.com/overview/prologue/introduction/
  [Superset configuration docs]: 
https://superset.apache.org/docs/configuration/configuring-superset/
  [Superset OAuth2 configuration docs]: 
https://superset.apache.org/docs/configuration/configuring-superset/#custom-oauth2-configuration

GitHub link: https://github.com/apache/superset/discussions/34054

----
This is an automatically sent email for [email protected].
To unsubscribe, please send an email to: 
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to