GitHub Apps からトークンを生成して GitHub API を利用する
GitHub API を利用するプログラムを書くときは、パーソナルアクセストークンを利用するのがお手軽なやり方です。
しかし、GitHub Apps を利用すると、
という優れた点を享受できます。GitHub Apps はトークン発行の手順がパーソナルアクセストークンに比べると複雑なので、本記事で紹介したいと思います。
手順は、以下です。 (前提として、GitHub Apps を作成して、Organization に GitHub Appsをインストール設定済み、GitHub Apps の設定から秘密鍵を生成済みとします。)
トークン取得手順
JWTデータの生成
トークンを取得する前に、まず GitHub Apps の認証をします。認証には、GitHub Apps の設定画面から生成した秘密鍵で署名した、JWTデータを利用します。
以下は、GitHub のドキュメントに掲載されているJWTデータを生成するRubyスクリプトの例です(jwt の gem をインストールしておく必要あり)。
jwt.rb
require 'openssl' require 'jwt' # https://rubygems.org/gems/jwt # Private key contents private_pem = File.read(ARGV[0]) 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: "ARGV[1]" } jwt = JWT.encode(payload, private_key, "RS256") puts jwt
Installationの選定
上記 Ruby スクリプトを実行することで得られたJWTデータを利用して、Installation のリストを取得します。Installation とは、GitHub Apps をインストールしている主体のことです。例えば、GitHub Apps を公開して、様々な個人ユーザーのアカウントや企業の Organization にインストールされたとすると、このインストールをした一つ一つの主体が Installation となります。トークンは Installation の単位で発行されるので、トークンを生成するときにはどの Installation を対象にトークンを発行するのか決めるために、Installation ID を指定する必要があります。
以下は、Installation のリストを取得するための curl リクエストです。${JWT} のところに、前節で生成したJWTの値を入れます。
$ curl -i -X GET \
-H "Authorization: Bearer ${JWT}" \
-H "Accept: application/vnd.github+json" \
https://api.github.com/app/installations
リクエストが成功すると、以下の応答例のように Installation の一覧を取得できます。応答データから、トークンを発行したい Installation の id を控えます。もし、 GitHub Apps をプライベートに設定していると、GitHub Apps のオーナーのみが GitHub Apps をインストールできる設定となっているので、自作のGitHub Apps をインストールした状態であれば、Installation の数はおそらく1つになります。
https://api.github.com/app/installations の応答例
[
{
"id": 1,
"account": {
"login": "octocat",
"id": 1,
"node_id": "MDQ6VXNlcjE=",
"avatar_url": "https://github.com/images/error/octocat_happy.gif",
"gravatar_id": "",
"url": "https://api.github.com/users/octocat",
"html_url": "https://github.com/octocat",
"followers_url": "https://api.github.com/users/octocat/followers",
"following_url": "https://api.github.com/users/octocat/following{/other_user}",
"gists_url": "https://api.github.com/users/octocat/gists{/gist_id}",
"starred_url": "https://api.github.com/users/octocat/starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users/octocat/subscriptions",
"organizations_url": "https://api.github.com/users/octocat/orgs",
"repos_url": "https://api.github.com/users/octocat/repos",
"events_url": "https://api.github.com/users/octocat/events{/privacy}",
"received_events_url": "https://api.github.com/users/octocat/received_events",
"type": "User",
"site_admin": false
},
"access_tokens_url": "https://api.github.com/installations/1/access_tokens",
"repositories_url": "https://api.github.com/installation/repositories",
"html_url": "https://github.com/organizations/github/settings/installations/1",
"app_id": 1,
"target_id": 1,
"target_type": "Organization",
"permissions": {
"checks": "write",
"metadata": "read",
"contents": "read"
},
"events": [
"push",
"pull_request"
],
"single_file_name": "config.yaml",
"has_multiple_single_files": true,
"single_file_paths": [
"config.yml",
".github/issue_TEMPLATE.md"
],
"repository_selection": "selected",
"created_at": "2017-07-08T16:18:44-04:00",
"updated_at": "2017-07-08T16:18:44-04:00",
"app_slug": "github-actions",
"suspended_at": null,
"suspended_by": null
}
]
トークンの生成
JWTを生成して、トークン発行対象のInstallation ID も決まったら、以下のように、トークンの生成リクエストを GitHub API に送ります。
${YOUR_JWT} には、生成したJWTの値、${installation_id}には、前段で控えた Installation の id を設定します。
curl -i -X POST \
-H "Authorization: Bearer ${YOUR_JWT}" \
-H "Accept: application/vnd.github+json" \
https://api.github.com/app/installations/${installation_id}/access_tokens
GitHub API へのリクエストに成功すると、応答のjsonデータからトークン値を取得できます。
{
"token": "ghs_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
"expires_at": "2022-10-02T13:39:20Z",
"permissions": {
"metadata": "read"
},
"repository_selection": "selected"
}
GitHub API と通信
取得したトークン値を利用して、GitHub Apps をインストールした時に許可したパーミッションの範囲内で GitHub リソースの読み込みや変更を行うことができます。
例えば、GitHub Apps で Contents の Read-only パーミッションをインストール時に許可されれば、

以下のコマンドでトークンを利用して git clone を実行できます。
git clone https://x-access-token:${TOKEN}@github.com/owner/repo.git
また、以下のコマンドを使えば、対象レポジトリのコミットの一覧を取得できます。
curl \
-H "Accept: application/vnd.github+json" \
-H "Authorization: Bearer ${TOKEN}" \
https://api.github.com/repos/owner/repo/commits
APIのリファレンスを参照しながら実装すれば、様々な GitHub のリソース情報を取得・変更可能です。
手順をコマンドにまとめたもの
ここまでの手順をコマンドにまとめたものが以下です。プライベートの GitHub Apps を作成した前提で、Installation リストの一番最初を Installation ID として設定しています。 トークンを取得すれば、あとは許可された範囲内で GitHub リソースの操作が可能です。
GITHUB_APP_ID=123456
GITHUB_APP_PRIVATE_KEY_PATH="xxxxxx"
# JWT の生成
cat << EOF > jwt.rb
require 'openssl'
require 'jwt' # https://rubygems.org/gems/jwt
private_pem = File.read(ARGV[0])
private_key = OpenSSL::PKey::RSA.new(private_pem)
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: "#{ARGV[1]}"
}
jwt = JWT.encode(payload, private_key, "RS256")
puts jwt
EOF
JWT=$(ruby jwt.rb ${GITHUB_APP_PRIVATE_KEY_PATH} ${GITHUB_APP_ID})
# Installation ID の取得
INSTALLATION_ID=$(curl -s -X GET \
-H "Authorization: Bearer ${JWT}" \
-H "Accept: application/vnd.github+json" \
https://api.github.com/app/installations | jq '.[0].id')
# Token の取得
TOKEN=$(curl -s -X POST \
-H "Authorization: Bearer ${JWT}" \
-H "Accept: application/vnd.github+json" \
https://api.github.com/app/installations/${INSTALLATION_ID}/access_tokens | jq -r .token)
参考
https://docs.github.com/en/developers/apps/building-github-apps/authenticating-with-github-apps