海外apple 苹果 登陆 快速配置 Sign In with Apple

登录 Apple 开发者后台

https://developer.apple.com/account

 

 

 

 

 

我们需要获得具有 Sign In with Apple 功能的 App Id。

• 进入 Certificates, Identifiers & Profiles > Identifiers,然后单击 Identifiers 旁边左上角的 + 号;

• 选择 App IDs 并点击继续;

 在此处输入任意 DescriptionBundle ID(Apple建议使用反向域名样式字符串,如:com.domainname.appname)。向下滚动 Capabilities 项,并确保勾选 Sign In with Apple。 最后,单击“继续”,在下一页中验证详细信息,然后单击 Register

现在我们需要获取一个 Services ID。当您调用 API 来验证用户身份时,这个值也将充当 cliend_id

• 再次进到 Certificates, Identifiers & Profiles > Identifiers,然后单击 Identifiers 旁边左上角的 + 号。

• 这次选择 Services IDs 并点击继续。

 

 

 

通过下载的p8文件并运行ruby脚本来获取client_secret

设置 Ruby 后运行命令 sudo gem install jwt 来设置 ruby-jwt。

添加必要的详细信息并将以下内容保存为 secret_gen.rb

require "jwt"

key_file = "Path to the private key"
team_id = "Your Team ID"  
client_id = "The Service ID of the service you created" # client_id 就是包名com.xxx.xxx
key_id = "The Key ID of the private key"
validity_period = 180 # In days. Max 180 (6 months) according to Apple docs.

private_key = OpenSSL::PKey::EC.new IO.read key_file

token = JWT.encode(
    {
        iss: team_id,
        iat: Time.now.to_i,
        exp: Time.now.to_i + 86400 * validity_period,
        aud: "https://appleid.apple.com",
        sub: client_id
    },
    private_key,
    "ES256",
    header_fields=
    {
        kid: key_id 
    }
)
puts token

 

修改这四个参数

key_file = "P8文件本地路径"
team_id = "Your Team ID"
client_id = "Your App Bundle ID"
key_id = "The Key ID of the private key"

获取team_id: https://developer.apple.com/account

配置key_id和key_file:https://developer.apple.com/account/resources/authkeys/list

client_id 就是你的包名: com.xxx.xxx

 

获取team_id

 

注意:key_file 只能下载一次

 

获取包名com.xxx.xxx

 

 

 

 得到的secrte是有有效期最长6个月

 通过secret获取id_token

import requests
import json

# Apple API endpoint for token verification
APPLE_TOKEN_URL = "https://appleid.apple.com/auth/token"

def verify_apple_token(client_id, client_secret, authorization_code):
    # Request parameters
    headers = {
        "Content-Type": "application/x-www-form-urlencoded"
    }
    data = {
        "grant_type": "authorization_code",
        "client_id": client_id,
        "client_secret": client_secret,
        "code": authorization_code,

    }

    # Send the request to Apple's token endpoint
    response = requests.post(APPLE_TOKEN_URL, headers=headers, data=data)
    print(response.text)
    if response.status_code == 200:
        token_response = json.loads(response.text)
        print(token_response)
        """
        {
    'access_token': 'af53cda59c1224fbea044d9576769223e.0.rrywq.TQvJO5iffaaavoPyG5Ib3w',
    'token_type': 'Bearer',
    'expires_in': 3600,
    'refresh_token': 're236004793734227cc7b55a7138915f3.0.rrywq.9ciGgJQjpTtfY16MZBZHxA',
    'id_token': 'aaJraWQiOiJXNldjT0tCIiwiYWxnIjocclMyNTYifQ.eyJpc3MiOiJodHRwczovL2FwcGxlaWQuYXBwbGUuY29tIiwiYXVkIjoiY29tLndvbnhpbmcucGlja3dhbnQiLCJleHAiOjE2OTc3NjkyMjUsImlhdCI6MTY5NzY4MjgyNSwic3ViIjoiMDAxODYwLmIxMmI0OTczYjNlODQyNzdhMGQ0ZjEzNTUwMzlkMzhmLjA4MDMiLCJhdF9oYXNoIjoibE5ZZkhZUXhrNEFXd0k5bkxQLTE0USIsImVtYWlsIjoiYzU4Z3pzcmhjbkBwcml2YXRlcmVsYXkuYXBwbGVpZC5jb20iLCJlbWFpbF92ZXJpZmllZCI6InRydWUiLCJpc19wcml2YXRlX2VtYWlsIjoidHJ1ZSIsImF1dGhfdGltZSI6MTY5NzY4Mjc4OCwibm9uY2Vfc3VwcG9ydGVkIjp0cnVlfQ.Q56Xa3EiY2jLh8qCmgT6SRyX0RFTxmDc6YPxeoxpLE4vUnDMtjNfHFnHi-FU9j8BrNoG9daBIwNV4FgyYodL3MJNjKaIAtOMD9tBBiNDOgnk6WJpq2O0r5xwrC4fkR_rIAE-1utKH8EUoHh-vYN5hjxk07CMlGz8PlBkDvULLzHzbKMN6BqjTbtWVz75wFsl5fLB7KtLKNM7XAF9nNJkIyN6i6z5w3vxfy20BCfy1WRlWxsr7Jn0lKhQugAlUL6xtHVMfFZS04csjA4CaVF8Q9bvbRhj_gY_qzRZIBVVIigqIndO5IMBSVUyRG_F9qtMu6nVtqOLCQkYPUWEMVdxvw'
}
        """

    else:
        print("Token verification failed")
        print("Error:", response.text)

