使用REST请求App Store Connect API响应出现'401' | 'NOT_AUTHORIZED'错误

3
我们发布了一款iOS应用程序,我想从App Store Connect API中获取一些信息(例如安装、更新和评论)。根据苹果公司的官方文档创建一个JSON Web令牌:链接。然后在标头中使用令牌进行请求。现在每次都会得到“401”|“NOT_AUTHORIZED”的回答,如下图所示:REST Response
以下是Python代码片段(我尝试在Python和R中解决它,但结果总是相同)。首先,创建JWT:
from datetime import datetime, timedelta
from jose import jwt, jws
import ecdsa
KEY_ID = "XXXXXXXXXX" ISSUER_ID = "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX" PRIVATE_KEY = open('AuthKey_XXXXXXXXXX.p8', 'r').read() TIMESTAMP = int( (datetime.now() - timedelta(minutes = 45)).timestamp() * 1000)
claim = {"iss" : ISSUER_ID, "exp" : TIMESTAMP, "aud" : "appstoreconnect-v1"}
header = { "alg": "ES256", "kid": KEY_ID, "typ": "JWT" }
# Create the JWT encoded = jwt.encode(claim, PRIVATE_KEY, algorithm='ES256', headers=header)
现在,当我打印编码时,我得到以下JWT(对我来说看起来有效):
'eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiIsImtpZCI6IlhYWFhYWFhYWFgifQ.eyJpc3MiOiJYWFhYWFhYWC1YWFhYLVhYWFgtWFhYWC1YWFhYWFhYWFhYWFgiLCJleHAiOjE1NDUzOTc1MTQ1ODAsImF1ZCI6ImFwcHN0b3JlY29ubmVjdC12MSJ9.eTl6iaAW-Gp67FNmITrWCpLTtJzVdLYXIl5_KKgqaNgzwyGo7npBOBo9_u5PtLNnssQFEwJWbPND-6Ww5ACgEg'

即使我通过Base64解码JWT的前两部分,我也可以得到正确的标题(它还包含正确的编码算法:“alg”:“ES256”)和声明。
从jose.utils中导入base64url_decode
print(base64url_decode(b'eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiIsImtpZCI6IlhYWFhYWFhYWFgifQ'))
print(base64url_decode(b'eyJpc3MiOiJYWFhYWFhYWC1YWFhYLVhYWFgtWFhYWC1YWFhYWFhYWFhYWFgiLCJleHAiOjE1NDUzOTc1MTQ1ODAsImF1ZCI6ImFwcHN0b3JlY29ubmVjdC12MSJ9'))

查看下图:Base64解码输出

现在,我认为JWT对象已经准备好了,我将请求发送到API:

    import requests
JWT = 'Bearer ' + encoded
URL = 'https://api.appstoreconnect.apple.com/v1/apps' HEAD = {'Authorization': JWT} print(HEAD)
R = requests.get(URL, headers=HEAD) R.json()

现在我们可以看到我的问题,参见图片:Header | REST Response

请注意,我已经隐藏了示例中的KEY_ID、ISSUER_ID和PRIVATE_KEY。


你能够使用URLRequest和URLSession与令牌一起使用吗? - the Reverend
3个回答

1

您的令牌包含一个过期时间

"exp": 1545397514580,

相当于50941年9月12日。

当我删除最后三位数字时

"exp": 1545397514,

我得到的是2018年12月21日,这更有意义。

更改那一行

TIMESTAMP = int( (datetime.now() - timedelta(minutes = 45)).timestamp() * 1000)

to

TIMESTAMP = int( (datetime.now() - timedelta(minutes = 45)).timestamp())

exp是一个时间戳,定义为从1970年1月1日00:00开始的秒数。 另请参见这里


非常感谢,当我写下以下代码时它能够正常工作:TIMESTAMP = int( (datetime.now() + timedelta(minutes = 15)).timestamp()) - T.Junge
我遇到了类似的问题,我是这样解决的(我在这里发布了我的答案): https://dev59.com/XrHma4cB1Zd3GeqPI0Nv#56590980 - Nah

0

这是适用于我的工作解决方案。没有延迟,它会返回 401 错误。

KEY_ID = "xxxxx"
ISSUER_ID = "xxxxx"
EXPIRATION_TIME = int(round(time.time() + (20.0 * 60.0))) # 20 minutes timestamp
PATH_TO_KEY = '../credentials/AuthKey_xxxxx.p8'
with open(PATH_TO_KEY, 'r') as f:
    PRIVATE_KEY = f.read()

header = {
    "alg": "ES256",
    "kid": KEY_ID,
    "typ": "JWT"
}

payload = {
    "iss": ISSUER_ID,
    "exp": EXPIRATION_TIME,
    "aud": "appstoreconnect-v1"
}

# Create the JWT
token = jwt.encode(header, payload, PRIVATE_KEY)
JWT = 'Bearer ' + token.decode()
HEAD = {'Authorization': JWT}

# Without delay I got 401 error
time.sleep(5)

URL = 'https://api.appstoreconnect.apple.com/v1/apps';
r = requests.get(URL, params={'limit': 200}, headers=HEAD)

@Oleksandt Kovalenko 我正在使用POSTMAN测试这个API,我已经得到了除了私钥之外的所有内容,请问我在哪里可以找到私钥,并且如何在POSTMAN中使用这个密钥? - Ramprasath Selvam

0

首先,请不要在没有上下文管理器的情况下打开文件。这个字符串:

PRIVATE_KEY = open('AuthKey_XXXXXXXXXX.p8', 'r').read()

应该是:

应该是:

with open('AuthKey_XXXXXXXXXX.p8', 'r') as f:
    PRIVATE_KEY = f.read()

这将帮助你避免未来许多未关闭文件的问题。

然后,检查一下你从文件中读取的令牌是否正确?

我看到的下一个问题是时间戳。 "令牌的过期时间,以Unix纪元时间表示;" 你提供的是毫秒,我猜测。


嗨@witold komorowski,实际上我正在Postman中进行测试,我在哪里可以找到私钥? - Ramprasath Selvam

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接