Skip to content

Commit 3471a9f

Browse files
authored
Use workload identity federation for Claude auth in CI workflows (#984)
## What Switches this repository's Claude automation workflows from the static `ANTHROPIC_API_KEY` secret to [Workload Identity Federation](https://platform.claude.com/docs/en/manage-claude/workload-identity-federation): the workflow's GitHub OIDC token is exchanged for a short-lived Claude API access token at runtime, so no long-lived API key needs to be stored in the repository. | Workflow | Change | | --- | --- | | `claude.yml` | `anthropic_api_key` → federation inputs | | `claude-code-review.yml` | `anthropic_api_key` → federation inputs | | `claude-issue-triage.yml` | `anthropic_api_key` → federation inputs, plus `id-token: write` (the other two already request it) | | `build-and-publish.yml` | `anthropic_api_key` → federation inputs in the changelog step, plus `id-token: write` on the `publish` job | | `auto-release.yml`, `publish.yml` | grant `id-token: write` to the jobs that call the `build-and-publish.yml` reusable workflow (a called workflow can only use permissions its caller grants) | This uses the federation support shipped in [anthropics/claude-code-action](https://github.com/anthropics/claude-code-action) (`docs/setup.md#workload-identity-federation`, anthropics/claude-code-action#1338). ## How it activates The federation rule, organization, service account, and workspace IDs are read from repository **variables** (`vars.ANTHROPIC_FEDERATION_RULE_ID`, `vars.ANTHROPIC_ORGANIZATION_ID`, `vars.ANTHROPIC_SERVICE_ACCOUNT_ID`, `vars.ANTHROPIC_WORKSPACE_ID`). These are identifiers, not credentials. Until a repo admin sets them, the action fails fast at env validation with a clear "authentication required" message — so this PR is safe to merge ahead of that, and switching over is a settings change rather than another PR. The `ANTHROPIC_API_KEY` secret is intentionally left in place until the federated path has produced green runs; rollback is reverting this PR. ## Behavior notes - `claude-code-review.yml` runs on `pull_request`. Fork PRs don't receive `id-token: write` (GitHub withholds it the same way it withholds secrets), so reviews continue to run only for same-repo PRs — identical to today's behavior with the secret. - `test.yml` is deliberately **not** migrated here: it passes `ANTHROPIC_API_KEY` directly to pytest and to `docker run` for the SDK under test. Migrating that path means mounting an identity token into the container rather than swapping a workflow input, so it needs its own treatment.
1 parent 095a8b3 commit 3471a9f

6 files changed

Lines changed: 36 additions & 4 deletions

File tree

.github/workflows/auto-release.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,8 @@ jobs:
4747
needs: check-trigger
4848
permissions:
4949
contents: write
50+
# Required to mint the OIDC token exchanged for a Claude API access token (Workload Identity Federation)
51+
id-token: write
5052
uses: ./.github/workflows/build-and-publish.yml
5153
with:
5254
version: ${{ needs.check-trigger.outputs.version }}

.github/workflows/build-and-publish.yml

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,8 @@ jobs:
4646
environment: production
4747
permissions:
4848
contents: write
49+
# Required to mint the OIDC token exchanged for a Claude API access token (Workload Identity Federation)
50+
id-token: write
4951
env:
5052
VERSION: ${{ inputs.version }}
5153
steps:
@@ -186,7 +188,13 @@ jobs:
186188
uses: anthropics/claude-code-action@v1
187189
with:
188190
prompt: "/generate-changelog new version: ${{ env.VERSION }}, old version: ${{ inputs.previous_tag }}"
189-
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
191+
# Authenticate to the Claude API via Workload Identity Federation
192+
# (the workflow's OIDC token is exchanged for a short-lived access
193+
# token) instead of a static API key.
194+
anthropic_federation_rule_id: ${{ vars.ANTHROPIC_FEDERATION_RULE_ID }}
195+
anthropic_organization_id: ${{ vars.ANTHROPIC_ORGANIZATION_ID }}
196+
anthropic_service_account_id: ${{ vars.ANTHROPIC_SERVICE_ACCOUNT_ID }}
197+
anthropic_workspace_id: ${{ vars.ANTHROPIC_WORKSPACE_ID }}
190198
github_token: ${{ secrets.GITHUB_TOKEN }}
191199
claude_args: |
192200
--model claude-opus-4-6

.github/workflows/claude-code-review.yml

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,13 @@ jobs:
3131
id: claude-review
3232
uses: anthropics/claude-code-action@v1
3333
with:
34-
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
34+
# Authenticate to the Claude API via Workload Identity Federation
35+
# (the workflow's OIDC token is exchanged for a short-lived access
36+
# token) instead of a static API key.
37+
anthropic_federation_rule_id: ${{ vars.ANTHROPIC_FEDERATION_RULE_ID }}
38+
anthropic_organization_id: ${{ vars.ANTHROPIC_ORGANIZATION_ID }}
39+
anthropic_service_account_id: ${{ vars.ANTHROPIC_SERVICE_ACCOUNT_ID }}
40+
anthropic_workspace_id: ${{ vars.ANTHROPIC_WORKSPACE_ID }}
3541

3642
claude_args: --model claude-opus-4-6
3743

.github/workflows/claude-issue-triage.yml

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ jobs:
1111
permissions:
1212
contents: read
1313
issues: write
14+
# Required to mint the OIDC token exchanged for a Claude API access token (Workload Identity Federation)
15+
id-token: write
1416

1517
steps:
1618
- name: Checkout repository
@@ -24,6 +26,12 @@ jobs:
2426
CLAUDE_CODE_SCRIPT_CAPS: '{"edit-issue-labels.sh":2}'
2527
with:
2628
prompt: "/label-issue REPO: ${{ github.repository }} ISSUE_NUMBER: ${{ github.event.issue.number }}"
27-
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
29+
# Authenticate to the Claude API via Workload Identity Federation
30+
# (the workflow's OIDC token is exchanged for a short-lived access
31+
# token) instead of a static API key.
32+
anthropic_federation_rule_id: ${{ vars.ANTHROPIC_FEDERATION_RULE_ID }}
33+
anthropic_organization_id: ${{ vars.ANTHROPIC_ORGANIZATION_ID }}
34+
anthropic_service_account_id: ${{ vars.ANTHROPIC_SERVICE_ACCOUNT_ID }}
35+
anthropic_workspace_id: ${{ vars.ANTHROPIC_WORKSPACE_ID }}
2836
allowed_non_write_users: "*" # Required for issue triage workflow, if users without repo write access create issues
2937
github_token: ${{ secrets.GITHUB_TOKEN }}

.github/workflows/claude.yml

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,13 @@ jobs:
4343
id: claude
4444
uses: anthropics/claude-code-action@v1
4545
with:
46-
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
46+
# Authenticate to the Claude API via Workload Identity Federation
47+
# (the workflow's OIDC token is exchanged for a short-lived access
48+
# token) instead of a static API key.
49+
anthropic_federation_rule_id: ${{ vars.ANTHROPIC_FEDERATION_RULE_ID }}
50+
anthropic_organization_id: ${{ vars.ANTHROPIC_ORGANIZATION_ID }}
51+
anthropic_service_account_id: ${{ vars.ANTHROPIC_SERVICE_ACCOUNT_ID }}
52+
anthropic_workspace_id: ${{ vars.ANTHROPIC_WORKSPACE_ID }}
4753

4854
# Optional: Customize the trigger phrase (default: @claude)
4955
# trigger_phrase: "/claude"

.github/workflows/publish.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,8 @@ jobs:
7676
needs: [test, lint, get-previous-tag]
7777
permissions:
7878
contents: write
79+
# Required to mint the OIDC token exchanged for a Claude API access token (Workload Identity Federation)
80+
id-token: write
7981
uses: ./.github/workflows/build-and-publish.yml
8082
with:
8183
version: ${{ github.event.inputs.version }}

0 commit comments

Comments
 (0)