모든 GitHub Actions 워크플로 실행은 GitHub의 호스팅된 발급자인 https://token.actions.githubusercontent.com에서 서명된 ID 토큰을 요청할 수 있습니다. Workload Identity Federation을 사용하면 워크플로가 해당 토큰을 수명이 짧은 Anthropic 액세스 토큰으로 교환하므로, 저장소에 ANTHROPIC_API_KEY 시크릿을 저장하지 않고도 CI 작업에서 Claude API를 호출할 수 있습니다.
토큰의 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 저장소.GitHub는 명시적으로 요청한 작업에만 ID 토큰을 발급합니다. 워크플로 또는 작업 수준에서 id-token: write 권한을 추가하세요:
permissions:
id-token: write
contents: read작업 내부에서 러너는 두 개의 환경 변수를 노출합니다: ACTIONS_ID_TOKEN_REQUEST_URL과 ACTIONS_ID_TOKEN_REQUEST_TOKEN. 요청 토큰을 bearer 자격 증명으로 사용하고 선택한 audience를 쿼리 매개변수로 하여 요청 URL을 호출한 다음, 반환된 "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-jwtJavaScript를 선호하는 경우, 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에서 Settings → Workload identity를 열고 Connect workload를 클릭한 다음 GitHub Actions 타일을 선택하세요. 마법사가 발급자 등록, 서비스 계정 생성, 페더레이션 규칙 생성 과정을 안내합니다.
마법사가 이러한 리소스를 생성합니다. 마법사에 입력하든 Admin API로 전송하든 다음 값을 사용하세요:
페더레이션 발급자: GitHub는 OIDC 디스커버리 문서와 JWKS를 공개적으로 게시하므로 디스커버리 모드를 사용하세요. GitHub가 키를 교체하면 Anthropic이 자동으로 키를 새로 고칩니다.
{
"name": "github-actions",
"issuer_url": "https://token.actions.githubusercontent.com",
"jwks": { "type": "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
}워크로드가 허용하는 한 최대한 구체적으로 지정하세요. sub의 마지막 세그먼트는 ref:..., environment:..., pull_request 이벤트에 따라 달라지므로, 규칙이 동일한 저장소의 여러 이벤트 유형과 매칭되어야 하는 경우에만 subject_prefix를 repo:your-org/your-repo:*로 완화하세요(claims.ref 제약 조건과 함께 사용).
작업에 페더레이션 환경 변수를 설정하고 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-opus-5",
max_tokens=1024,
messages=[{"role": "user", "content": "Hello, Claude"}],
)
print(next(block.text for block in message.content if block.type == "text"))GitHub가 발급한 각 ID 토큰은 발급 후 약 5분이 지나면 만료됩니다. 토큰 요청 엔드포인트(ACTIONS_ID_TOKEN_REQUEST_URL)는 작업 전체 기간 동안 유효하므로 언제든지 새 토큰을 가져올 수 있습니다. SDK는 첫 사용 시 토큰을 교환하고 결과로 얻은 Anthropic 액세스 토큰을 캐시합니다. Anthropic 토큰의 수명보다 오래 실행되는 작업의 경우, SDK는 새로 고칠 때마다 ANTHROPIC_IDENTITY_TOKEN_FILE을 다시 읽으므로, 파일을 최신 상태로 유지하려면 가져오기 단계를 주기적으로 다시 실행하세요(또는 백그라운드 루프로 감싸세요). 또는 파일 경로를 사용하는 대신 ACTIONS_ID_TOKEN_REQUEST_URL을 직접 호출하는 토큰 제공자 콜백을 SDK에 전달할 수도 있습니다.
교환이 성공하면 sk-ant-oat01-로 시작하는 access_token과 초 단위의 expires_in 값이 반환됩니다. 400 invalid_grant가 발생하면 실패한 교환 문제 해결을 참조하세요. GitHub Actions 측에서 가장 흔한 원인은 sub 클레임 형식이 일치하지 않는 것입니다(마지막 세그먼트는 ref:..., environment:..., pull_request 이벤트에 따라 달라집니다).
repo:your-org/*만으로 된 subject_prefix는 조직의 모든 저장소와 매칭되며, ref 제약 조건이 없으면 포크에서 트리거된 pull_request 실행과도 매칭됩니다. 매칭되는 저장소에 풀 리퀘스트를 열 수 있는 사람은 누구나 페더레이션된 Anthropic 토큰을 얻을 수 있습니다.
규칙의 match 블록을 사용 사례에 맞는 가장 좁은 범위로 제한하세요:
subject_prefix: "repo:your-org/your-repo:*"를 사용하여 조직의 다른 저장소가 매칭되지 않도록 하세요.claims 아래에 "ref": "refs/heads/main"(또는 릴리스 브랜치)을 추가하여 풀 리퀘스트 실행과 기능 브랜치가 매칭되지 않도록 하세요.sub 파싱의 예외 상황에 대한 심층 방어 검사로 claims 아래에 "repository_owner": "your-org"를 추가하세요.subject_prefix: "repo:your-org/your-repo:environment:production"과 매칭하고 GitHub에서 필수 검토자로 해당 환경을 보호하세요.Was this page helpful?