Skip to content

Authorization backend function do_auth called twice #802

@grindnoise

Description

@grindnoise

I've implemented apple sign-in following this article using subclass:
https://github.com/truffls/sign-in-with-apple-using-django/blob/master/backend.md

Code:

import jwt
import requests
from datetime import timedelta
from django.conf import settings
from django.utils import timezone
from social_core.utils import handle_http_errors
from social_core.backends.oauth import BaseOAuth2


class AppleOAuth2(BaseOAuth2):
    name = 'apple'
    ACCESS_TOKEN_URL = 'https://appleid.apple.com/auth/token'
    SCOPE_SEPARATOR = ','
    ID_KEY = 'uid'

    @handle_http_errors
    def do_auth(self, access_token, *args, **kwargs):
        response_data = {}
        client_id, client_secret = self.get_key_and_secret()
        
        headers = {'content-type': "application/x-www-form-urlencoded"}
        data = {
            'client_id': client_id,
            'client_secret': client_secret,
            'code': access_token,
            'grant_type': 'authorization_code',
        }

        res = requests.post(AppleOAuth2.ACCESS_TOKEN_URL, data=data, headers=headers)
        response_dict = res.json()
        id_token = response_dict.get('id_token', None)

        if id_token:
            decoded = jwt.decode(id_token, '', algorithms=["ES256"], options={"verify_signature": False})
            response_data.update({'email': decoded['email']}) if 'email' in decoded else None
            response_data.update({'uid': decoded['sub']}) if 'sub' in decoded else None

        response = kwargs.get('response') or {}
        response.update(response_data)
        response.update({'access_token': access_token}) if 'access_token' not in response else None

        kwargs.update({'response': response, 'backend': self})
        return self.strategy.authenticate(*args, **kwargs)

    def get_user_details(self, response):
        email = response.get('email', None)
        details = {
            'email': email,
        }

        return details

    def get_key_and_secret(self):
        headers = {
            'kid': settings.SOCIAL_AUTH_APPLE_ID_KEY
        }**strong text**

        payload = {
            'iss': settings.SOCIAL_AUTH_APPLE_ID_TEAM,
            'iat': timezone.now(),
            'exp': timezone.now() + timedelta(days=180),
            'aud': 'https://appleid.apple.com',
            'sub': settings.SOCIAL_AUTH_APPLE_ID_CLIENT,
        }

        client_secret = jwt.encode(
            payload,
            settings.SOCIAL_AUTH_APPLE_ID_SECRET,
            algorithm='ES256',
            headers=headers
        )
        
        return settings.SOCIAL_AUTH_APPLE_ID_CLIENT, client_secret

Authorization succeeds during first call, i receive access token from apple, new entries in Users & User social auths tables are created but after that function do_auth is called one more time causing duplicate entry:
enter image description here

The very time user logs in - new entry in Users & User social auths tables created.
enter image description here

During the second call id_token is empty because apple's authorization code is one time token. So
SOCIAL_AUTH_PIPELINE in settings.py now are commented, I tried different variants.
Can somebody help me?

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions