Skip to content

Instantly share code, notes, and snippets.

@davidhariri
Created October 12, 2019 18:14
Show Gist options
  • Save davidhariri/b053787aabc9a8a9cc0893244e1549fe to your computer and use it in GitHub Desktop.
Save davidhariri/b053787aabc9a8a9cc0893244e1549fe to your computer and use it in GitHub Desktop.
Code required to verify Sign in with app-made Apple JWT tokens server-side in Python
import jwt
from jwt.algorithms import RSAAlgorithm
import requests
from time import time
import json
import os
APPLE_PUBLIC_KEY_URL = "https://appleid.apple.com/auth/keys"
APPLE_PUBLIC_KEY = None
APPLE_KEY_CACHE_EXP = 60 * 60 * 24
APPLE_LAST_KEY_FETCH = 0
class AppleUser(object):
def __init__(self, apple_id, email=None):
self.id = apple_id
self.email = email
self.full_user = False
if email is not None:
self.full_user = True
def __repr__(self):
return "<AppleUser {}>".format(self.id)
def _fetch_apple_public_key():
# Check to see if the public key is unset or is stale before returning
global APPLE_LAST_KEY_FETCH
global APPLE_PUBLIC_KEY
if (APPLE_LAST_KEY_FETCH + APPLE_KEY_CACHE_EXP) < int(time()) or APPLE_PUBLIC_KEY is None:
key_payload = requests.get(APPLE_PUBLIC_KEY_URL).json()
APPLE_PUBLIC_KEY = RSAAlgorithm.from_jwk(json.dumps(key_payload["keys"][0]))
APPLE_LAST_KEY_FETCH = int(time())
return APPLE_PUBLIC_KEY
def _decode_apple_user_token(apple_user_token):
public_key = _fetch_apple_public_key()
try:
token = jwt.decode(apple_user_token, public_key, audience=os.getenv("APPLE_APP_ID"), algorithm="RS256")
except jwt.exceptions.ExpiredSignatureError as e:
raise Exception("That token has expired")
except jwt.exceptions.InvalidAudienceError as e:
raise Exception("That token's audience did not match")
except Exception as e:
print(e)
raise Exception("An unexpected error occoured")
return token
def retrieve_user(user_token):
token = _decode_apple_user_token(user_token)
apple_user = AppleUser(token["sub"], token.get("email", None))
return apple_user
@scorpioailabs
Copy link

scorpioailabs commented Sep 18, 2024

amazing thank you so much! this works, with a bit of tweaking for jose:

from jose import jwt, JWTError
from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.primitives.asymmetric import rsa
# method to validate apple sign in using id_token param
#....
        try:
            public_key_from_apple  = _fetch_apple_public_key()
            apple_public_key_as_string = public_key_from_apple.public_bytes(
                encoding=serialization.Encoding.PEM,
                format=serialization.PublicFormat.SubjectPublicKeyInfo
            )
        try:
            decoded = jwt.decode(id_token_str, apple_public_key_as_string, algorithms="RS256", audience=os.getenv("APPLE_CLIENT_ID"))
        except Exception as e:
            raise Exception(e)

@montasaurus
Copy link

I hit signature verification issues since it's assuming the first key is the one used to sign the token. Wrote an updated version here that you can use as a CLI to test with as well: https://gist.github.com/montasaurus/6376b15f334d93262190a3de50fd0716

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment