Guide to Validating Sign-In with Apple Tokens in Python

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.

  1. Client ID (client_id)
  2. 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

  1. access_token: token from frontend
  2. token_type: type for OAuth 2.0 framework
  3. expires_in: lifespan or validity duration of an access token
  4. refresh_token: renews access without re-login
  5. 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

  1. https://developer.apple.com/documentation/sign_in_with_apple/generate_and_validate_tokens
  2. https://developer.apple.com/documentation/accountorganizationaldatasharing/creating-a-client-secret

原文链接:Guide to Validating Sign-In with Apple Tokens in Python

© 版权声明
THE END
喜欢就支持一下吧
点赞8 分享
If you get tired, learn to rest, not to quit.
如果你累了,学会休息,而不是放弃
评论 抢沙发

请登录后发表评论

    暂无评论内容