AngellusMortis / django_microsoft_auth

Simple app to enable Microsoft Account, Office 365 and Xbox Live authentcation as a Django authentcation backend.
MIT License
137 stars 84 forks source link

Multiple users with same email causes barf #463

Open hazraa opened 2 years ago

hazraa commented 2 years ago

.../site-packages/microsoft_auth/backends.py in _verify_microsoft_user

  1. user = User.objects.get(email=data["email"])

Maybe particular to our 'incorrect' use case where django admin users can be setup manually and then are tied to their AD user by the previously manually entered email address. The 'get()' ofcourse barfs on multiples.

bartTC commented 2 years ago

We had the same problem, I've overwritten the Backend to make a case insensitive check. I wouldn't say its incorrect. On MS we store emails CamelCase while Django uses lowercase.

import logging

from django.contrib.auth import get_user_model
from microsoft_auth.backends import MicrosoftAuthenticationBackend

logger = logging.getLogger("django")
User = get_user_model()

class CIUserMicrosoftAuthenticationBackend(MicrosoftAuthenticationBackend):
    """
    Extend the original auth backend to use and compare lowercase
    usernames/emails. These methods might need to be updated if the
    upstream class changes.
    """

    def _verify_microsoft_user(self, microsoft_user, data):
        user = microsoft_user.user

        if user is None:
            fullname = data.get("name")
            first_name, last_name = "", ""
            if fullname is not None:
                try:
                    # LastName, FirstName format
                    last_name, first_name = fullname.split(", ")
                except ValueError:
                    try:
                        first_name, last_name = fullname.split(" ", 1)
                    except ValueError:
                        firstname = fullname

            try:
                # create new Django user from provided data
                user = User.objects.get(
                    username__iexact=data["email"].lower()
                )  # ⚠️ Change here

                if user.first_name == "" and user.last_name == "":
                    user.first_name = first_name
                    user.last_name = last_name
                    user.save()
            except User.DoesNotExist:
                user = User(
                    username=data["email"].lower(),  # ⚠️ Change here
                    first_name=first_name,
                    last_name=last_name,
                    email=data["email"].lower(),  # ⚠️ Change here
                )
                user.save()

            existing_account = self._get_existing_microsoft_account(user)
            if existing_account is not None:
                if self.config.MICROSOFT_AUTH_AUTO_REPLACE_ACCOUNTS:
                    existing_account.user = None
                    existing_account.save()
                else:
                    logger.warning(
                        (
                            "User {} already has linked Microsoft "
                            "account and MICROSOFT_AUTH_AUTO_REPLACE_ACCOUNTS "
                            "is False"
                        ).format(user.email)
                    )
                    return None

            microsoft_user.user = user
            microsoft_user.save()

        return user