sign-in-with-apple-using-django
sign-in-with-apple-using-django copied to clipboard
Here is the updated code for backend.
Here is the updated code for backend.
There were certain problems that I faced during this process. Unlike in docs I am not using any package for authentication. So, I had to strip down the code to bare minimum so below code is for creating client secret
and validating
the authorization_code
.
Problem was with identity_token
or authorization_code
by client side
- In docs, access_token
was mentioned and all that client side can provide was identity_token
or authorization_code
or both. Here I have used authorization_code
.
How to use PRIVATE_KEY
securely(maybe not a part that docs is related to). I have stored PRIVATE_KEY in my env file
. I have just appended the private key into a single line and later on strip to get raw PRIVATE_KEY
Example:
APPLE_PRIVATE_KEY=-----BEGIN PRIVATE KEY-----\nyourrandomnstring1\nyourrandomnstring2\nyourrandomnstring3\n-----END PRIVATE KEY-----
Original:
-----BEGIN PRIVATE KEY-----
yourrandomnstring1
yourrandomnstring2
yourrandomnstring3
-----END PRIVATE KEY-----
Decoding of the id_token
.During the time docs was made may be it was correct but the code mention in code didn't worked for me so I had to update the decoding processing.
PY JWT reading the claim without validation
```
decoded = jwt.decode(id_token, options={"verify_signature": False})
```
Things to remember:
-
authorization_code
is 1 time thing, create new one every time you try to login (client side). -
PRIVATE_KEY
is 1 time thing once you download cannot be downloaded again -
Below code only creates
client_secret
and validates theauthorization_code
this is not a complete flow -
It only returns email if
authorization_code
is valid otherwise it returns empty dict.
import jwt
import requests
from datetime import timedelta
from medhavhilms.settings import env
from django.utils import timezone
APPLE_BUNDLE_NAME = env('APPLE_BUNDLE_NAME')
APPLE_KEY_ID = env('APPLE_KEY_ID')
APPLE_TEAM_ID = env('APPLE_TEAM_ID')
APPLE_BUNDLE_ID = env('APPLE_BUNDLE_ID')
APPLE_PRIVATE_KEY = env('APPLE_PRIVATE_KEY').strip().replace('\\n', '\n') # Remove whitespace and replace escaped newlines
class AppleOAuth2():
ACCESS_TOKEN_URL = 'https://appleid.apple.com/auth/token'
def authenticate(self, auth_code):
response_body = {}
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': auth_code,
'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, options={"verify_signature": False})
response_body.update({'email': decoded.get('email')})
return response_body
def get_key_and_secret(self):
headers = {
'alg': 'ES256',
'kid': APPLE_KEY_ID
}
payload = {
'iss': APPLE_TEAM_ID,
'iat': timezone.now(),
'exp': timezone.now() + timedelta(days=180),
'aud': 'https://appleid.apple.com',
'sub': APPLE_BUNDLE_ID,
}
client_secret = jwt.encode(
payload,
APPLE_PRIVATE_KEY,
algorithm='ES256',
headers=headers
)
return APPLE_BUNDLE_ID, client_secret