From 2a90c0b35eb52234c0d34c58add0f6a37a0c7d2d Mon Sep 17 00:00:00 2001 From: Jens Langhammer Date: Sat, 21 Aug 2021 15:12:18 +0200 Subject: [PATCH] sources/oauth2: migrate to microsoft graph instead of azure graph Signed-off-by: Jens Langhammer --- authentik/lib/sentry.py | 6 +-- .../sources/oauth/tests/test_type_azure_ad.py | 46 +++++++++++++++++++ authentik/sources/oauth/types/azure_ad.py | 19 ++++++-- 3 files changed, 64 insertions(+), 7 deletions(-) create mode 100644 authentik/sources/oauth/tests/test_type_azure_ad.py diff --git a/authentik/lib/sentry.py b/authentik/lib/sentry.py index 627df3f53..7b847458f 100644 --- a/authentik/lib/sentry.py +++ b/authentik/lib/sentry.py @@ -14,9 +14,6 @@ from django.http.response import Http404 from django_redis.exceptions import ConnectionInterrupted from docker.errors import DockerException from ldap3.core.exceptions import LDAPException - -# pylint: disable=no-name-in-module -from psycopg2.errors import Error from redis.exceptions import ConnectionError as RedisConnectionError from redis.exceptions import RedisError, ResponseError from rest_framework.exceptions import APIException @@ -48,6 +45,9 @@ class SentryIgnoredException(Exception): def before_send(event: dict, hint: dict) -> Optional[dict]: """Check if error is database error, and ignore if so""" + # pylint: disable=no-name-in-module + from psycopg2.errors import Error + ignored_classes = ( # Inbuilt types KeyboardInterrupt, diff --git a/authentik/sources/oauth/tests/test_type_azure_ad.py b/authentik/sources/oauth/tests/test_type_azure_ad.py new file mode 100644 index 000000000..b2d8b9f5f --- /dev/null +++ b/authentik/sources/oauth/tests/test_type_azure_ad.py @@ -0,0 +1,46 @@ +"""azure ad Type tests""" +from django.test import TestCase + +from authentik.sources.oauth.models import OAuthSource +from authentik.sources.oauth.types.azure_ad import AzureADOAuthCallback + +# https://docs.microsoft.com/en-us/graph/api/user-get?view=graph-rest-1.0&tabs=http#response-2 +AAD_USER = { + "@odata.context": "https://graph.microsoft.com/v1.0/$metadata#users/$entity", + "@odata.id": ( + "https://graph.microsoft.com/v2/7ce9b89e-646a-41d2-9fa6-8371c6a8423d/" + "directoryObjects/018b0aff-8aff-473e-bf9c-b50e27f52208/Microsoft.DirectoryServices.User" + ), + "businessPhones": [], + "displayName": "foo bar", + "givenName": "foo", + "jobTitle": None, + "mail": "foo@beryju.org", + "mobilePhone": None, + "officeLocation": None, + "preferredLanguage": None, + "surname": "bar", + "userPrincipalName": "foo@beryju.org", + "id": "018b0aff-8aff-473e-bf9c-b50e27f52208", +} + + +class TestTypeAzureAD(TestCase): + """OAuth Source tests""" + + def setUp(self): + self.source = OAuthSource.objects.create( + name="test", + slug="test", + provider_type="openid-connect", + authorization_url="", + profile_url="", + consumer_key="", + ) + + def test_enroll_context(self): + """Test azure_ad Enrollment context""" + ak_context = AzureADOAuthCallback().get_user_enroll_context(AAD_USER) + self.assertEqual(ak_context["username"], AAD_USER["displayName"]) + self.assertEqual(ak_context["email"], AAD_USER["mail"]) + self.assertEqual(ak_context["name"], AAD_USER["displayName"]) diff --git a/authentik/sources/oauth/types/azure_ad.py b/authentik/sources/oauth/types/azure_ad.py index e6066de21..2893aabdf 100644 --- a/authentik/sources/oauth/types/azure_ad.py +++ b/authentik/sources/oauth/types/azure_ad.py @@ -8,10 +8,20 @@ from structlog.stdlib import get_logger from authentik.sources.oauth.clients.oauth2 import OAuth2Client from authentik.sources.oauth.types.manager import MANAGER, SourceType from authentik.sources.oauth.views.callback import OAuthCallback +from authentik.sources.oauth.views.redirect import OAuthRedirect LOGGER = get_logger() +class AzureADOAuthRedirect(OAuthRedirect): + """Azure AD OAuth2 Redirect""" + + def get_additional_parameters(self, source): # pragma: no cover + return { + "scope": "openid https://graph.microsoft.com/User.Read", + } + + class AzureADClient(OAuth2Client): """Azure AD Oauth client, azure ad doesn't like the ?access_token that is sent by default""" @@ -42,7 +52,7 @@ class AzureADOAuthCallback(OAuthCallback): def get_user_id(self, info: dict[str, Any]) -> Optional[str]: try: - return str(UUID(info.get("objectId")).int) + return str(UUID(info.get("id")).int) except TypeError: return None @@ -63,11 +73,12 @@ class AzureADType(SourceType): """Azure AD Type definition""" callback_view = AzureADOAuthCallback + redirect_view = AzureADOAuthRedirect name = "Azure AD" slug = "azure-ad" urls_customizable = True - authorization_url = "https://login.microsoftonline.com/common/oauth2/authorize" - access_token_url = "https://login.microsoftonline.com/common/oauth2/token" # nosec - profile_url = "https://graph.windows.net/myorganization/me?api-version=1.6" + authorization_url = "https://login.microsoftonline.com/common/oauth2/v2.0/authorize" + access_token_url = "https://login.microsoftonline.com/common/oauth2/v2.0/token" # nosec + profile_url = "https://graph.microsoft.com/v1.0/me"