Social Authentication (dj_rest_auth) with multiple role user

46 Views Asked by At

#user_model:

class User(AbstractUser, PermissionsMixin):

    class Role(models.TextChoices):
        SELLER = "seller"
        BUYER = "buyer"
        SUPER = "super"

    name = models.CharField(max_length=100)
    phone = models.CharField(max_length=15)
    email = models.EmailField()
    created_at = models.DateTimeField(default=timezone.now)
    shipping_address = models.CharField(max_length=100, blank=True, null=True)
    role = models.CharField(
        max_length=100, default=Role.SELLER.value, choices=Role.choices
    )
    updated_at = models.DateTimeField(default=timezone.now, null=True)
    first_name = None
    last_name = None
    profile_picture = models.ImageField(
        default="profile 1.jpg", upload_to="profile_images"
    )
    is_staff = models.BooleanField(default=False)
    is_blocked = models.BooleanField(default=False)
    is_active = models.BooleanField(default=True)
    is_superuser = models.BooleanField(default=False)
    username = models.CharField(max_length=30, unique=True)
    USERNAME_FIELD = "username"
    REQUIRED_FIELDS = ["email", "name", "role"]
    objects = myUserManager()
    class Meta:
        unique_together = [("email", "role"), ("phone", "role")]
        ordering = ["id"]
        db_table = "users"

I've implemented dj_rest_auth with Google Authentication, and it functions as intended. However, a limitation arises wherein it only supports users with a single role. For instance, if we create a user with the role of seller, it successfully creates an account and logs in the user. However, if we attempt to log in with a user role such as buyer, it erroneously returns the seller user and logs them in instead.

#CustomSocialLoginSerializer:

import requests
from allauth.socialaccount.models import SocialAccount
from dj_rest_auth.registration.serializers import SocialLoginSerializer
from django.contrib.auth import get_user_model
from django.core.files.base import ContentFile
from rest_framework import serializers
from rest_framework.response import Response

User = get_user_model()


class CustomSocialSerializer(SocialLoginSerializer):
    role = serializers.ChoiceField(choices=User.Role.choices, required=True)

    def validate(self, attrs):
        attrs = super().validate(attrs)
        user = attrs["user"]
        role = attrs.get("role")
        if role:
            user.role = role
            user.username = User.generate_username(user.email, user.role)
            try:
                sociallogin = SocialAccount.objects.get(user=user)
                user.name = sociallogin.extra_data.get("name", "")
                picture = sociallogin.extra_data.get("picture", "")
                if picture:
                    response = requests.get(picture)
                    if response.status_code == 200:
                        user.profile_picture.save(
                            f"{user.name}_picture.jpg",
                            ContentFile(response.content),
                            save=True,
                        )
            except:
                return Response({"Message": "Failed to save extra Details !"})
            user.save()
        return attrs

#views.py:

class CustomLoginView(SocialLoginView):
    serializer_class = CustomSocialSerializer


class GoogleLogin(CustomLoginView):
    adapter_class = GoogleOAuth2Adapter
    callback_url = "/"
    client_class = OAuth2Client

I've discovered that the Google authentication process looks up the SocialAccount's UID, and if it finds a match, it simply logs in the user based on that ID. How can I modify this functionality to achieve a different behavior?

    def _lookup_by_socialaccount(self):
        assert not self.is_existing
        try:
            a = SocialAccount.objects.get(
                provider=self.account.provider, uid=self.account.uid
            )
            # Update account
            a.extra_data = self.account.extra_data
            self.account = a
            self.user = self.account.user
            a.save()
            signals.social_account_updated.send(
                sender=SocialLogin, request=context.request, sociallogin=self
            )
            # Update token
            if app_settings.STORE_TOKENS and self.token:
                assert not self.token.pk
                try:
                    t = SocialToken.objects.get(
                        account=self.account, app=self.token.app
                    )
                    t.token = self.token.token
                    if self.token.token_secret:
                        # only update the refresh token if we got one
                        # many oauth2 providers do not resend the refresh token
                        t.token_secret = self.token.token_secret
                    t.expires_at = self.token.expires_at
                    t.save()
                    self.token = t
                except SocialToken.DoesNotExist:
                    self.token.account = a
                    self.token.save()
            return True
        except SocialAccount.DoesNotExist:
            pass
0

There are 0 best solutions below