生成用于App Store Connect API的令牌。

6

我需要为Store Connect API生成JWT令牌。我尝试使用ruby-jwt宝石。这是我的令牌生成代码,

payload = {
      'iss': my_issuer_id_from_db,
      'exp': generated_unix_timestamp, #Time.now + 20min
      'aud': 'hard_coded_string_from_doc'
  }
  header = {
      'alg': 'ES256',
      'kid': my_key_id_from_db,
      'typ': 'JWT'
  }

private_key = OpenSSL::PKey.read(File.read('/tmp/private_key.pem'))
# private_key - <OpenSSL::PKey::EC:0x000000000XXXXXXX>

@token = JWT.encode(payload, private_key, 'ES256', header)
# encoded_header.encoded_payload.emcoded_signature

我将这个令牌放在请求头中:

headers = { Authorization: 'Bearer' + @token }

我收到的响应是:

        "errors": [{
                "status": "401",
                "code": "NOT_AUTHORIZED",
                "title": "Authentication credentials are missing or invalid.",
                "detail": "Provide a properly configured and signed bearer token, and make sure that it has not expired. Learn more about Generating Tokens for API Requests https://developer.apple.com/go/?id=api-generating-tokens"
        }]
}

我认为问题在于令牌(直接与签名有关)。当我尝试使用在线工具解码令牌时,我的有效负载和标头都被正确解码。状态:无效签名。

我做错了什么?有没有任何想法如何正确执行?


3
  1. 在“Bearer”和令牌之间加一个空格。标题应为“Authorization: Bearer eyJ...”
  2. 在jwt.io网站上,你是否将密钥粘贴到了密钥字段中?许多人会忘记这一点,所以不知道为什么该工具会显示“Invalid signature”(无效签名)。
- jps
我尝试过,但jwt.io工具需要公钥和私钥。我只有私钥。 - Radosław Sakowicz
1
第一点是原因。 - Radosław Sakowicz
1
嘿,我也遇到了同样的问题。你是怎么解决这个问题的?你能分享整个Ruby文件吗?谢谢! - gmoraleda
4个回答

3
我刚刚在这里创建了Python3版本。可能与获取时间的issuedexpired有关,导致401身份验证凭据缺失或无效
请检查您的函数获取的时间是否符合苹果推荐的UNIXepoch时间
import jwt
import datetime as dt

key_id = '#####' 
alg = 'ES256'
typ = 'JWT'
issue_id = '##########################'
aud = 'appstoreconnect-v1'

# Define issue timestamp.
issued_at_timestamp = int(dt.datetime.now().timestamp())
# Define expiration timestamp. May not exceed 20 minutes from issue timestamp.
expiration_timestamp = issued_at_timestamp + 20*60

# Define JWT headers.
headers = dict()
headers['alg'] = alg
headers['kid'] = key_id
headers['typ'] = typ

# Define JWT payload.
payload = dict()
payload['iss'] = issue_id
payload['iat'] = issued_at_timestamp
payload['exp'] = expiration_timestamp
payload['aud'] = aud

# Path to signed private key.
KEY_FILE = '#########.p8' 

with open(KEY_FILE,'r') as key_file:
     key = ''.join(key_file.readlines())

client_secret = jwt.encode(
payload=payload,  
headers=headers,
algorithm=alg,  
key=key
)

with open('client_secret.txt', 'w') as output: 
     output.write(client_secret)
    
    
# Usage, after run this code by python3
# get token from `client_secret.txt` and replace to [signed token]
# Remember expired time maximum is 20 minutes
#
# curl -v -H 'Authorization: Bearer [signed token]' "https://api.appstoreconnect.apple.com/v1/apps"
#
# More detail https://developer.apple.com/documentation/appstoreconnectapi/generating_tokens_for_api_requests

2
你的授权字符串中缺少了一个空格。在修改为以下内容后,你的代码就可以正常工作:
``` headers = { Authorization: 'Bearer ' + @token } ```
"Bearer"是一种身份验证方案,在此处用于指定访问令牌的类型。

2

