JSON Web Token (JWT) について
アプリとしての認証やインストール用のアクセス トークンを生成するには、JSON Web Token (JWT) を生成する必要があります。 REST API のエンドポイントが JWT を必要とする場合、そのエンドポイントのドキュメントで、JWT を使ってエンドポイントにアクセスする必要があることが示されています。
JWT は RS256
アルゴリズムを使って署名される必要があり、次のような要求を含む必要があります。
要求 | 意味 | 詳細 |
---|---|---|
iat | 発行時刻 | JWT が作成された時刻。 クロック ドリフトから保護するために、これを過去 60 秒に設定し、サーバーの日付と時刻が正確に (たとえば、ネットワーク タイム プロトコルを使って) 設定されるようにすることをお勧めします。 |
exp | 有効期限が切れるタイミング | JWT の有効期限。これを過ぎると、インストール トークンの要求に使うことができません。 時間は 10 分以内にする必要があります。 |
iss | 発行者 | クライアント ID または GitHub Appのアプリケーション ID。 この値は、JWT の署名を検証するための適切な公開キーの検索に使用されます。 アプリの ID は、GitHub App の設定ページにあります。 クライアント ID の使用をお勧めします。 GitHub App の設定ページに移動する方法の詳細については、「GitHub App 登録の変更」を参照してください。 |
alg | メッセージ認証コード アルゴリズム | JWT は RS256 アルゴリズムを使って署名しなければならないので、これは RS256 である必要があります。 |
JWT を使うには、API 要求の Authorization
ヘッダーに JWT を渡します。 次に例を示します。
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 Token (JWT) を生成する
ほとんどのプログラミング言語には、JWT を生成できるパッケージがあります。 いずれの場合も、秘密キーと GitHub App の ID を用意する必要があります。 秘密キーの生成に関する詳細については、「GitHub Apps の秘密キーの管理」を参照してください。 アプリの ID は、GET /app
REST API エンドポイントで確認できます。 詳しくは、REST API のドキュメントの「アプリ」をご覧ください。
注: JWT を作成する代わりに、GitHub の Octokit SDK を使い、アプリとして認証できます。 この 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/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 PyJWT
を実行して PyJWT
パッケージをインストールする必要があります。
#!/usr/bin/env python3 import sys import time import jwt # 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 = 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 encoded_jwt = jwt.encode(payload, signing_key, algorithm='RS256') print(f"JWT: {encoded_jwt}")
#!/usr/bin/env python3
import sys
import time
import jwt
# 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 = 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
encoded_jwt = jwt.encode(payload, signing_key, algorithm='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