anthropics / anthropics/claude-code

WIF: token-exchange assertions are single-use (undocumented) — shared identity-token file + another consumer ⇒ opaque 401 with a misleading hint

オープン
#89,095 コメント 0 件 リアクション 0 件 担当者 0 名 GitHub で見る
area:auth bug has repro platform:linux
主要言語
Python
スター
145k
フォーク
23.1k
PR マージ指標
PR 指標を取得中

説明

## Summary

`POST /v1/oauth/token` (Workload Identity Federation) enforces **one-time use per assertion**: exchanging the same OIDC JWT a second time returns `401 authentication_error: Authentication failed`. This is not mentioned in the WIF docs, and the CLI's error hint ("Ensure your federation rule matches your identity token") points at rule matching, which sends users down the wrong path. Found while migrating GitHub Actions agent workflows to keyless auth; it cost a full day of forensics.

## Reproduction (GitHub Actions, any repo with a working federation rule)

```bash
JWT=$(curl -sS -H "Authorization: Bearer $ACTIONS_ID_TOKEN_REQUEST_TOKEN" \
"$ACTIONS_ID_TOKEN_REQUEST_URL&audience=https://api.anthropic.com" | jq -r .value)
# same body twice:
curl -s https://api.anthropic.com/v1/oauth/token -H content-type:application/json -d "{\"grant_type\":\"urn:ietf:params:oauth:grant-type:jwt-bearer\",\"assertion\":\"$JWT\",\"federation_rule_id\":\"fdrl_…\",\"organization_id\":\"…\",\"service_account_id\":\"svac_…\",\"workspace_id\":\"wrkspc_…\"}"
# -> 200, access_token
# (repeat the identical request)
# -> 401 {"error":{"type":"authentication_error","message":"Authentication failed"}}
```

Observed consistently on 2026-08-23 (JWT age 0–90s made no difference; body/headers/transport identical between the two calls).

## How this bites Claude Code users

The WIF reference says the SDK "re-reads `ANTHROPIC_IDENTITY_TOKEN_FILE` on every exchange so that projected tokens that rotate on disk are always current", and Claude Code (`claude-cli/2.1.241`, headless `-p`) follows the same pattern. If **anything else** exchanges the assertion in that file first — in our case a setup step that validated the federation chain eagerly — Claude Code reads an already-consumed assertion and fails with:

```
API Error: Token exchange failed with status 401 (request-id …): {"error":{"type":"authentication_error","message":"Authentication failed"}} Ensure your federation rule matches your identity token.
```

It then retries the same in-memory assertion for ~3 minutes (every retry is a replay, so every retry fails) before giving up. Nothing in the message suggests replay.

## Asks

1. **Document the single-use property** in the WIF reference (validation rules / troubleshooting) and in the token-file guidance: one consumer per assertion; mint a separate assertion for any additional exchanger.
2. **Make the CLI hint replay-aware** — e.g. "…or the identity token was already exchanged (assertions are single-use)". Ideally the CLI re-reads the token file between retries, so a rotating file self-heals instead of replaying the same burned assertion.
3. (Nice to have) Surface `replay`/`assertion_reused` as a distinct deny reason where deny reasons are recorded.

## Environment

- `@anthropic-ai/claude-code` 2.1.241 (native binary), headless `-p` mode
- GitHub Actions self-hosted runners (Linux x64), GitHub OIDC issuer, discovery-mode JWKS
- Federation rule: exact-subject match, audience `https://api.anthropic.com`, single workspace, default token lifetime

コントリビューションガイド

このリポジトリのコントリビューションガイドは索引されていません

調査の方向性

Start with the WIF reference and token-file guidance, then inspect Claude Code's token-exchange retry and error-hint entry points. Reproduce the first successful exchange and subsequent 401 with the same assertion. Done should include documented single-use assertions and a replay-aware CLI path, with token-file re-reading considered between retries.

索引モデルが issue の本文から書いたものです。

評価

技術スタック
github-actions
領域
authentication, cli, documentation
issue の種類
バグ
難易度
4/5
見積もり時間
3〜5日
活発さ
活発
明瞭さ
おおむね明確
初心者へのやさしさ
52/100

新しい issue をメールで受け取る

初心者向けの GitHub issue を短くまとめたダイジェスト。