authenticationdjango-rest-frameworkoauth-2.0sign-in-with-apple

Understanding Sign in with Apple using Django Rest Framework (DRF)


I am trying to implement Sign in with Apple in my iOS app and Django Rest Framework (DRF) application. I want to know if I understand the workflow correctly.

Currently, I am using the libraries python-social-auth and rest_social_auth, and rest_framework_simplejwt.authentication.JWTAuthentication for authentication. The problem is that I set the refresh tokens to expire in one week (this should really be one day). At that point, I need to use the refresh token that Apple originally returned in their first authentication request, but python-social-auth does not persist those.

Specifically, I am wondering how I can get access to those original refresh tokens. More generally, I am wondering if this functionally has already been written in python-social-auth.


Solution

  • After a year and a half, "I" finally figured this out.

    I was conflating the device Sign in with Apple with the Sign in with Apple Rest API. If you are developing in iOS/React Native, you do not need to make any calls to the Sign in with Apple Rest API.

    You do need to make a call to an Apple auth server, but not a Sign in with Apple Rest API server. The Apple auth server will return a public key that you use to then confirm the authenticity of the identity token that Apple send to you on the device.

    I borrowed heavily from this Gist, but I believe it is outdated because Apple is now cycling through (at least at the time of this post) 3 different keys. I also used Chris Herbert's post as a resource.

    def _fetch_apple_public_key(kid):
        global APPLE_LAST_KEY_FETCH
        global APPLE_PUBLIC_KEYS
    
        apple_public_key = APPLE_PUBLIC_KEYS.get(kid)
        if (APPLE_LAST_KEY_FETCH + APPLE_KEY_CACHE_EXP) < int(time()) or apple_public_key is None:
            keys_payload = requests.get(APPLE_PUBLIC_KEY_URL).json()
            for key_payload in keys_payload["keys"]:
                if key_payload.get('kid') == kid:
                    APPLE_PUBLIC_KEYS[kid] = RSAAlgorithm.from_jwk(json.dumps(key_payload))
                    APPLE_LAST_KEY_FETCH = int(time())
    
        return APPLE_PUBLIC_KEYS[kid]
    

    You can get the kid from the header of the identity token:

    headers = jwt.get_unverified_header(user_token)
    kid = headers.get('kid')