social-core icon indicating copy to clipboard operation
social-core copied to clipboard

Authorization backend function do_auth called twice

Open grindnoise opened this issue 1 year ago • 0 comments

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?

grindnoise avatar Jun 01 '23 10:06 grindnoise