Skip to content

Authorization backend function do_auth called twice #802

Closed

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?

Activity

  1. gersona commented on Dec 11, 2024

    @gersona
    Contributor

    @grindnoise Any specific reason you wanted to write a custom Apple backend? Since the article was published in November 2019, support for Apple sign-in has been added (March 2020).

  2. locked and limited conversation to collaborators on Apr 1, 2025
  3. converted this issue into a discussion #1084 on Apr 1, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

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