'Implementing OpenID with Apache Superset. AttributeError: 'bool' object has no attribute 'is_active'
I am trying to implement OpenID in Apache Superset. I am using Broadcom CA-SSO as my provider. I found a similar post in Stack Overflow where someone implemented with Keycloak (Using KeyCloak(OpenID Connect) with Apache SuperSet) I found the post helpful in my implementation. I have followed the similar approach.
I have been able to authenticate my user with CA-SSO. However, when the browser reaches the callback route, superset is throwing the following error:
Traceback (most recent call last):
File "/usr/local/lib/python3.7/site-packages/flask/app.py", line 2464,
in __call__
return self.wsgi_app(environ, start_response)
File "/usr/local/lib/python3.7/site-packages/flask/app.py", line 2450,
in wsgi_app
response = self.handle_exception(e)
File "/usr/local/lib/python3.7/site-packages/flask/app.py", line 1867,
in handle_exception
reraise(exc_type, exc_value, tb)
File "/usr/local/lib/python3.7/site-packages/flask/_compat.py", line
39, in reraise
raise value
File "/usr/local/lib/python3.7/site-packages/flask/app.py", line 2447,
in wsgi_app
response = self.full_dispatch_request()
File "/usr/local/lib/python3.7/site-packages/flask/app.py", line 1952,
in full_dispatch_request
rv = self.handle_user_exception(e)
File "/usr/local/lib/python3.7/site-packages/flask/app.py", line 1821,
in handle_user_exception
reraise(exc_type, exc_value, tb)
File "/usr/local/lib/python3.7/site-packages/flask/_compat.py", line
39, in reraise
raise value
File "/usr/local/lib/python3.7/site-packages/flask/app.py", line 1950,
in full_dispatch_request
rv = self.dispatch_request()
File "/usr/local/lib/python3.7/site-packages/flask/app.py", line 1936,
in dispatch_request
return self.view_functions[rule.endpoint](**req.view_args)
File "/app/superset/custom_security.py", line 34, in login
return handle_login()
File "/usr/local/lib/python3.7/site-packages/flask_oidc/__init__.py",
line 487, in decorated
return view_func(*args, **kwargs)
File "/app/superset/custom_security.py", line 31, in handle_login
login_user(user, remember=False)
File "/usr/local/lib/python3.7/site-packages/flask_login/utils.py",
line 158, in login_user
if not force and not user.is_active:
AttributeError: 'bool' object has no attribute 'is_active'
Here is my implementation:
client_secrets.json
{
"web": {
"issuer": "https://<SSO DOMAIN>",
"auth_uri": "https://<SSO DOMAIN>/authorize",
"client_id": "<CLIENT ID>",
"client_secret": "<CLIENT SECRET>",
"redirect_uris": [
"https://<YOUR DOMAIN>/oidc_callback"
],
"userinfo_uri": "https://<SSO DOMAIN>/userinfo",
"token_uri": "https://<SSO DOMAIN>/token",
"token_introspection_uri": "https://<SSO DOMAIN>/introspect"
}
}
security.py
from flask import redirect, request
from flask_appbuilder.security.manager import AUTH_OID
from superset.security import SupersetSecurityManager
from flask_oidc import OpenIDConnect
from flask_appbuilder.security.views import AuthOIDView
from flask_login import login_user
from urllib.parse import quote
from flask_appbuilder.views import ModelView, SimpleFormView, expose
import logging
class AuthOIDCView(AuthOIDView):
@expose('/login/', methods=['GET', 'POST'])
def login(self, flag=True):
sm = self.appbuilder.sm
oidc = sm.oid
@self.appbuilder.sm.oid.require_login
def handle_login():
user = sm.auth_user_oid(oidc.user_getfield('email'))
if user is None:
info = oidc.user_getinfo(['preferred_username', 'given_name', 'family_name', 'email'])
user = sm.add_user(info.get('preferred_username'), info.get('given_name'), info.get('family_name'), info.get('email'), sm.find_role('Gamma'))
login_user(user, remember=False)
return redirect(self.appbuilder.get_url_for_index)
return handle_login()
@expose('/logout/', methods=['GET', 'POST'])
def logout(self):
oidc = self.appbuilder.sm.oid
oidc.logout()
super(AuthOIDCView, self).logout()
redirect_url = request.url_root.strip('/') + self.appbuilder.get_url_for_login
return redirect(oidc.client_secrets.get('issuer') + '/protocol/openid-connect/logout?redirect_uri=' + quote(redirect_url))
class OIDCSecurityManager(SupersetSecurityManager):
authoidview = AuthOIDCView
def __init__(self,appbuilder):
super(OIDCSecurityManager, self).__init__(appbuilder)
if self.auth_type == AUTH_OID:
self.oid = OpenIDConnect(self.appbuilder.get_app)
In superset/config.py
from security import OIDCSecurityManager
AUTH_TYPE = AUTH_OID
OIDC_CLIENT_SECRETS = <path_to_configuration_file>
OIDC_ID_TOKEN_COOKIE_SECURE = False
OIDC_REQUIRE_VERIFIED_EMAIL = False
AUTH_USER_REGISTRATION = True
AUTH_USER_REGISTRATION_ROLE = 'Gamma'
CUSTOM_SECURITY_MANAGER = OIDCSecurityManager
OVERWRITE_REDIRECT_URI = 'https://<YOUR DOMAIN>/oidc_callback'
I am using flask-oidc
. I am using the default callback /oidc_callback
. When superset is redirected to /authorize
endpoint, the redirect_uri was not picked up correctly. It was picking up localhost rather than the domain name, hence I had to use OVERWRITE_REDIRECT_URI
flag. It seemed to have fixed that.
Also, I am using Nginx reverse-proxy
Also one weird thing that I noticed. I decoded the state
query parameter from the /authorize
Url. It is an object with key csrf_token
and destination
. I then decoded the destination
. It contains the algorithm value and my login route. However, the login URL had localhost (http://127.0.0.1:8088/login/
) rather then https://<YOUR DOMAIN>/login
. Is this correct?
I am using superset docker to run the app. I'm new to python-flask.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|