每次 GitHub Actions 工作流运行时,都可以从 GitHub 托管的颁发者 https://token.actions.githubusercontent.com 请求一个已签名的身份令牌。借助 "Workload Identity Federation"(工作负载身份联合),您的工作流可以用该令牌换取一个短期有效的 Anthropic 访问令牌,这样您的 CI 作业就可以调用 Claude API,而无需在代码仓库中存储 ANTHROPIC_API_KEY 密钥。
该令牌的 sub 声明编码了代码仓库和触发上下文信息。对于推送到分支的情况,其格式为 repo:<owner>/<repo>:ref:refs/heads/<branch>。拉取请求运行使用 repo:<owner>/<repo>:pull_request,而受环境门控的部署则使用 repo:<owner>/<repo>:environment:<name>。您的联合规则会根据此声明(以及其他声明,如 repository_owner 和 ref)进行匹配,以决定允许哪些工作流运行进行身份验证。
id-token: write 权限。GitHub 仅向明确请求身份令牌的作业颁发令牌。请在工作流或作业级别添加 id-token: write 权限:
permissions:
id-token: write
contents: read在作业内部,运行器会暴露两个环境变量:ACTIONS_ID_TOKEN_REQUEST_URL 和 ACTIONS_ID_TOKEN_REQUEST_TOKEN。使用请求令牌作为 Bearer 凭据调用请求 URL,并将您选择的受众(audience)作为查询参数传入,然后将返回的 "JSON Web Token"(JSON 网络令牌),即 JWT,写入文件:
- name: Fetch GitHub OIDC token
run: |
curl -sS -H "Authorization: Bearer $ACTIONS_ID_TOKEN_REQUEST_TOKEN" \
"$ACTIONS_ID_TOKEN_REQUEST_URL&audience=https://api.anthropic.com" \
| jq -r .value > /tmp/gha-jwt如果您更喜欢使用 JavaScript,actions/github-script 通过 core.getIDToken(audience) 提供了相同的功能:
- name: Fetch GitHub OIDC token
uses: actions/github-script@v8
with:
script: |
const fs = require('fs');
const token = await core.getIDToken('https://api.anthropic.com');
fs.writeFileSync('/tmp/gha-jwt', token);解码后的令牌包含描述工作流运行的声明。您的联合规则会根据这些声明进行匹配:
{
"iss": "https://token.actions.githubusercontent.com",
"sub": "repo:your-org/your-repo:ref:refs/heads/main",
"aud": "https://api.anthropic.com",
"repository": "your-org/your-repo",
"repository_owner": "your-org",
"ref": "refs/heads/main",
"sha": "abc123...",
"workflow": "CI",
"actor": "octocat",
"event_name": "push"
}有关 sub 格式的完整列表,请参阅 GitHub 的 OIDC subject 声明参考文档。
按照设置演练在 Claude Console 中注册联合颁发者、创建 Anthropic 服务账号并创建联合规则。请使用以下 GitHub Actions 特定的值。
联合颁发者: GitHub 公开发布其 OIDC 发现文档和 JWKS,因此请使用发现模式。当 GitHub 轮换密钥时,Anthropic 会自动刷新密钥。
{
"name": "github-actions",
"issuer_url": "https://token.actions.githubusercontent.com",
"jwks_source": "discovery"
}联合规则: 仅匹配您打算信任的工作流运行。有关如何安全地限定这些声明的范围,请参阅限制哪些工作流可以进行身份验证。
{
"name": "gha-main",
"issuer_id": "fdis_...",
"match": {
"subject_prefix": "repo:your-org/your-repo:ref:refs/heads/main",
"audience": "https://api.anthropic.com",
"claims": {
"repository_owner": "your-org"
}
},
"target": {
"type": "service_account",
"service_account_id": "svac_..."
},
"workspace_id": "wrkspc_...",
"oauth_scope": "workspace:developer",
"token_lifetime_seconds": 600
}请在工作负载允许的范围内尽可能具体。仅当规则必须匹配来自同一代码仓库的多种事件类型时,才将 subject_prefix 放宽为 repo:your-org/your-repo:*(并配合 claims.ref 约束使用),因为 sub 的末尾部分在 ref:...、environment:... 和 pull_request 事件之间会有所不同。
在作业上设置联合环境变量,然后正常调用 SDK。Anthropic() 会读取 ANTHROPIC_IDENTITY_TOKEN_FILE,在第一次请求时交换 JWT,并在访问令牌过期前自动刷新。
import anthropic
# 从作业环境中读取 ANTHROPIC_FEDERATION_RULE_ID、ANTHROPIC_ORGANIZATION_ID、
# ANTHROPIC_SERVICE_ACCOUNT_ID、ANTHROPIC_WORKSPACE_ID 和 ANTHROPIC_IDENTITY_TOKEN_FILE
# 环境变量。
client = anthropic.Anthropic()
message = client.messages.create(
model="claude-sonnet-4-6",
max_tokens=1024,
messages=[{"role": "user", "content": "Hello, Claude"}],
)
print(message.content[0].text)每个 GitHub 颁发的身份令牌在颁发后大约五分钟过期。令牌请求端点(ACTIONS_ID_TOKEN_REQUEST_URL)在整个作业期间保持有效,因此您可以随时获取新的令牌。SDK 在首次使用时交换令牌,并缓存生成的 Anthropic 访问令牌。对于运行时间超过 Anthropic 令牌有效期的作业,SDK 会在每次刷新时重新读取 ANTHROPIC_IDENTITY_TOKEN_FILE,因此请定期重新运行获取步骤(或将其包装在后台循环中)以保持文件内容最新。或者,您也可以向 SDK 传递一个令牌提供者回调函数,直接调用 ACTIONS_ID_TOKEN_REQUEST_URL,而不使用文件路径。
成功的交换会返回一个以 sk-ant-oat01- 开头的 access_token 以及一个以秒为单位的 expires_in 值。如果遇到 400 invalid_grant 错误,请参阅排查交换失败问题;GitHub Actions 端最常见的原因是 sub 声明格式不匹配(其末尾部分在 ref:...、environment:... 和 pull_request 事件之间会有所不同)。
仅使用 repo:your-org/* 作为 subject_prefix 会匹配您组织中的每个代码仓库,并且如果没有 ref 约束,它还会匹配从 fork 触发的 pull_request 运行。任何能够针对匹配的代码仓库发起拉取请求的人都可能获得联合的 Anthropic 令牌。
将规则的 match 块锁定到适合您用例的最小范围:
subject_prefix: "repo:your-org/your-repo:*",这样组织中的其他代码仓库就不会匹配。claims 下添加 "ref": "refs/heads/main"(或您的发布分支),这样拉取请求运行和功能分支就不会匹配。claims 下添加 "repository_owner": "your-org",作为针对 sub 解析边缘情况的纵深防御检查。subject_prefix: "repo:your-org/your-repo:environment:production",并在 GitHub 中为该环境设置必需的审核人员门控。Was this page helpful?