← ブログに戻る

Claude Codeの認証は2層構造になっている。setup-tokenとloginは別物だ

この記事を含む総合ガイド Claude Code 実戦運用ガイド

同一マシンから2つのClaude Codeアカウントを使い分けていたときのことだ。一方はレートリミットの余裕があるMaxアカウント、もう一方は軽い作業用のサブアカウント。どちらもアカウントマネージャーに登録していて、UIにはそれぞれのusageバーが表示されていた。一見、問題はなかった。

しばらくして、UIが「active」として強調しているアカウントと、claudeが実際に使っているアカウントが食い違っていることに気づいた。環境変数にはサブアカウントのトークンが入っていた。Maxアカウントは「login active」として表示されていた。2つのアカウントが、異なる意味で同時に「active」になっていたのだ。

そこで初めて気づいた。Claude Codeの認証は一枚岩ではなく、独立して動く2つのレイヤーで構成されている。

Claude Codeの認証2層構造 — レイヤー2(CLAUDE_CODE_OAUTH_TOKEN)がレイヤー1(credentials.json)を上書きする

レイヤー1: credentials.json

最初のレイヤーは ~/.claude/.credentials.json だ。claude login でブラウザ認証を行うと、このファイルが書き込まれる。OAuthアクセストークンとリフレッシュトークンが格納されており、アクセストークンが期限切れになるとリフレッシュトークンが自動で新しいものを取得する。

ほとんどのユーザーが知っているのはこのレイヤーだ。一度ログインすればcredentialsファイルが書き込まれ、以降の claude 起動時にそのアカウントが使われる。このファイルは単一のAnthropicアカウントに紐づく。

複数アカウントを同一マシンで使い分けるツール(たとえば claude-shift)は、このファイルの中身を差し替えることでアカウントを切り替える。シンプルな仕組みだ。

レイヤー2: CLAUDE_CODE_OAUTH_TOKEN

2番目のレイヤーは環境変数 CLAUDE_CODE_OAUTH_TOKEN だ。claude を起動するシェル環境にこの変数がセットされていると、レイヤー1を完全に上書きする

ランタイムのチェックロジックはシンプルだ。CLAUDE_CODE_OAUTH_TOKEN が環境にあればそれを使い、なければ credentials.json にフォールバックする。環境変数が常に勝つ。

つまり、「loginアカウント」(レイヤー1)と「実行アカウント」(レイヤー2)が同時に別々のアイデンティティを指すことができる。この変数の存在を知らない状態で、スタートアップスクリプトやアカウントマネージャーが変数をセットしていると、私が体験したような状況になる。UIは一方のアカウントを強調しているのに、claude は別のアカウントとして動いている。

OAuthトークンとsetup-tokenの比較 — リフレッシュ有無・有効期限・マルチマシン対応・CI対応・実行優先度

setup-tokenの正体

claude setup-token は、特定のユースケース向けの長期クレデンシャルを生成するコマンドだ。ブラウザ認証が不便または不可能な環境で、Claude Codeを非インタラクティブに動かすためのものだ。CIパイプライン、リモートマシン、ヘッドレスサーバーなどが対象になる。

このコマンドが生成するトークンは、credentials.json にあるOAuthトークンと決定的に異なる点が一つある。リフレッシュトークンを持たない。

通常のOAuthフローでは、短命のアクセストークンと長命のリフレッシュトークンがセットで発行される。アクセストークンが期限切れになると、リフレッシュトークンが裏側で新しいものを取得する。インタラクティブにログインしている環境では、このライフサイクル管理が透過的に動く。

setup-tokenは、1年間の有効期限を持つ単一のクレデンシャルとして発行される。リフレッシュトークンは別途存在しない。期限が来たら新しいものを生成する。その代わり安定性がある。同じトークン文字列を複数マシンで使い回せて、シークレットマネージャーに保存でき、更新のためにブラウザ操作が不要だ。

