django_microsoft_auth icon indicating copy to clipboard operation
django_microsoft_auth copied to clipboard

Email issue when using Azure account

Open nicoabergel opened this issue 5 years ago • 7 comments

  • Django Microsoft Authentication Backend version: 2.2.3

  • Django version: 2.2.1

  • Python version: 3.6.8

  • Operating System: Ubuntu 18.04

  • Browser and version: Firefox 66.0.5 (64-bit)

  • Browser extensions/plugins you have installed: Form Filler (2.10.3) by Hussein

Description

I am trying to implement single sign-on on my django application using microsoft auth and Azure AD. I was following the instructions on this page : https://django-microsoft-auth.readthedocs.io/en/latest/usage.html

At step 9 of "Quickstart", There is an error coming from the file /microsoft_auth/backends.py in the _verify_microsoft_user function. The function uses the email from the Azure account to create a new user, but not all azure accounts have an email and it is not possible to add one.

Environment Setup Steps

I am using localhost in debug mode MICROSOFT_AUTH_LOGIN_TYPE = 'ma' There is no email associated to my azure account as I don't have an Office 365 account. (screen provided)

Steps to Reproduce

Follow the instruction on this page : https://django-microsoft-auth.readthedocs.io/en/latest/usage.html Try to log on with an Azure account with no email at step 9. Error should occur.

Environment:Request Method: POST
Request URL: http://localhost:8000/microsoft/auth-callback/Django Version: 2.2.1
Python Version: 3.6.8
Installed Applications:
['videlio_connect',
'widget_tweaks',
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'organization',
'services',
'home',
'users',
'bar',
'django.contrib.sites',
'microsoft_auth']
Installed Middleware:
['django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware']Traceback:File "/home/nicolasabergel/Projects/env/lib/python3.6/site-packages/django/core/handlers/exception.py" in inner
 34.             response = get_response(request)File "/home/nicolasabergel/Projects/env/lib/python3.6/site-packages/django/core/handlers/base.py" in _get_response
 115.                 response = self.process_exception_by_middleware(e, request)File "/home/nicolasabergel/Projects/env/lib/python3.6/site-packages/django/core/handlers/base.py" in _get_response
 113.                 response = wrapped_callback(request, *callback_args, **callback_kwargs)File "/home/nicolasabergel/Projects/env/lib/python3.6/site-packages/django/views/generic/base.py" in view
 71.             return self.dispatch(request, *args, **kwargs)File "/home/nicolasabergel/Projects/env/lib/python3.6/site-packages/django/utils/decorators.py" in _wrapper
 45.         return bound_method(*args, **kwargs)File "/home/nicolasabergel/Projects/env/lib/python3.6/site-packages/django/views/decorators/csrf.py" in wrapped_view
 54.         return view_func(*args, **kwargs)File "/home/nicolasabergel/Projects/env/lib/python3.6/site-packages/microsoft_auth/views.py" in dispatch
 47.         return super().dispatch(request, *args, **kwargs)File "/home/nicolasabergel/Projects/env/lib/python3.6/site-packages/django/views/generic/base.py" in dispatch
 97.         return handler(request, *args, **kwargs)File "/home/nicolasabergel/Projects/env/lib/python3.6/site-packages/microsoft_auth/views.py" in post
 145.         context = self.get_context_data(**request.POST.dict())File "/home/nicolasabergel/Projects/env/lib/python3.6/site-packages/microsoft_auth/views.py" in get_context_data
 68.         self._authenticate(kwargs.get("code"))File "/home/nicolasabergel/Projects/env/lib/python3.6/site-packages/microsoft_auth/views.py" in _authenticate
 129.                 user = authenticate(self.request, code=code)File "/home/nicolasabergel/Projects/env/lib/python3.6/site-packages/django/contrib/auth/__init__.py" in authenticate
 73.             user = backend.authenticate(request, **credentials)File "/home/nicolasabergel/Projects/env/lib/python3.6/site-packages/microsoft_auth/backends.py" in authenticate
 49.                 user = self._authenticate_user()File "/home/nicolasabergel/Projects/env/lib/python3.6/site-packages/microsoft_auth/backends.py" in _authenticate_user
 60.             return self._authenticate_microsoft_user()File "/home/nicolasabergel/Projects/env/lib/python3.6/site-packages/microsoft_auth/backends.py" in _authenticate_microsoft_user
 74.             return self._get_user_from_microsoft(claims)File "/home/nicolasabergel/Projects/env/lib/python3.6/site-packages/microsoft_auth/backends.py" in _get_user_from_microsoft
 132.             user = self._verify_microsoft_user(microsoft_user, data)File "/home/nicolasabergel/Projects/env/lib/python3.6/site-packages/microsoft_auth/backends.py" in _verify_microsoft_user
 162.                 user = User.objects.get(email=data["email"])
