如何使用 Authlib 为 Superset 配置 Keycloak?
How to configure Keycloak for Superset with Authlib?
我正在尝试使用 Authlib 将 Keycloak 设置为 Superset 的 SSO。一切正常,直到用户被重定向回 Superset。然后就出现了这个错误:
authlib.integrations.base_client.errors.MismatchingStateError: mismatching_state: CSRF Warning! State not equal in request and response.
这是我的代码:
在superset_config.py
AUTH_TYPE = AUTH_OAUTH
AUTH_USER_REGISTRATION = True
AUTH_USER_REGISTRATION_ROLE = 'Public'
CSRF_ENABLED = True
ENABLE_PROXY_FIX = True
OAUTH_PROVIDERS = [
{
'name': 'keycloak',
'token_key': 'access_token',
'icon': 'fa-icon',
'remote_app': {
'client_id': CLIENT_ID,
'client_secret': CLIENT_SECRET,
'client_kwargs': {
'scope': 'openid email profile'
},
'access_token_method': 'POST',
'api_base_url': 'https://KEYCLOAK_URL/auth/realms/REALM_NAME/protocol/openid-connect/',
'access_token_url': 'https://KEYCLOAK_URL/auth/realms/REALM_NAME/protocol/openid-connect/token',
'authorize_url': 'https://KEYCLOAK_URL/auth/realms/REALM_NAME/protocol/openid-connect/auth',
},
}
]
CUSTOM_SECURITY_MANAGER = OIDCSecurityManager
这是我的 OIDCSecurityManager:
class OIDCSecurityManager(SupersetSecurityManager):
def get_oauth_user_info(self, provider, response=None):
if provider == 'keycloak':
me = self.appbuilder.sm.oauth_remotes[provider].get("userinfo")
return {
"first_name": me.data.get("given_name", ""),
"last_name": me.data.get("family_name", ""),
"email": me.data.get("email", "")
}
我该怎么做才能解决这个问题? (这发生在所有浏览器 chrome、firefox 等)
我在 OIDC 配置方面也遇到了问题,但这个安全管理器配置适合我。
注意 - 我已经在 Keycloak 和映射器中为我的客户端配置添加了角色,因此可以从 user info
响应中选择角色。
class CustomSsoSecurityManager(SupersetSecurityManager):
def oauth_user_info(self, provider, response=None):
logging.debug("Oauth2 provider: {0}.".format(provider))
logging.debug("Oauth2 oauth_remotes provider: {0}.".format(self.appbuilder.sm.oauth_remotes[provider]))
if provider == 'keycloak':
# Get the user info using the access token
res = self.appbuilder.sm.oauth_remotes[provider].get(os.getenv('KEYCLOAK_BASE_URL') + '/userinfo')
logger.info(f"userinfo response:")
for attr, value in vars(res).items():
print(attr, '=', value)
if res.status_code != 200:
logger.error('Failed to obtain user info: %s', res._content)
return
#dict_str = res._content.decode("UTF-8")
me = json.loads(res._content)
logger.debug(" user_data: %s", me)
return {
'username' : me['preferred_username'],
'name' : me['name'],
'email' : me['email'],
'first_name': me['given_name'],
'last_name': me['family_name'],
'roles': me['roles'],
'is_active': True,
}
def auth_user_oauth(self, userinfo):
user = super(CustomSsoSecurityManager, self).auth_user_oauth(userinfo)
roles = [self.find_role(x) for x in userinfo['roles']]
roles = [x for x in roles if x is not None]
user.roles = roles
logger.debug(' Update <User: %s> role to %s', user.username, roles)
self.update_user(user) # update user roles
return user
我正在尝试使用 Authlib 将 Keycloak 设置为 Superset 的 SSO。一切正常,直到用户被重定向回 Superset。然后就出现了这个错误:
authlib.integrations.base_client.errors.MismatchingStateError: mismatching_state: CSRF Warning! State not equal in request and response.
这是我的代码:
在superset_config.py
AUTH_TYPE = AUTH_OAUTH
AUTH_USER_REGISTRATION = True
AUTH_USER_REGISTRATION_ROLE = 'Public'
CSRF_ENABLED = True
ENABLE_PROXY_FIX = True
OAUTH_PROVIDERS = [
{
'name': 'keycloak',
'token_key': 'access_token',
'icon': 'fa-icon',
'remote_app': {
'client_id': CLIENT_ID,
'client_secret': CLIENT_SECRET,
'client_kwargs': {
'scope': 'openid email profile'
},
'access_token_method': 'POST',
'api_base_url': 'https://KEYCLOAK_URL/auth/realms/REALM_NAME/protocol/openid-connect/',
'access_token_url': 'https://KEYCLOAK_URL/auth/realms/REALM_NAME/protocol/openid-connect/token',
'authorize_url': 'https://KEYCLOAK_URL/auth/realms/REALM_NAME/protocol/openid-connect/auth',
},
}
]
CUSTOM_SECURITY_MANAGER = OIDCSecurityManager
这是我的 OIDCSecurityManager:
class OIDCSecurityManager(SupersetSecurityManager):
def get_oauth_user_info(self, provider, response=None):
if provider == 'keycloak':
me = self.appbuilder.sm.oauth_remotes[provider].get("userinfo")
return {
"first_name": me.data.get("given_name", ""),
"last_name": me.data.get("family_name", ""),
"email": me.data.get("email", "")
}
我该怎么做才能解决这个问题? (这发生在所有浏览器 chrome、firefox 等)
我在 OIDC 配置方面也遇到了问题,但这个安全管理器配置适合我。
注意 - 我已经在 Keycloak 和映射器中为我的客户端配置添加了角色,因此可以从 user info
响应中选择角色。
class CustomSsoSecurityManager(SupersetSecurityManager):
def oauth_user_info(self, provider, response=None):
logging.debug("Oauth2 provider: {0}.".format(provider))
logging.debug("Oauth2 oauth_remotes provider: {0}.".format(self.appbuilder.sm.oauth_remotes[provider]))
if provider == 'keycloak':
# Get the user info using the access token
res = self.appbuilder.sm.oauth_remotes[provider].get(os.getenv('KEYCLOAK_BASE_URL') + '/userinfo')
logger.info(f"userinfo response:")
for attr, value in vars(res).items():
print(attr, '=', value)
if res.status_code != 200:
logger.error('Failed to obtain user info: %s', res._content)
return
#dict_str = res._content.decode("UTF-8")
me = json.loads(res._content)
logger.debug(" user_data: %s", me)
return {
'username' : me['preferred_username'],
'name' : me['name'],
'email' : me['email'],
'first_name': me['given_name'],
'last_name': me['family_name'],
'roles': me['roles'],
'is_active': True,
}
def auth_user_oauth(self, userinfo):
user = super(CustomSsoSecurityManager, self).auth_user_oauth(userinfo)
roles = [self.find_role(x) for x in userinfo['roles']]
roles = [x for x in roles if x is not None]
user.roles = roles
logger.debug(' Update <User: %s> role to %s', user.username, roles)
self.update_user(user) # update user roles
return user