CLAUDE_CODE_OAUTH_TOKEN にsetup-tokenをセットすると、Claude Codeはそれを実行時のアイデンティティとして使う。setup-tokenがアクセストークンとリフレッシュの仕組みを両方置き換える形だ。

なぜ2層なのか

Claude Codeがカバーすべきデプロイシナリオを考えると、この設計には理由がある。

インタラクティブ・単一マシン: 開発者1人、マシン1台、アカウント1つ。claude login を一度実行すれば credentials.json が全部面倒を見て、自動リフレッシュが動く。レイヤー2は不要。

インタラクティブ・同一マシンで複数アカウント: credentials.json を差し替えてアカウントを切り替える。レイヤー2を使って自動化用に一つのアカウントを「固定」しながら、レイヤー1でインタラクティブセッションを管理することができる。2つのレイヤーが同時に異なる役割を担う。

非インタラクティブ・リモートまたはCI: ブラウザログインが不可能。claude setup-token で安定したクレデンシャルを生成し、環境変数 CLAUDE_CODE_OAUTH_TOKEN にセットする。OAuthの儀式なしにClaude Codeが動く。レイヤー1は不要。

複数マシン: 複数のマシンに同じアカウントを展開し、それぞれ異なるワークロードを動かす。setup-tokenをシークレットマネージャー経由で各マシンに配布すれば、個別のブラウザログインが不要になる。

2層設計は冗長性ではなく、同じツールをインタラクティブOAuthフローに依存できない幅広いデプロイ環境で動かすための仕組みだ。

split問題

2つのレイヤーを独立して管理するツールを使うと、両者が乖離することがある。レイヤー1がアカウントA(最後に claude login したのがAだったため)を指し、レイヤー2がアカウントB(BのsetupトークンがTOKEN環境変数にセットされているため)を指す。これが冒頭で体験した状態だ。

どちらのアカウントを実際に使いたいかによって、一方は正しく、もう一方はステール(古くなった)状態だ。問題は、ツールが明示的にチェックしない限り、CLIもUIも2つのレイヤーが別の場所を指していることを明確に示してくれない点だ。

いまの私が使っているパターンはシンプルだ。「どのアカウントがactive?」を確認するとき、両方のレイヤーを確認する。一致していればクリーンな状態だ。乖離していれば、どちらを使うべきかを意図的に決め、もう一方を消去する。

実践的な含意

単一マシン・単一アカウントでClaude Codeを使うだけなら、この話は関係ない。デフォルトのOAuthフローが全部処理してくれる。

複数アカウントを使う場合、またはsetup-tokenを自動化に使う場合、以下の3点を理解しておくと後で困らない。

  1. CLAUDE_CODE_OAUTH_TOKEN は常に credentials.json より優先される。変数がセットされていれば、それが実行アカウントだ。
  2. claude logincredentials.json を書き込むが、CLAUDE_CODE_OAUTH_TOKEN には触れない。インタラクティブにログインし直しても、セット済みのトークンピンはクリアされない。
  3. setup-tokenは自動ローテーションしない。1年後の期限前にリマインダーを設定しておくべきだ。

claude-shift のようなツールは、この2層を1つの UI で管理できる。Layer 1 の切り替えは login 切替ボタン、Layer 2 の切り替えは token 切替ボタンで独立して操作でき、実効 active なカードが青い左ボーダーで強調される。2層が乖離すると split 警告バナーが表示される。

claude-shift UI — login切替・token切替ボタン、token pinバッジ、activeカードの青い強調

hands-onの手順——setup-tokenの取得方法、アカウントの切り替え方、2つのレイヤーが乖離したときの回復手順——については、後日公開予定のQiita記事で扱う。

2層設計は、知ってしまえば複雑ではない。難しいのは、公式ドキュメントのどこにも「2層ある」と書いていない点だ。

実践Claude Code 関連書籍 実践Claude Code 毎日 Claude Code を使うエンジニアへ — CLAUDE.md・Plan Mode・チーム展開の実践ガイド 書籍ページを見る →