Claude CodeでCI向け長期トークンを発行する方法
claude setup-tokenは、ブラウザーログインが使えないCI環境向けに1年間有効なOAuthトークンを発行します。v2.1.285の実機出力をもとに、発行手順・優先順位のつまずき・できないこと・失効時の対処を扱います。
CIの認証はOAuthトークン・APIキー・WIFのどれか
claude setup-tokenは、ブラウザーログインが使えないCIやスクリプト向けに、1年間有効なOAuthトークンを発行するコマンドです。Claude Pro・Max・Team・Enterpriseのいずれかのサブスクリプションが必要で、発行した値はCLAUDE_CODE_OAUTH_TOKEN環境変数に入れて使います。CIの認証情報はほかにも選べるので、先に3つの違いを見ておきます。
CIの認証情報の3つの選択肢
OAuthトークン(setup-token)
サブスクリプションで認証されます。トークンは
claude setup-tokenを実行した本人のサブスクリプションに紐づくため、複数リポジトリで共有するSecretsには、公式のGitHub ActionsガイドがAPIキーを勧めています。発行は手元のPCで1回だけです。有効期間は1年です。APIキー(Claude Console)
ANTHROPIC_API_KEYに入れます。Claude Console側にAPI利用のアカウントが必要です。非対話モード(-p)では、環境にキーがあれば常にそのキーが使われます。Workload Identity Federation(WIF)
CI側が出すOIDCトークンを、短命なAnthropicのトークンに交換します。保管する静的なシークレットがありません。対象はGitHub Actionsに限らず、AWS・Google Cloud・Kubernetes・Okta・Microsoft Entra IDなどのOIDC発行元です。
WIFの設定では、Claude Consoleにフェデレーションルール・サービスアカウント・発行元(issuer)を作ります。GitHub Actionsのジョブ側にはid-token: write権限と、ルールID・組織ID・サービスアカウントIDの環境変数が要ります。トークンの有効期間は、APIで指定を省略したときの既定が3600秒です。Consoleのウィザードの初期値は600秒で、GitHub Actionsガイドの例も600秒です。実際の寿命は、ルールの設定値と、提示したOIDCトークンの残り時間の2倍の短いほうで決まります。共有の自動化で個人のOAuthトークンを配りたくない組織は、この選択肢が候補になります。OpenClawなど他のCLIで同じサブスクリプションを再利用する場合の扱いは別記事にあります。
トークンを発行してCIに渡す流れ
発行からCIでの利用まで
- 1
手元のPCで発行する
ブラウザーで承認すると、トークンがターミナルに表示されます。Claude Codeはこの値をどこにも保存しません。
- 2
値をコピーしてCIのSecretsに登録する
表示を取りこぼしたときは、コマンドをもう一度実行して発行し直します。ワークフローには
${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}のような参照だけを書き、値そのものはコミットしません。 - 3
ジョブの環境変数に渡す
シェルなら
export CLAUDE_CODE_OAUTH_TOKEN=your-tokenです。GitHub Actionsでの具体的な書き方はClaude CodeをGitHub Actionsに組み込むにあります。 - 4
どの認証情報が使われるか確かめる
実行環境で
claude auth statusを実行します。次節のとおり、出力から実際に選ばれた認証情報が読めます。
claude setup-tokenコマンドは/loginと同じブラウザー認可フローを開きます。ログイン方式全体の違いはClaude Codeログイン方法3種の使い分けにまとめています。
実機の出力から分かること(v2.1.285で確認)
claude setup-tokenは実行するとブラウザー認可に進むので、ここでは--helpだけを確認しました。空のディレクトリをCLAUDE_CONFIG_DIRに指定し、手元の設定に触れない状態です。
$ claude --version
2.1.285 (Claude Code)
$ claude setup-token --help
Usage: claude setup-token [options]
Set up a long-lived authentication token (requires Claude subscription)
Options:
-h, --help Display help for commandオプションは--helpだけです。有効期間の指定や、権限範囲を絞るフラグはありません。トークンの有効期間は、実行時に選ぶものではなく1年で決まっています。
次は認証状態を調べるclaude auth statusです。同じ空の設定ディレクトリで、環境変数の有無を変えて実行しました。トークンにはダミーの文字列(dummy)を入れています。
$ claude auth status --json # 何も設定していない環境
{
"loggedIn": false,
"authMethod": "none",
...
}
(終了コード 1)
$ CLAUDE_CODE_OAUTH_TOKEN=dummy claude auth status --json
{
"loggedIn": true,
"authMethod": "oauth_token",
...
}
(終了コード 0)
$ CLAUDE_CODE_OAUTH_TOKEN=dummy ANTHROPIC_API_KEY=dummy2 claude auth status --text
Auth token: CLAUDE_CODE_OAUTH_TOKEN · not in use
API key: ANTHROPIC_API_KEY出力から読めることが3つあります。
- 認証情報が何も無いと終了コードが1になります。ジョブの冒頭に置けば、Secretsの渡し忘れを検出できます。
- ダミーの値でも
loggedIn: trueになりました。トークンが有効かどうかを確かめるコマンドではなく、どの認証情報を拾うかを見るコマンドです。 - トークンとAPIキーを同時に置くと、トークンの行に
not in useが付きました。使われるのはAPIキーの側です。
OAuthトークンを置いたのにAPIキーが使われるとき
上の3つ目の出力は、認証情報の優先順位そのものです。順位は次のとおりです。
- クラウドプロバイダーの認証情報(Bedrock・Vertex・Foundry)
ANTHROPIC_AUTH_TOKENANTHROPIC_API_KEYapiKeyHelperの出力CLAUDE_CODE_OAUTH_TOKEN- Anthropicプロファイル・フェデレーション認証情報
/loginのサブスクリプション認証
CLAUDE_CODE_OAUTH_TOKENは5番目です。ランナーやコンテナのイメージに古いANTHROPIC_API_KEYが残っていると、トークンを設定してもAPIキー側にリクエストが流れます。claude auth statusなら、この状態が見えます。直し方は、CI側でキーをunsetするか、Secretsから古いキーを消すかのどちらかです。
サブスクリプションで使っているつもりがAPIキー経由になっていた、というケースは、公式のエラー対処でも/statusのキー行を確認する手順として案内されています。
もうひとつの落とし穴が--bareです。実機でも、ダミーのトークンを渡してclaude --bare auth status --textを実行すると、Not logged in. Run claude auth login to authenticate.と出ました。bareモードはCLAUDE_CODE_OAUTH_TOKENを読みません。claude --bare -pで起動を速くする構成では、ANTHROPIC_API_KEYかapiKeyHelperで認証します。bareでは、hooks・skills・カスタムコマンド・サブエージェント・プラグイン・MCPサーバー・自動メモリ・CLAUDE.mdの自動検出も省かれます。使い分けは-pモードでスクリプトを自動化する基本で扱っています。
このトークンでできないこと
CLAUDE_CODE_OAUTH_TOKENが認証できるのはモデルへのリクエストだけです。Remote Controlセッションの確立と、claude.aiコネクタの取得はできません。ローカルの.mcp.jsonやsettings.jsonで設定したMCPサーバーは、そのまま動きます。
CIでコードを書かせるだけなら支障はありません。Remote Controlやコネクタを前提にしたワークフローでは、/loginのサブスクリプション認証に戻す必要があります。
トークンが失効・取り消しされたとき
失効すると、次のいずれかのメッセージが出ます。
OAuth token revoked · Please run /login
Please run /login · API Error: 401 OAuth token has expired ...CLAUDE_CODE_OAUTH_TOKENで認証しているセッションは、401が返っても保存済みのログインに切り替わらず、環境変数の値を送り続けます。v2.1.225より前は違いました。セッション中に変数の値が保存済みログインの短命トークンに置き換わることがあり、その期限が切れると再び401で失敗していたと、公式のエラーページに書かれています。古いバージョンを固定しているCIでは、失効の原因が別のところにある可能性があります。
対処は、claude setup-tokenで新しいトークンを発行してSecretsを更新し、ジョブを再実行することです。変数をunsetして/loginに戻す方法もあります。エラー文言ごとの読み方は「OAuth token revoked」の対処にまとめています。
CIのSecretsからトークンを削除しても、発行済みのトークンが無効になるわけではありません。Secretsの削除は、GitHub上の参照が消えるだけです。新しいトークンを発行しても、旧トークンが止まるとは限りません。発行済みトークンを個別に取り消す手順は、公式ドキュメントに見当たりません。errorsページは取り消しの例として、どこからでもサインアウトした場合と、管理者がアクセスを削除した場合を挙げています。
ブラウザーを開けない環境でのプロビジョニング
コンテナを毎回使い捨てる環境では、claude setup-tokenを手で実行するのは現実的ではありません。環境変数CLAUDE_CODE_OAUTH_REFRESH_TOKENとCLAUDE_CODE_OAUTH_SCOPESを設定すると、claude auth loginはブラウザーを開かずにトークンを直接交換します。CLAUDE_CODE_OAUTH_SCOPESは、リフレッシュトークンを設定した場合は必須です。公式の例は"user:profile user:inference user:sessions:claude_code"です。
実機では、claude auth loginのオプションは--claudeai(既定)・--console・--email・--ssoでした。リフレッシュトークンの払い出し方は、公式ドキュメントに詳しい運用例がありません。本番のCIに入れる前に、手元で挙動を確かめてから使います。
まとめ
個人のサブスクリプションで回すか、ConsoleのAPIキーかWIFに寄せるかは、Secretsを何リポジトリで共有するかで決まります。1年後に止まる前提で、発行日をローテーション計画に入れておくのが最低限です。
よくある質問
組織のログイン制限は、トークンの発行に影響しますか
forceLoginMethodだけが適用され、forceLoginOrgUUIDは確認されません。公式は、別の組織でトークンを生成できると説明しています。組織を限定してログインさせている場合の仕組みはClaude Codeで組織限定ログインを強制する方法にあります。
GitHub Actions以外のCIでも使えますか
使えます。CLAUDE_CODE_OAUTH_TOKENは、GitHub Actions専用ではなくClaude Code CLIが読む環境変数です。GitLab CIやコンテナ内のジョブなど、CLIを呼び出せる環境なら同じ変数名で認証します。