# Replace with your client ID, client secret, and authorization code
client_id = "com.wonxxx.xxx" # 包名
client_secret = "eyJraWQiOiI1VVFCTFdCWDZYIiwixxxnIjoiRVMyNTYifQ.eyJpcxxiOiJCMzhNUEs5SFoyIiwiaWF0IjoxNjk3Njg0NDAyLCJleHAiOjE3MTMyMzY0MDIsImF1ZCI6Imh0dHBzOi8vYXBwbGVpZC5hcHBsZS5jb20iLCJzdWIiOiJjb20ud29ueGluZy5waWNrd2FudCJ9.0bbM8db5la22-bWP0lnaxKtW5GfI7bCsQCTCUQaN0EeIEYfvcAKXL0YVCanOnaU_neXjsUYLkUXB_TypzuTecg" # 获取的secret
authorization_code = "cfdcf21c6e14a453d9b1dadxxdde3605f.0.rrywq.Ev2cDtzJ-k0mZQQhhbBQWQ" # 客户端传的code

# Verify the Apple token
verify_apple_token(client_id, client_secret, authorization_code)

 

jwt解析idtoken 获取用户信息

pip install python-jwt

# encoding=utf8
import requests
import logging
import python_jwt as jwt, jwcrypto.jwk as jwk

log = logging.getLogger('test')


