Background
The integration of Sign In with Apple into applications has become increasingly popular due to its convenience and privacy-focused approach. However, navigating the token validation process and retrieving user information can be a complex task for developers. This article aims to demystify this process specifically for Python developers, offering a step-by-step guide on how to validate tokens obtained through Sign In with Apple and efficiently access user data within your application. By delving into the intricacies of token validation and user information retrieval, this comprehensive guide aims to equip developers with the knowledge and tools necessary to seamlessly implement and utilize Sign In with Apple functionality in their Python-based applications.
Prerequisite
This article is not focussing on how to get the value from Apple Developer page.
- Client ID (client_id)
- Client Secret (client_secret) – A JSON Web Token (JWT) generated by the developer
Generate client secret
Generate a signed token to identify your client application. [2]
<span>key_id</span> <span>=</span> <span>None</span><span>team_id</span> <span>=</span> <span>None</span><span>client_id</span> <span>=</span> <span>None</span><span>private_key</span> <span>=</span> <span>None</span> <span># .p8 key file </span><span>headers</span> <span>=</span> <span>{</span><span>"</span><span>kid</span><span>"</span><span>:</span> <span>key_id</span><span>}</span><span>open_private_key</span> <span>=</span> <span>open</span><span>(</span><span>private_key</span><span>)</span><span>private_key_data</span> <span>=</span> <span>open_private_key</span><span>.</span><span>read</span><span>()</span><span>payload</span> <span>=</span> <span>{</span><span>"</span><span>iss</span><span>"</span><span>:</span> <span>team_id</span><span>,</span><span>"</span><span>iat</span><span>"</span><span>:</span> <span>datetime</span><span>.</span><span>now</span><span>(),</span><span>"</span><span>exp</span><span>"</span><span>:</span> <span>datetime</span><span>.</span><span>now</span><span>()</span> <span>+</span> <span>timedelta</span><span>(</span><span>days</span><span>=</span><span>180</span><span>),</span><span>"</span><span>aud</span><span>"</span><span>:</span> <span>"</span><span>https://appleid.apple.com</span><span>"</span><span>,</span><span>"</span><span>sub</span><span>"</span><span>:</span> <span>client_id</span><span>,</span><span>}</span><span>client_secret</span> <span>=</span> <span>jwt</span><span>.</span><span>encode</span><span>(</span><span>payload</span><span>,</span> <span>private_key_data</span><span>,</span> <span>algorithm</span><span>=</span><span>"</span><span>ES256</span><span>"</span><span>,</span> <span>headers</span><span>=</span><span>headers</span><span>).</span><span>decode</span><span>(</span><span>"</span><span>utf-8</span><span>"</span><span>)</span><span>key_id</span> <span>=</span> <span>None</span> <span>team_id</span> <span>=</span> <span>None</span> <span>client_id</span> <span>=</span> <span>None</span> <span>private_key</span> <span>=</span> <span>None</span> <span># .p8 key file </span> <span>headers</span> <span>=</span> <span>{</span><span>"</span><span>kid</span><span>"</span><span>:</span> <span>key_id</span><span>}</span> <span>open_private_key</span> <span>=</span> <span>open</span><span>(</span><span>private_key</span><span>)</span> <span>private_key_data</span> <span>=</span> <span>open_private_key</span><span>.</span><span>read</span><span>()</span> <span>payload</span> <span>=</span> <span>{</span> <span>"</span><span>iss</span><span>"</span><span>:</span> <span>team_id</span><span>,</span> <span>"</span><span>iat</span><span>"</span><span>:</span> <span>datetime</span><span>.</span><span>now</span><span>(),</span> <span>"</span><span>exp</span><span>"</span><span>:</span> <span>datetime</span><span>.</span><span>now</span><span>()</span> <span>+</span> <span>timedelta</span><span>(</span><span>days</span><span>=</span><span>180</span><span>),</span> <span>"</span><span>aud</span><span>"</span><span>:</span> <span>"</span><span>https://appleid.apple.com</span><span>"</span><span>,</span> <span>"</span><span>sub</span><span>"</span><span>:</span> <span>client_id</span><span>,</span> <span>}</span> <span>client_secret</span> <span>=</span> <span>jwt</span><span>.</span><span>encode</span><span>(</span> <span>payload</span><span>,</span> <span>private_key_data</span><span>,</span> <span>algorithm</span><span>=</span><span>"</span><span>ES256</span><span>"</span><span>,</span> <span>headers</span><span>=</span><span>headers</span> <span>).</span><span>decode</span><span>(</span><span>"</span><span>utf-8</span><span>"</span><span>)</span>key_id = None team_id = None client_id = None private_key = None # .p8 key file headers = {"kid": key_id} open_private_key = open(private_key) private_key_data = open_private_key.read() payload = { "iss": team_id, "iat": datetime.now(), "exp": datetime.now() + timedelta(days=180), "aud": "https://appleid.apple.com", "sub": client_id, } client_secret = jwt.encode( payload, private_key_data, algorithm="ES256", headers=headers ).decode("utf-8")
Enter fullscreen mode Exit fullscreen mode
Generate and validate tokens
Validate an authorization grant code originating from your frontend interface.
<span>headers</span> <span>=</span> <span>{</span><span>"</span><span>content-type</span><span>"</span><span>:</span> <span>"</span><span>application/x-www-form-urlencoded</span><span>"</span><span>}</span><span>data</span> <span>=</span> <span>{</span><span>"</span><span>client_id</span><span>"</span><span>:</span> <span>client_id</span><span>,</span><span>"</span><span>client_secret</span><span>"</span><span>:</span> <span>client_secret</span><span>,</span><span>"</span><span>code</span><span>"</span><span>:</span> <span>id_token</span><span>,</span><span>"</span><span>grant_type</span><span>"</span><span>:</span> <span>"</span><span>authorization_code</span><span>"</span><span>,</span><span>}</span><span>res</span> <span>=</span> <span>requests</span><span>.</span><span>post</span><span>(</span><span>URL</span><span>,</span> <span>data</span><span>=</span><span>data</span><span>,</span> <span>headers</span><span>=</span><span>headers</span><span>)</span><span>values</span> <span>=</span> <span>res</span><span>.</span><span>json</span><span>()</span><span>headers</span> <span>=</span> <span>{</span><span>"</span><span>content-type</span><span>"</span><span>:</span> <span>"</span><span>application/x-www-form-urlencoded</span><span>"</span><span>}</span> <span>data</span> <span>=</span> <span>{</span> <span>"</span><span>client_id</span><span>"</span><span>:</span> <span>client_id</span><span>,</span> <span>"</span><span>client_secret</span><span>"</span><span>:</span> <span>client_secret</span><span>,</span> <span>"</span><span>code</span><span>"</span><span>:</span> <span>id_token</span><span>,</span> <span>"</span><span>grant_type</span><span>"</span><span>:</span> <span>"</span><span>authorization_code</span><span>"</span><span>,</span> <span>}</span> <span>res</span> <span>=</span> <span>requests</span><span>.</span><span>post</span><span>(</span><span>URL</span><span>,</span> <span>data</span><span>=</span><span>data</span><span>,</span> <span>headers</span><span>=</span><span>headers</span><span>)</span> <span>values</span> <span>=</span> <span>res</span><span>.</span><span>json</span><span>()</span>headers = {"content-type": "application/x-www-form-urlencoded"} data = { "client_id": client_id, "client_secret": client_secret, "code": id_token, "grant_type": "authorization_code", } res = requests.post(URL, data=data, headers=headers) values = res.json()
Enter fullscreen mode Exit fullscreen mode
Below is the response you’ll get after success make request
<span>{</span><span> </span><span>"access_token"</span><span>:</span><span> </span><span>"adg61...67Or9"</span><span>,</span><span> </span><span>"token_type"</span><span>:</span><span> </span><span>"Bearer"</span><span>,</span><span> </span><span>"expires_in"</span><span>:</span><span> </span><span>3600</span><span>,</span><span> </span><span>"refresh_token"</span><span>:</span><span> </span><span>"rca7...lABoQ"</span><span>,</span><span> </span><span>"id_token"</span><span>:</span><span> </span><span>"eyJra...96sZg"</span><span> </span><span>}</span><span> </span><span>{</span><span> </span><span>"access_token"</span><span>:</span><span> </span><span>"adg61...67Or9"</span><span>,</span><span> </span><span>"token_type"</span><span>:</span><span> </span><span>"Bearer"</span><span>,</span><span> </span><span>"expires_in"</span><span>:</span><span> </span><span>3600</span><span>,</span><span> </span><span>"refresh_token"</span><span>:</span><span> </span><span>"rca7...lABoQ"</span><span>,</span><span> </span><span>"id_token"</span><span>:</span><span> </span><span>"eyJra...96sZg"</span><span> </span><span>}</span><span> </span>{ "access_token": "adg61...67Or9", "token_type": "Bearer", "expires_in": 3600, "refresh_token": "rca7...lABoQ", "id_token": "eyJra...96sZg" }
Enter fullscreen mode Exit fullscreen mode
- access_token: token from frontend
- token_type: type for OAuth 2.0 framework
- expires_in: lifespan or validity duration of an access token
- refresh_token: renews access without re-login
- id_token: token to get the user details (next action)
Get the user details
<span>decoded</span> <span>=</span> <span>jwt</span><span>.</span><span>decode</span><span>(</span><span>id_token</span><span>,</span><span>""</span><span>,</span><span>verify</span><span>=</span><span>False</span><span>,</span><span>options</span><span>=</span><span>{</span><span>"</span><span>verify_signature</span><span>"</span><span>:</span> <span>False</span><span>},</span><span>algorithms</span><span>=</span><span>[</span><span>"</span><span>HS256</span><span>"</span><span>],</span><span>)</span><span>decoded</span> <span>=</span> <span>jwt</span><span>.</span><span>decode</span><span>(</span> <span>id_token</span><span>,</span> <span>""</span><span>,</span> <span>verify</span><span>=</span><span>False</span><span>,</span> <span>options</span><span>=</span><span>{</span><span>"</span><span>verify_signature</span><span>"</span><span>:</span> <span>False</span><span>},</span> <span>algorithms</span><span>=</span><span>[</span><span>"</span><span>HS256</span><span>"</span><span>],</span> <span>)</span>decoded = jwt.decode( id_token, "", verify=False, options={"verify_signature": False}, algorithms=["HS256"], )
Enter fullscreen mode Exit fullscreen mode
References
- https://developer.apple.com/documentation/sign_in_with_apple/generate_and_validate_tokens
- https://developer.apple.com/documentation/accountorganizationaldatasharing/creating-a-client-secret
原文链接:Guide to Validating Sign-In with Apple Tokens in Python
暂无评论内容