JSON Web Token (JWT) について
アプリとしての認証やインストール用のアクセス トークンを生成するには、JSON Web Token (JWT) を生成する必要があります。 REST API のエンドポイントが JWT を必要とする場合、そのエンドポイントのドキュメントで、JWT を使ってエンドポイントにアクセスする必要があることが示されています。
JWT は RS256 アルゴリズムを使って署名される必要があり、次のような要求を含む必要があります。
| 要求 | 意味 | 詳細 | 
|---|---|---|
iat | 発行時刻 | JWT が作成された時刻。 クロック ドリフトから保護するために、これを過去 60 秒に設定し、サーバーの日付と時刻が正確に (たとえば、ネットワーク タイム プロトコルを使って) 設定されるようにすることをお勧めします。 | 
exp | 有効期限が切れるタイミング | JWT の有効期限。これを過ぎると、インストール トークンの要求に使うことができません。 時間は 10 分以内にする必要があります。 | 
iss | 発行者 | GitHub App の ID。 この値は、JWT の署名を検証するための適切な公開キーの検索に使用されます。 アプリの ID は、GET /app REST API エンドポイントで確認できます。 詳しくは、REST API のドキュメントの「アプリ」をご覧ください。 | 
alg | メッセージ認証コード アルゴリズム | JWT は RS256 アルゴリズムを使って署名しなければならないので、これは RS256 である必要があります。 | 
JWT を使うには、API 要求の Authorization ヘッダーに JWT を渡します。 次に例を示します。
curl --request GET \
--url "http(s)://<em>HOSTNAME</em>/api/v3/app" \
--header "Accept: application/vnd.github+json" \
--header "Authorization: Bearer YOUR_JWT"
ほとんどの場合は、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 identifier
  iss: "YOUR_APP_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 App ID
if len(sys.argv) > 2:
    app_id = sys.argv[2]
else:
    app_id = input("Enter your APP 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 identifier
    'iss': app_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 App ID
if len(sys.argv) > 2:
    app_id = sys.argv[2]
else:
    app_id = input("Enter your APP 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 identifier
    'iss': app_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
app_id=$1 # App 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":'"${app_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
app_id=$1 # App 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":'"${app_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_APP_ID は、実際のアプリの ID に置き換えます。 YOUR_PATH_TO_PEM の値は必ず二重引用符で囲んでください。
#!/usr/bin/env pwsh
$app_id = YOUR_APP_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 = $app_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
$app_id = YOUR_APP_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 = $app_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