class AppleLoginManager(object):
    """
    苹果登录
    """

    @classmethod
    def get_key(cls, kid):
        """
        访问apple 获取公钥。apple的接口会返回很多公钥的,根据jwt数据header的kid,找到对应的公钥
        :param kid:
        :return: {
            "kty": "RSA",
            "kid": "eXaunmL",
            "use": "sig",
            "alg": "RS256",
            "n": "4dGQ7bQK8LgILOdLsYzfZjkEAoQeVC_aqyc8GC6RX7dq_KvRAQAWPvkam8VQv4GK5T4ogklEKEvj5ISBamdDNq1n52TpxQwI2EqxSk7I9fKPKhRt4F8-2yETlYvye-2s6NeWJim0KBtOVrk0gWvEDgd6WOqJl_yt5WBISvILNyVg1qAAM8JeX6dRPosahRVDjA52G2X-Tip84wqwyRpUlq2ybzcLh3zyhCitBOebiRWDQfG26EH9lTlJhll-p_Dg8vAXxJLIJ4SNLcqgFeZe4OfHLgdzMvxXZJnPp_VgmkcpUdRotazKZumj6dBPcXI_XID4Z4Z3OM1KrZPJNdUhxw",
            "e": "AQAB"
        }
        """
        ret = requests.get('https://appleid.apple.com/auth/keys')
        ret_json = ret.json()
        for key in ret_json['keys']:
            if key['kid'] == kid:
                return key
        log.error(u'[苹果登录]找不到对应的kid %s %s' % (kid, ret_json))
        raise Exception('找不到对应的kid')

    @classmethod
    def verify_jwt(cls, token):
        """
        验证jwt数据,返回:
        {
          u'c_hash': u'HpjAKvivbJr9j9ZxfFxA',
          u'aud': u'com.test.moe',
          u'iss': u'https://appleid.apple.com',
          u'email_verified': u'true',
          u'nonce_supported': True,
          u'exp': 1583829815,
          u'auth_time': 1583829215,
          u'iat': 1583829215,
          u'email': u'hua@163.com',
          u'sub': u'0017xx.9035989b3bxxxxx7c88b30086b37.xxx' # 用户唯一标志,相当于openid
        }

        """
        header, claims = jwt.process_jwt(token)  # 获取信息,但是不验证
        print("claims:",claims)
        if claims['aud'] != 'com.wonxing.pickwant':  # 检验是不是自己的安装包名
            log.error(u'[苹果登录]aud异常 aud:%s  token:%s' % (claims['aud'], token))
            raise Exception(u'安装包名异常')
        key_obj = AppleLoginManager.get_key(header['kid'])

        key = jwk.JWK(**key_obj)

        header, claims = jwt.verify_jwt(token, key, [key_obj['alg']], checks_optional=1)  # 获取信息并验证
        return claims

    @classmethod
    def get_access_info(cls, token):
        """
            获取授权信息
        Args:
            token:
        Returns:
        """

        try:
            resp_json = AppleLoginManager.verify_jwt(token)
            print(resp_json)
            return {
                "open_id": resp_json['sub'],
            }
        except:
            log.exception(u'苹果登录异常,token:%s' % token)
            raise Exception('苹果登录异常')



token = "eyJraWQiOiJXNldjT0tCIixxYWxnIjoiUlMyNTYifQ.eyJpc3MiOiJodHRwczovL2FwcGxlaWQuYXBwbGUuY29tIiwiYXVkIjoiY29tLndvbnhpbmcucGlja3dhbnQiLCJleHAiOjE2OTc3NjkyMjUsImlhdCI6MTY5NzY4MjgyNSwic3ViIjoiMDAxODYwLmIxMmI0OTczYjNlODQyNzdhMGQ0ZjEzNTUwMzlkMzhmLjA4MDMiLCJhdF9oYXNoIjoibE5ZZkhZUXhrNEFXd0k5bkxQLTE0USIsImVtYWlsIjoiYzU4Z3pzcmhjbkBwcml2YXRlcmVsYXkuYXBwbGVpZC5jb20iLCJlbWFpbF92ZXJpZmllZCI6InRydWUiLCJpc19wcml2YXRlX2VtYWlsIjoidHJ1ZSIsImF1dGhfdGltZSI6MTY5NzY4Mjc4OCwibm9uY2Vfc3VwcG9ydGVkIjp0cnVlfQ.Q56Xa3EiY2jLh8qCmgT6SRyX0RFTxmDc6YPxeoxpLE4vUnDMtjNfHFnHi-FU9j8BrNoG9daBIwNV4FgyYodL3MJNjKaIAtOMD9tBBiNDOgnk6WJpq2O0r5xwrC4fkR_rIAE-1utKH8EUoHh-vYN5hjxk07CMlGz8PlBkDvULLzHzbKMN6BqjTbtWVz75wFsl5fLB7KtLKNM7XAF9nNJkIyN6i6z5w3vxfy20BCfy1WRlWxsr7Jn0lKhQugAlUL6xtHVMfFZS04csjA4CaVF8Q9bvbRhj_gY_qzRZIBVVIigqIndO5IMBSVUyRG_F9qtMu6nVtqOLCQkYPUWEMVdxvw" # 服务端获取的id_token
AppleLoginManager.get_access_info(token)

 

 

 

    

 

 
 

posted on 2023-10-19 11:03  星河赵  阅读(568)  评论(0编辑  收藏  举报

导航