htknでGitHub AppのUser Access Tokenを使い、ローカルにシークレットを置かずGitHubと通信する

htknでGitHub AppのUser Access Tokenを使い、ローカルにシークレットを置かずGitHubと通信する

PATの代わりにGitHub AppのUser Access Tokenを使い、ローカルにシークレットを置かずGitHub APIを利用するCLIツール「ghtkn」を解説。手元に残すのはClient IDだけ。短命トークンはOSのシークレットマネージャが管理し、8時間で失効します。
2026.06.24

クライアント端末からGitHub APIを叩くとき、サプライチェーン攻撃対策として、ローカルにシークレットは置きたくないですよね?
そんなときにぴったりなCLIツールが、ghtknです。

ghtknは以下の3つの特徴を備えています。

1. ローカルにトークンを置かない

ghtknはシークレットでないGitHub AppのClient IDだけをローカルに保存します。トークンもプライベートキーも要りません。Gitコミットしても問題ありません。

apps:
  - name: my-org/dev
    client_id: Ivxxxxxxxxxxxxxxxx

2. 短命のトークンはOSのシークレットマネージャーが管理

ghtknは、GitHub AppのDevice Flowで短命のトークンを発行し、OSのシークレットマネージャー(macOS Keychain / Windows Credential Manager等)に保存します。
しかもトークンは8時間で期限切れになるため、流出リスクが大幅に減ります。

3. 認可はGitHub AppとユーザーのAND

ghtknが利用するUser Access Tokenの権限はユーザーとGitHub AppのANDです。ユーザーに広範な権限があっても、GitHub App側で制限できます。
この仕組みはGitHub AppのUser Access Tokenの仕様であり、ドキュメントでは、次の3項目が明記されています。

  • ユーザーがアクセスできるリソースにしかアクセスできない
  • Appが権限を持つリソースにしかアクセスできない
  • Appがインストールされているアカウントのリソースにしかアクセスできない

ghtknとPATの違い

GitHubのAPI操作でよく使われるPATと比較すると、ghtknの位置づけが分かりやすくなります。

観点 classic PAT fine-grained PAT ghtkn (GitHub App User Access Token)
ローカルに置く秘密情報 トークン本体 トークン本体 なし(Client IDのみ)
トークンの発行・更新 ユーザーが手動で発行・再発行 ユーザーが手動で発行・再発行 ghtknが自動で取得・リフレッシュ
保管場所 ユーザー任せ ユーザー任せ OSのシークレットマネージャ
有効期限 任意(無期限も可) 任意 8時間で失効
組織側の制御 一括許可/ブロックのみ 承認制・失効管理 Appの権限・インストールで管理
権限の絞り込み ユーザー権限に依存 トークン単位で細かく指定 App×ユーザーのANDで制限

やってみる

macOSからghtknを使って、GitHub AppのUser Access Tokenを取得し、gh CLIで利用するまでの流れを確認します。

環境

  • OS : macOS 26.4.1
  • ghtkn : 0.2.5
  • gh : 2.93.0

GitHub Appの準備

まずGitHub Appを作成します。組織のSettings > Developer settings > GitHub Appsから新規作成し、以下を設定してください。

  • GitHub App name: ユニークな名前
  • Expire user authorization tokens: 有効化
  • Enable Device Flow: 有効化
  • Webhook: 無効化
  • Permissions: なし(デフォルトのまま)
  • Repository access: なし(デフォルトのまま)
  • Where can this GitHub App be installed?: Only on this account(=プライベート)

github-app-detail

ghtknのインストール

$ brew install suzuki-shunsuke/ghtkn/ghtkn

$ ghtkn init

$HOME/.config/ghtkn/ghtkn.yamlにGitHub AppのClient IDを記述します。

apps:
  - name: my-org/dev
    client_id: Ivxxxxxxxxxxxxxxxx

トークン取得からgh CLIでの利用まで