我面临着类似的身份验证错误,即 NOT_AUTHORIZED。通过以下步骤,我解决了这个问题:

1. 创建Ruby脚本文件以生成有效的Bearer令牌:

参考: https://medium.com/xcblog/generating-jwt-tokens-for-app-store-connect-api-2b2693812a35

require "base64"
require "jwt"
ISSUER_ID = "YOUR_ISSUER_ID"
KEY_ID = "YOUR PRIVATE KEY ID"    // this is ID part from downloaded .p8 file name (see below for ref.)
private_key = OpenSSL::PKey.read(File.read(path_to_your_private_key/AuthKey_#{KEY_ID}.p8))   // you can enclose your file path in quotes if needed, and can pass here totally static file path (here we are reusing Key_ID variable)

token = JWT.encode(
   {
    iss: ISSUER_ID,
    exp: Time.now.to_i + 20 * 60,
    aud: "appstoreconnect-v1"
   },
   private_key,
   "ES256",
   header_fields={
       kid: KEY_ID }
)
puts token

在您的 Mac 上使用以下命令运行此脚本。

$ ruby jwt.rb

这将在终端屏幕上显示一个有效的 Bearer 令牌,您可以在下一步中使用它。

注:

  • 为了运行上述脚本,您需要安装 ruby
  • 您将从开发者帐户中复制 Issuer ID。如果没有,请生成一个。
  • 确保您正在对经过身份验证的用户使用 '.p8' 证书,这意味着您下载 '.p8' 证书的帐户应具有执行 API 级操作的权限。对于我的情况,我使用了 管理员 类型的帐户。最初,我使用的是 开发人员 类型的用户帐户,当我进行最终的 Curl 调用以获取令牌时,它一直给我返回 未授权 错误。

2. 使用令牌:

现在,我们已经看到如何生成用于访问 App Store Connect API 的令牌,我们可以通过传递授权头来使用它。例如,要获取所有用户的列表,我们可以使用以下命令:

$ curl  https://api.appstoreconnect.apple.com/v1/users --Header "Authorization: Bearer lOOOOOOOOOOOONG_GENERATED_TOKEN"

这将列出App Store Connect的所有用户。请记住,我们必须在每个请求中使用此令牌,并且我们必须在每20分钟后创建新令牌。


1
这个例子是从xcblog(https://medium.com/xcblog/wwdc18-a-basic-guide-to-app-store-connect-api-366208651ac6)复制粘贴的,即使删除注释并填写合法值后仍然无法工作。这可能与未使用header_fields有关。(尝试在文件上运行rubocop。) - ablarg
请确保已安装JWT gem以使脚本正常工作:sudo gem install jwt - Tom Curran

1

我有一个来自2023年的答案,利用了@Nah答案中的Ruby脚本,但对我有效,因为它添加了一些在旧答案中缺失的键。

Ruby脚本:

require "base64"
require "jwt"
ISSUER_ID = "YOUR-ISSUER-ID"
KEY_ID = "YOUR-KEY-ID"
private_key = OpenSSL::PKey.read(File.read("PATH_TO_KEY_DOWNLOADED_FROM_APP_STORE_CONNECT.p8"))
token = JWT.encode(
  {
    iss: ISSUER_ID,
    exp: Time.now.to_i + 20 * 60,
    aud: "appstoreconnect-v1",
    bid: "YOUR_BUNDLE_ID"
  },
  private_key,
  "ES256",
  header_fields = {
    alg: "ES256",
    kid: KEY_ID,
    typ: "JWT"
  }
)
puts token

您可以将这个密钥放入与苹果通信时的Authorization头中。
例如,我需要从苹果获取有关应用内购买事件的测试通知,因此我可以将令牌放入curl请求中并获得200:
curl -v --request POST \
-H "Authorization: Bearer YOUR_LONG_TOKEN_HERE" \
"https://api.storekit-sandbox.itunes.apple.com/inApps/v1/notifications/test"

如果您更愿意使用Postman,可以在“授权”选项卡中设置一个“Bearer Token”为Ruby脚本生成的令牌。请保留HTML标签。

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