海外apple 苹果 登陆 快速配置 Sign In with Apple
登录 Apple 开发者后台
https://developer.apple.com/account
我们需要获得具有 Sign In with Apple 功能的 App Id。
• 进入 Certificates, Identifiers & Profiles > Identifiers,然后单击 Identifiers 旁边左上角的 +
号;
• 选择 App IDs
并点击继续;
在此处输入任意 Description 和 Bundle 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)