ghtkn 経由でトークンを取得しましょう。初回はブラウザが開きDevice Flowの認可が走ります。

$ ghtkn get

The application uses the device flow to generate your GitHub User Access Token.
Copy your one-time code: XXX-XXXX
This code is valid until 2026-06-23T18:01:51+09:00
Press Enter to open https://github.com/login/device in your browser (it opens automatically after 10 seconds)...
Jun 23 17:47:02.918 INF opened the browser program=ghtkn version=0.2.5 app_name=my-org/dev url=https://github.com/login/device
ghu_xxx

ブラウザから one-time code を入力しましょう。

github-app-device-otp

ghと連携してみましょう。

$ ghtkn get
ghu_XXX
$ REPO=my-org/demo-ghtkn
$ env GH_TOKEN=$(ghtkn get) gh issue create -R "$REPO" --title "Hello, ghtkn" --body "This is created by ghtkn"

Creating issue in my-org/demo-ghtkn

GraphQL: Could not resolve to a Repository with the name 'my-org/demo-ghtkn'. (repository)

初回インストール時に、権限(Permissions)とリポジトリ(Repository)を設定していなかったのが原因です。

権限として IssuesのRead and writeを設定し、操作先リポジトリも設定して再度実行しましょう。

$ env GH_TOKEN=$(ghtkn get) gh issue create -R "$REPO" --title "Hello, ghtkn" --body "This is created by ghtkn"

Creating issue in my-org/demo-ghtkn

https://github.com/my-org/demo-ghtkn/issues/4

今度は成功しました。

このように、最小権限から始めて、足りなければCould not resolve to a Repositoryのようなエラーを手がかりに、権限(Issues: Read and write)と対象リポジトリへのインストールを足していく、という進め方ができます。

ghをghtkn向けにラップする

毎回env GH_TOKEN=$(ghtkn get) gh ...と書くのは面倒です。ラップスクリプトを用意すれば、ghコマンドを直接叩くだけでghtknのトークンが透過的に使われます。

#!/usr/bin/env bash
set -eu
if [ -z "${GH_TOKEN:-}" ] && [ -z "${GITHUB_TOKEN:-}" ]; then
  GH_TOKEN="$(ghtkn get)"
  export GH_TOKEN
fi
exec /opt/homebrew/bin/gh "$@"

このスクリプトをPATHが通るディレクトリに置き(例: ~/bin/gh)、実行権限(chmod +x ~/bin/gh)を付与してください。

この仕組みにより、ラップスクリプト経由でghコマンドが呼ばれるたびに、ghtknがキャッシュからトークンを取得してGH_TOKENに注入します。

Claude Codeからの呼び出しにも利用できます。

保存されたトークンを確認する(macOS)

トークンはlogin Keychainにgeneric passwordとして、Client IDをキーに保存されます。中身やメタ情報はsecurityコマンドで確認できます。

$ security dump-keychain login.keychain-db | grep -iB2 -A15 ghtkn
class: "genp"
attributes:
    0x00000007 <blob>="github.com/suzuki-shunsuke/ghtkn"
    0x00000008 <blob>=<NULL>
    "acct"<blob>="Ivxxx"
    "cdat"<timedate>=0x32303236303631343035333032355A00  "20260614053025Z\000"
    ...

最後に

GitHubをクライアント環境からAPI操作するとなると、PATがすぐに思いつきますが、GitHub AppのUser Access Tokenを利用しても、シークレットではないClient IDだけをローカル管理して同様のことを実現できます。これをいい感じにラップしたのが、今回ご紹介したghtknです。特に、fine-grained PATのユースケースの多くをカバーできます。

classic PATのように、一つのトークンで何でも操作できず、fine-grained PATと同じく、組織ごとのインストールとなることにご留意ください。

また、ghtknはDevice Flowでユーザー認証するため、CI/CDのような非対話的な用途には使えないことにご注意ください。

参考

この記事をシェアする

関連記事