diff --git a/docs/security.rst b/docs/security.rst index 9deab25af6..6736b157c1 100644 --- a/docs/security.rst +++ b/docs/security.rst @@ -262,7 +262,21 @@ Specify a list of OAUTH_PROVIDERS in **config.py** that you want to allow for yo "client_kwargs": {"scope": "openid profile email groups"}, "access_token_url": "https://OKTA_DOMAIN.okta.com/oauth2/v1/token", "authorize_url": "https://OKTA_DOMAIN.okta.com/oauth2/v1/authorize", - "server_metadata_url": f"https://OKTA_DOMAIN.okta.com/.well-known/openid-configuration", + "server_metadata_url": "https://OKTA_DOMAIN.okta.com/.well-known/openid-configuration", + }, + }, + { + "name": "auth0", + "icon": "fa-shield-halved", + "token_key": "access_token", + "remote_app": { + "client_id": "AUTH0_KEY", + "client_secret": "AUTH0_SECRET", + "api_base_url": "https://AUTH0_DOMAIN/oauth2/v1/", + "client_kwargs": {"scope": "openid profile email groups"}, + "access_token_url": "https://AUTH0_DOMAIN/oauth/token", + "authorize_url": "https://AUTH0_DOMAIN/authorize", + "server_metadata_url": "https://AUTH0_DOMAIN/.well-known/openid-configuration", }, }, { diff --git a/flask_appbuilder/security/manager.py b/flask_appbuilder/security/manager.py index 20b35d2644..80a91b8cf4 100644 --- a/flask_appbuilder/security/manager.py +++ b/flask_appbuilder/security/manager.py @@ -649,10 +649,21 @@ def get_oauth_user_info( data = me.json() log.debug("User info from Okta: %s", data) return { - "username": "okta_" + data.get("sub", ""), + "username": f"{provider}_{data['sub']}", "first_name": data.get("given_name", ""), "last_name": data.get("family_name", ""), - "email": data.get("email", ""), + "email": data["email"], + "role_keys": data.get("groups", []), + } + # for Auth0 + if provider == "auth0": + data = self.appbuilder.sm.oauth_remotes[provider].userinfo() + log.debug("User info from Auth0: %s", data) + return { + "username": f"{provider}_{data['sub']}", + "first_name": data.get("given_name", ""), + "last_name": data.get("family_name", ""), + "email": data["email"], "role_keys": data.get("groups", []), } # for Keycloak diff --git a/tests/security/test_auth_oauth.py b/tests/security/test_auth_oauth.py index acf4b9e833..799ce46a6d 100644 --- a/tests/security/test_auth_oauth.py +++ b/tests/security/test_auth_oauth.py @@ -1,6 +1,7 @@ import logging import os import unittest +from unittest.mock import MagicMock from flask import Flask from flask_appbuilder import AppBuilder, SQLA @@ -47,7 +48,22 @@ def setUp(self): "AZURE_APPLICATION_ID/" "oauth2/authorize", }, - } + }, + { + "name": "auth0", + "icon": "fa-shield-halved", + "token_key": "access_token", + "remote_app": { + "client_id": "AUTH0_KEY", + "client_secret": "AUTH0_SECRET", + "api_base_url": "https://AUTH0_DOMAIN/oauth2/v1/", + "client_kwargs": {"scope": "openid profile email groups"}, + "access_token_url": "https://AUTH0_DOMAIN/oauth/token", + "authorize_url": "https://AUTH0_DOMAIN/authorize", + "server_metadata_url": "https://AUTH0_DOMAIN/.well-known/" + "openid-configuration", + }, + }, ] # start Database @@ -652,3 +668,30 @@ def test_oauth_user_info_azure_with_jwt_validation(self): "username": "b1a54a40-8dfa-4a6d-a2b8-f90b84d4b1df", }, ) + + def test_oauth_user_info_auth0(self): + self.appbuilder = AppBuilder(self.app, self.db.session) + + self.appbuilder.sm.oauth_remotes["auth0"].userinfo = MagicMock( + return_value={ + "email": "test@gmail.com", + "given_name": "test", + "family_name": "user", + "role_keys": [], + "sub": "test-sub", + } + ) + + user_info = self.appbuilder.sm.get_oauth_user_info( + "auth0", {"access_token": "", "id_token": ""} + ) + self.assertEqual( + user_info, + { + "email": "test@gmail.com", + "first_name": "test", + "last_name": "user", + "role_keys": [], + "username": "auth0_test-sub", + }, + )