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