初めて GitHub 連携を作るとき、GitHub App と OAuth App のどちらが欲しいのかを見極めるのに 1 時間を費やすことになります。ドキュメントは両者を同等の選択肢として扱います。違います。Webhook の話だけでも、たいていは選択が自ずと決まります。
ごく短い要約
- GitHub App: アカウントやリポジトリにインストールされ、きめ細かい権限を持ち、インストールされたリソースの Webhook を受け取り、短命トークンで自分自身として認証します。
- OAuth App: ユーザーが認可し、その権限で本人の代わりに動作し、サードパーティツールのように自分で設定したときだけ Webhook を受け取り、長命トークンでユーザーとして認証します。
アクティブなユーザーなしで動くもの——CI ボット、コードレビュー自動化、スケジュール実行など——を作るなら GitHub App が欲しいはずです。ログイン中のユーザーの代わりにアクション(コード検索 UI など)を行うツールなら、OAuth App が適した形です。
Webhook の受け取り方
GitHub Apps
アプリ作成時に Webhook URL を 1 つ設定します。ユーザーがアカウントやリポジトリにアプリをインストールすると、GitHub はその範囲のイベントをあなたの URL へ送り始めます。イベントには installation(インストール/アンインストールのライフサイクル)、installation_repositories(追加・削除されたリポジトリ)、アプリが購読したイベント型(push、pull_request など)が含まれます。
各イベントには X-GitHub-Hook-Installation-Target-Type ヘッダー(「integration」)と installation ID が含まれ、その特定のインストール向けトークンを発行できます。署名は X-Hub-Signature-256 の HMAC SHA-256——リポジトリ Webhook と同じ形です。
OAuth Apps
OAuth Apps には組み込みの Webhook 購読がありません。Webhook を受け取るには、認可したユーザーがアプリの URL を指すリポジトリまたは組織の Webhook を作成する必要があります。つまりアプリの Webhook の到達範囲は、アプリ自身がどこに「インストール」されているかではなく、ユーザーがどこに購読を設定したかに紐づきます。
認可後に GitHub API 経由で自動的に Webhook を作る OAuth Apps を作るチームもあります。動きますが、アプリ自身のロジックに加えてユーザーごとの Webhook ライフサイクルを管理することになります。
認証こそ本当の分岐点
GitHub Apps は JWT 署名リクエストで認証し、インストールごとに短命のインストールトークン(1 時間)を発行します。JWT はアプリ作成時に生成する秘密鍵で署名します。コードは:
// 1. アプリの秘密鍵で JWT を署名(有効期限 10 分)
const jwt = createAppJwt(APP_ID, PRIVATE_KEY);
// 2. JWT をインストールトークンに交換(有効 1 時間)
const token = await fetchInstallationToken(jwt, INSTALLATION_ID);
// 3. そのトークンで API 呼び出し
const res = await fetch('https://api.github.com/repos/x/y/issues', {
headers: { Authorization: `token ${token}` },
});
インストールトークンはインストールに限定され自動的に失効するため、漏洩しても影響範囲は限られます。
OAuth Apps は標準 OAuth フローで得たユーザーアクセストークンで認証します。これらは既定で長命でユーザーとして動作するため、1 つ漏れればそのユーザーにできることすべてを攻撃者ができます。GitHub のドキュメントが新規連携で GitHub Apps を勧めるのは一部この理由です。
権限:限定 対 広範
GitHub Apps では細かい権限を要求できます。issues を読む、checks を書く、pull requests を読む、それ以外はアクセスなし。各権限は独立しています。ユーザーは正確な一覧を見て拒否できます。
OAuth Apps は古い scope ベースのシステムを使います:repo、read:user など。scope は粗いです。repo scope はユーザーが見えるすべてのリポジトリに読み書きを与えます——「特定リポジトリのみ読み取り」版はありません。
コードを読み check runs を書くだけのコードレビューボットには、2 つの具体的な権限を持つ GitHub App のほうが、完全な repo アクセスを求める OAuth App よりはるかに侵襲が少ないです。
両方のローカルテスト
Webhook 署名の仕組みは同一です——セットアップは GitHub Webhook のローカルテストを参照。違いは URL の登録方法です。
- GitHub App: アプリの設定ページで Webhook URL を設定。テスト用リポジトリにアプリをインストール。イベントを発火。以上。
- OAuth App: アプリ自体に Webhook はありません。トンネル URL を指すリポジトリ Webhook を(リポジトリ設定ページから手動、または API で)追加します。
OAuth Apps では OAuth コールバックフロー自体もテストが必要です——OAuth コールバックをローカルでテストする方法を参照。
両者間の移行は苦痛
OAuth App で始めて後から GitHub App が必要だと気づいても、移行はできません。ユーザーは新アプリを再認可し、保存済みトークンは再発行し、OAuth App 経由で設定したリポジトリ Webhook は削除するまで発火し続けます。最初の 10 分を正しい種類選びに使いましょう。
どちらを選ぶか
GitHub App を選ぶのは:
- 連携が独自のスケジュールやイベントへの反応で、ログイン中のユーザーなしに動く。
- 特定のリポジトリや組織に限定された権限が欲しい。
- リポジトリごとの設定ではなく、インストール範囲に紐づく Webhook が欲しい。
- いずれ GitHub Marketplace に掲載されるものを作っている。
OAuth App を選ぶのは:
- ツールがユーザーのログインする UI で、ユーザー自身の GitHub アカウント内で操作する。
- アクセスパターンを含めユーザーとして正確に動作する必要がある。
- Webhook が不要、またはリポジトリごとに手動で設定する。
署名検証の基礎は 署名検証ガイドを参照。GitHub の Webhook タイミングと再送用の捕捉を扱うトンネルは PortPreview のウェイトリストへ。