social-auth-app-django. Associate a social account by custom field in UserModel

I have a custom user model:

class Profile(AbstractUser):
    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    ...
    telegram_id = models.BigIntegerField(verbose_name='телеграм ID', unique=True, blank=True, null=True)
    ...

I have configured authorization via Telegram, Google, VK. After authorization via Telegram where do I get extra_data:

{"id": ["123"], "first_name": ["123"], "last_name": ["123"], "username": ["123"], "photo_url": ["https://t.me/i/userpic/320/123"], "auth_date": ["123"], "hash": ["123"]}

An instance Profile and social_auth_usersocialauth records is created.

I want to do the following:

  1. If the user is new when logging in via Telegram it is checked whether a Profile exists, where Profile.telegram_id == extra_data["id"]
  2. If such a profile exists: Associate the record social_auth_usersocialauth with this Profile
  3. If not: Create a record Profile as standard

I have the following social auth pipelines settings:

SOCIAL_AUTH_PIPELINE = (
    'social_core.pipeline.social_auth.social_details',
    'social_core.pipeline.social_auth.social_uid',
    'social_core.pipeline.social_auth.social_user',
    'social_core.pipeline.user.get_username',
    'social_core.pipeline.social_auth.associate_by_email',
    'social_core.pipeline.user.create_user',
    'social_core.pipeline.social_auth.associate_user',
    'social_core.pipeline.social_auth.load_extra_data',
    'social_core.pipeline.user.user_details',
)

I suppose I need to create my own pipeline where all the logic will take place, but I don't quite understand how to do it correctly so as not to break the logic of other backends.

Upvotes: 1

Views: 87

Answers (1)

Ok. I did it.
My pipeline:

def associate_by_telegram_id(backend, details, user=None, *args, **kwargs):
    if backend.name == 'telegram':
        if user:
            return None

        tgid = int(kwargs.get('uid'))
        if tgid:
            UserModel = get_user_model()
            users = list(UserModel.objects.filter(tgid=tgid))
            if len(users) == 0:
                return None
            elif len(users) > 1:
                raise AuthException(
                    backend, "The given Telegram ID is associated with another account"
                )
            else:
                return {"user": users[0], "is_new": False}

It was inserted here:

SOCIAL_AUTH_PIPELINE = (
    'social_core.pipeline.social_auth.social_details',
    'social_core.pipeline.social_auth.social_uid',
    'social_core.pipeline.social_auth.social_user',
    'social_core.pipeline.user.get_username',
    ### HERE ###,
    'social_core.pipeline.social_auth.associate_by_email',
    'social_core.pipeline.user.create_user',
    'social_core.pipeline.social_auth.associate_user',
    'social_core.pipeline.social_auth.load_extra_data',
    'social_core.pipeline.user.user_details',
)

It works great.

Upvotes: 1

Related Questions