1,参考文献
https://sarunw.com/posts/sign-in-with-apple-4/#create-a-sign-in-with-apple-private-key
2,实现代码
#! /usr/bin/env python
# -*- coding: utf-8 -*-
import jwt
import time
from tornado import gen
from tornado.log import gen_log
from hd_lib.utils.http_client import async_request
from hd_lib.utils.request_context import g_tn
PRIVATE_KEY_TOKEN = '''-----BEGIN PRIVATE KEY-----
xxxxxxx
-----END PRIVATE KEY-----
''' # sign in with apple
class AppleHelper(object):
@staticmethod
def get_client_secret():
return jwt.encode(
payload={
"iss": "xxxxxx",
"iat": time.time() - 10,
"exp": time.time() + 3500,
"aud": "https://appleid.apple.com",
"sub": "xxxxxx"
},
key=PRIVATE_KEY_TOKEN,
headers={
"alg": "ES256",
"kid": "xxxxx",
"typ": "JWT"
},
algorithm="ES256"
)
class AppleService(object):
AUTH_PATH = "https://appleid.apple.com/auth/token" # 认证接口
REVOKE_PATH = "https://appleid.apple.com/auth/revoke" # 注销认证接口
HEADERS = {"content-type": "application/x-www-form-urlencoded"}
COMMON_DATA = {
"client_id": "xxxxx",
"client_secret": AppleHelper.get_client_secret()
}
@classmethod
@gen.coroutine
def send_request(cls, url, data):
data.update(cls.COMMON_DATA)
ret = yield async_request(g_tn(), url,
data=data,
method='POST',
headers=cls.HEADERS,
timeout=10,
request_is_json=False)
raise gen.Return(ret)
@classmethod
@gen.coroutine
def revoke(cls, refresh_token, access_token):
if not refresh_token:
gen_log.info('<seq_no=%s> not refresh_token: refresh_token=%s', g_tn().seq_no, refresh_token)
return
# apple那里要求我们注销 refresh_token 和 access_token。
# 但是 access_token 本身就是有过期时间的,我们只注销用户登录当时的那个 access_token,不再刷新 access_token 后再次注销。
ret = yield cls.revoke_access_token(access_token)
gen_log.info('<seq_no=%s> revoke_access_token: access_token=%s ret=%s', g_tn().seq_no, access_token, ret)
ret = yield cls.revoke_refresh_token(refresh_token)
gen_log.info('<seq_no=%s> revoke_refresh_token: refresh_token=%s ret=%s', g_tn().seq_no, refresh_token, ret)
return
@classmethod
@gen.coroutine
def auth_grant_code(cls, code):
"""
通过 code 获取 access_token 和 refresh_token
@param code:
@return: {
"access_token": "adg61...67Or9",
"token_type": "Bearer",
"expires_in": 3600,
"refresh_token": "rca7...lABoQ"
"id_token": "eyJra...96sZg"
}
"""
data = {
"code": code,
"grant_type": "authorization_code",
}
ret = yield cls.send_request(cls.AUTH_PATH, data)
raise gen.Return(ret)
@classmethod
@gen.coroutine
def refresh_token(cls, refresh_token):
"""
通过 refresh_token 刷新 access_token
@param refresh_token:
@return: {
"access_token": "beg510...67Or9",
"token_type": "Bearer",
"expires_in": 3600,
"id_token": "eyJra...96sZg"
}
"""
data = {
"grant_type": "refresh_token",
"refresh_token": refresh_token,
}
ret = yield cls.send_request(cls.AUTH_PATH, data)
raise gen.Return(ret)
@classmethod
@gen.coroutine
def revoke_access_token(cls, access_token):
"""
注销 access_token
@param access_token:
@return:
"""
data = {
"token": access_token,
"token_type_hint": "access_token",
}
ret = yield cls.send_request(cls.REVOKE_PATH, data)
raise gen.Return(ret)
@classmethod
@gen.coroutine
def revoke_refresh_token(cls, refresh_token):
"""注销 refresh_token"""
data = {
"token": refresh_token,
"token_type_hint": "refresh_token",
}
ret = yield cls.send_request(cls.REVOKE_PATH, data)
raise gen.Return(ret)