Exception Type: KeyError at /microsoft/auth-callback/
Exception Value: 'email''

Screenshot from 2019-06-03 10-34-52

nicoabergel avatar Jun 03 '19 08:06 nicoabergel

Huh. I never would have guessed you could make an Azure AD tenant that does not have emails. Thanks for letting me know.

What is the use case for not having an email for authenticating? What do you use to log into the Microsoft log in form if you do not have an email? Can you give me an user and OIDC client on your Azure AD Tenant for me to test?

I do not have a lot of experience with Azure and would have no idea how to set this up myself, so I would need all of these questions answered to properly understand the problem and decide on a solution. Otherwise, PRs are welcome.

AngellusMortis avatar Jun 03 '19 13:06 AngellusMortis

For instance, my username is "[email protected]". It was provided by Azure, and only acts as a username, and nothing else. The email field on the profile is actually blank. To log into the Microsoft log in form, I put my username and a password, they don't ask for an email. Nonetheless, the creation of the user require an email which can't be provided by the Azure profile.

We are working on a patch that does not require the email field to create the user and will suggest a PR to you soon.

We are trying to make you a profile on our Azure AD. We will get back to you soon.

nicoabergel avatar Jun 03 '19 14:06 nicoabergel

We are working on a patch that does not require the email field to create the user and will suggest a PR to you soon.

Then I will leave you to it! Thanks for the help. If you tox/Travis tests fail, I will likely contribute to your PR to help you fix them as I have all of the Python versions and such already set up on my machine.

AngellusMortis avatar Jun 03 '19 14:06 AngellusMortis

Alright.

nicoabergel avatar Jun 03 '19 14:06 nicoabergel

Hi guys,

I'm having exactly the same issue as @nicoabergel.

I've made some modifications to the code to make it work but for some reason I'm unable to execute python3 -m pip install --user --upgrade pip as specified in CONTRIBUTING.rst meaning that I can't run flake8 or the tests.

If I submit a pull request are you able to run the tests @AngellusMortis?

gskudder-leadtosale avatar Jul 24 '19 04:07 gskudder-leadtosale

Had the same issue when testing my application with a "dummy" domain. Emails are indeed not created in Azure for the .onmicrosoft.com domains I created. I "fixed" it by inviting users from our real domain into my dummy domain for testing. Could be nice if we could rely on username in cases where emails do not exists.

monkeyclass avatar Oct 21 '19 08:10 monkeyclass

I fixed the issue on my site overriding the backend and using the "preferred_username" field as the email if there is none. Take into consideration that this is not an actual email address, so you can't send emails to these users.

# my_users_app/backends.py
from microsoft_auth.backends import MicrosoftAuthenticationBackend as BaseMicrosoftAuthenticationBackend


class MicrosoftAuthenticationBackend(BaseMicrosoftAuthenticationBackend):
    def _verify_microsoft_user(self, microsoft_user, data):
        if 'email' not in data:
            data['email'] = data['preferred_username']
        return super()._verify_microsoft_user(microsoft_user, data)
# settings.py
AUTHENTICATION_BACKENDS = [
    ...
    # 'microsoft_auth.backends.MicrosoftAuthenticationBackend', (REMOVE)
    'my_users_app.backends.MicrosoftAuthenticationBackend',
]

p14z avatar Oct 20 '20 20:10 p14z