关于 JSON Web Token (JWT)
为了以应用身份进行身份验证或生成安装访问令牌,您必须生成 JSON Web Token (JWT)。如果 REST API 端点需要 JWT,则该端点的文档将指示您必须使用 JWT 才能访问该端点。
您的 JWT 必须使用 RS256
算法进行签名,并且必须包含以下声明。
声明 | 含义 | 详情 |
---|---|---|
iat | 颁发时间 | JWT 创建的时间。为了防止时钟漂移,我们建议您将此时间设置为过去 60 秒,并确保您的服务器日期和时间设置准确(例如,使用网络时间协议)。 |
exp | 过期时间 | JWT 的过期时间,在此时间之后,它将无法用于请求安装令牌。该时间必须不超过未来 10 分钟。 |
iss | 发行者 | 您的 GitHub 应用的客户端 ID 或应用程序 ID。此值用于查找正确的公钥以验证 JWT 的签名。您可以在 GitHub 应用的设置页面上找到您的应用 ID。建议使用客户端 ID。有关导航到 GitHub 应用设置页面的更多信息,请参阅 "修改 GitHub 应用注册." |
alg | 消息认证码算法 | 这应该是 RS256 ,因为您的 JWT 必须使用 RS256 算法进行签名。 |
要使用 JWT,请将其传递到 API 请求的 Authorization
标头中。例如
curl --request GET \
--url "https://api.github.com/app" \
--header "Accept: application/vnd.github+json" \
--header "Authorization: Bearer YOUR_JWT" \
--header "X-GitHub-Api-Version: 2022-11-28"
在大多数情况下,您可以使用Authorization: Bearer
或 Authorization: token
传递令牌。但是,如果您传递的是 JSON Web 令牌 (JWT),则必须使用 Authorization: Bearer
。
生成 JSON Web 令牌 (JWT)
大多数编程语言都有一个可以生成 JWT 的包。在所有情况下,您都必须拥有私钥和 GitHub 应用程序的 ID。有关生成私钥的更多信息,请参阅“管理 GitHub 应用程序的私钥”。您可以在 REST API 端点 GET /app
中找到应用程序的 ID。有关更多信息,请参阅 REST API 文档中的“应用程序”。
注意:您可以使用 GitHub 的 Octokit SDK 来验证应用程序,而不是创建 JWT。SDK 将负责为您生成 JWT,并在令牌过期后重新生成 JWT。有关更多信息,请参阅“使用 REST API 和 JavaScript 编写脚本”。
示例:使用 Ruby 生成 JWT
注意:您必须运行 gem install jwt
来安装 jwt
包才能使用此脚本。
在以下示例中,将 YOUR_PATH_TO_PEM
替换为存储私钥的文件路径。将 YOUR_APP_ID
替换为应用程序的 ID。确保将 YOUR_PATH_TO_PEM
和 YOUR_APP_ID
的值用双引号括起来。
require 'openssl'
require 'jwt' # https://rubygems.org.cn/gems/jwt
# Private key contents
private_pem = File.read("YOUR_PATH_TO_PEM")
private_key = OpenSSL::PKey::RSA.new(private_pem)
# Generate the JWT
payload = {
# issued at time, 60 seconds in the past to allow for clock drift
iat: Time.now.to_i - 60,
# JWT expiration time (10 minute maximum)
exp: Time.now.to_i + (10 * 60),
# GitHub App's client ID
iss: "YOUR_CLIENT_ID"
}
jwt = JWT.encode(payload, private_key, "RS256")
puts jwt
示例:使用 Python 生成 JWT
注意:您必须运行 pip install jwt
来安装 jwt
包才能使用此脚本。
#!/usr/bin/env python3 from jwt import JWT, jwk_from_pem import time import sys # Get PEM file path if len(sys.argv) > 1: pem = sys.argv[1] else: pem = input("Enter path of private PEM file: ") # Get the Client ID if len(sys.argv) > 2: client_id = sys.argv[2] else: client_id = input("Enter your Client ID: ") # Open PEM with open(pem, 'rb') as pem_file: signing_key = jwk_from_pem(pem_file.read()) payload = { # Issued at time 'iat': int(time.time()), # JWT expiration time (10 minutes maximum) 'exp': int(time.time()) + 600, # GitHub App's client ID 'iss': client_id } # Create JWT jwt_instance = JWT() encoded_jwt = jwt_instance.encode(payload, signing_key, alg='RS256') print(f"JWT: {encoded_jwt}")
#!/usr/bin/env python3
from jwt import JWT, jwk_from_pem
import time
import sys
# Get PEM file path
if len(sys.argv) > 1:
pem = sys.argv[1]
else:
pem = input("Enter path of private PEM file: ")
# Get the Client ID
if len(sys.argv) > 2:
client_id = sys.argv[2]
else:
client_id = input("Enter your Client ID: ")
# Open PEM
with open(pem, 'rb') as pem_file:
signing_key = jwk_from_pem(pem_file.read())
payload = {
# Issued at time
'iat': int(time.time()),
# JWT expiration time (10 minutes maximum)
'exp': int(time.time()) + 600,
# GitHub App's client ID
'iss': client_id
}
# Create JWT
jwt_instance = JWT()
encoded_jwt = jwt_instance.encode(payload, signing_key, alg='RS256')
print(f"JWT: {encoded_jwt}")
此脚本将提示您输入存储私钥的文件路径和应用程序的 ID。或者,您可以在执行脚本时将这些值作为内联参数传递。
示例:使用 Bash 生成 JWT
注意:运行此脚本时,您必须将客户端 ID 和存储私钥的文件路径作为参数传递。
#!/usr/bin/env bash set -o pipefail client_id=$1 # Client ID as first argument pem=$( cat $2 ) # file path of the private key as second argument now=$(date +%s) iat=$((${now} - 60)) # Issues 60 seconds in the past exp=$((${now} + 600)) # Expires 10 minutes in the future b64enc() { openssl base64 | tr -d '=' | tr '/+' '_-' | tr -d '\n'; } header_json='{ "typ":"JWT", "alg":"RS256" }' # Header encode header=$( echo -n "${header_json}" | b64enc ) payload_json='{ "iat":'"${iat}"', "exp":'"${exp}"', "iss":'"${client_id}"' }' # Payload encode payload=$( echo -n "${payload_json}" | b64enc ) # Signature header_payload="${header}"."${payload}" signature=$( openssl dgst -sha256 -sign <(echo -n "${pem}") \ <(echo -n "${header_payload}") | b64enc ) # Create JWT JWT="${header_payload}"."${signature}" printf '%s\n' "JWT: $JWT"
#!/usr/bin/env bash
set -o pipefail
client_id=$1 # Client ID as first argument
pem=$( cat $2 ) # file path of the private key as second argument
now=$(date +%s)
iat=$((${now} - 60)) # Issues 60 seconds in the past
exp=$((${now} + 600)) # Expires 10 minutes in the future
b64enc() { openssl base64 | tr -d '=' | tr '/+' '_-' | tr -d '\n'; }
header_json='{
"typ":"JWT",
"alg":"RS256"
}'
# Header encode
header=$( echo -n "${header_json}" | b64enc )
payload_json='{
"iat":'"${iat}"',
"exp":'"${exp}"',
"iss":'"${client_id}"'
}'
# Payload encode
payload=$( echo -n "${payload_json}" | b64enc )
# Signature
header_payload="${header}"."${payload}"
signature=$(
openssl dgst -sha256 -sign <(echo -n "${pem}") \
<(echo -n "${header_payload}") | b64enc
)
# Create JWT
JWT="${header_payload}"."${signature}"
printf '%s\n' "JWT: $JWT"
示例:使用 PowerShell 生成 JWT
在以下示例中,将 YOUR_PATH_TO_PEM
替换为您的私钥存储位置的路径。将 YOUR_CLIENT_ID
替换为您的应用程序 ID。确保将 YOUR_PATH_TO_PEM
的值用双引号括起来。
#!/usr/bin/env pwsh $client_id = YOUR_CLIENT_ID $private_key_path = "YOUR_PATH_TO_PEM" $header = [Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes((ConvertTo-Json -InputObject @{ alg = "RS256" typ = "JWT" }))).TrimEnd('=').Replace('+', '-').Replace('/', '_'); $payload = [Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes((ConvertTo-Json -InputObject @{ iat = [System.DateTimeOffset]::UtcNow.AddSeconds(-10).ToUnixTimeSeconds() exp = [System.DateTimeOffset]::UtcNow.AddMinutes(10).ToUnixTimeSeconds() iss = $client_id }))).TrimEnd('=').Replace('+', '-').Replace('/', '_'); $rsa = [System.Security.Cryptography.RSA]::Create() $rsa.ImportFromPem((Get-Content $private_key_path -Raw)) $signature = [Convert]::ToBase64String($rsa.SignData([System.Text.Encoding]::UTF8.GetBytes("$header.$payload"), [System.Security.Cryptography.HashAlgorithmName]::SHA256, [System.Security.Cryptography.RSASignaturePadding]::Pkcs1)).TrimEnd('=').Replace('+', '-').Replace('/', '_') $jwt = "$header.$payload.$signature" Write-Host $jwt
#!/usr/bin/env pwsh
$client_id = YOUR_CLIENT_ID
$private_key_path = "YOUR_PATH_TO_PEM"
$header = [Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes((ConvertTo-Json -InputObject @{
alg = "RS256"
typ = "JWT"
}))).TrimEnd('=').Replace('+', '-').Replace('/', '_');
$payload = [Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes((ConvertTo-Json -InputObject @{
iat = [System.DateTimeOffset]::UtcNow.AddSeconds(-10).ToUnixTimeSeconds()
exp = [System.DateTimeOffset]::UtcNow.AddMinutes(10).ToUnixTimeSeconds()
iss = $client_id
}))).TrimEnd('=').Replace('+', '-').Replace('/', '_');
$rsa = [System.Security.Cryptography.RSA]::Create()
$rsa.ImportFromPem((Get-Content $private_key_path -Raw))
$signature = [Convert]::ToBase64String($rsa.SignData([System.Text.Encoding]::UTF8.GetBytes("$header.$payload"), [System.Security.Cryptography.HashAlgorithmName]::SHA256, [System.Security.Cryptography.RSASignaturePadding]::Pkcs1)).TrimEnd('=').Replace('+', '-').Replace('/', '_')
$jwt = "$header.$payload.$signature"
Write-Host $jwt