---
title: SPIFFE와 함께 WIF 사용하기
url: https://platform.claude.com/docs/ko/manage-claude/wif-providers/spiffe
description: SPIRE 또는 기타 SPIFFE 호환 발급자의 JWT-SVID를 사용하여 SPIFFE 워크로드를 Claude API에 인증합니다.
---

[SPIFFE](https://spiffe.io/)는 워크로드에 ID를 발급하기 위한 CNCF 표준입니다. [SPIRE](https://spiffe.io/docs/latest/spire-about/)는 그 오픈소스 참조 구현이며, 여러 상용 제품도 SPIFFE 호환 ID를 발급합니다. Anthropic은 OIDC 호환 JWT-SVID를 발급하는 모든 SPIFFE 구현과 페더레이션합니다. 현재 구현 목록은 SPIFFE 프로젝트 사이트의 [Commercial software that implements SPIFFE](https://spiffe.io/docs/latest/spiffe-about/overview/#commercial-software-that-implements-spiffe)를 참조하세요.

페더레이션은 공개 HTTPS URL의 OIDC 디스커버리 문서를 통해(`discovery` 모드, [URL 제약 조건](https://platform.claude.com/docs/ko/manage-claude/wif-reference#url-fields) 적용) 또는 JWKS를 직접 등록하는 방식(`inline` 모드)으로 동작합니다.

JWT-SVID 사양은 `sub`를 워크로드의 SPIFFE ID로 정의하고, SPIFFE Workload API는 호출자가 가져오기 시점에 `aud`를 제공하도록 요구하므로, 이 클레임들은 모든 구현에서 동일합니다. Anthropic은 추가로 `iss`와 `iat`를 요구하는데, 둘 다 JWT-SVID 사양에서 필수로 지정하지 않으므로 구현이 두 값을 모두 채우도록 구성하세요(SPIRE에서 `iss`는 `jwt_issuer` 서버 설정이며 `iat`는 자동으로 설정됩니다). 이것들이 갖춰지면 이 가이드의 [Anthropic 구성](https://platform.claude.com/docs/ko/manage-claude/wif-providers/spiffe#configure-anthropic), [토큰 획득 및 사용](https://platform.claude.com/docs/ko/manage-claude/wif-providers/spiffe#acquire-and-use-the-token), [규칙 범위 지정](https://platform.claude.com/docs/ko/manage-claude/wif-providers/spiffe#scope-your-rule) 섹션은 모든 SPIFFE 구현에 적용됩니다.

SPIFFE는 모든 워크로드에 `spiffe://<trust-domain>/<path>` 형식의 안정적인 ID URI를 할당하며, SPIRE는 Workload API를 통해 요청 시 해당 ID를 JWT-SVID로 발급합니다. JWT-SVID는 `sub` 클레임이 워크로드의 SPIFFE ID이고 `aud` 클레임이 가져오기 시점에 워크로드가 제공하는 일반적인 서명된 JWT입니다.

SPIRE 트러스트 도메인에서 표준 OIDC로 연결하는 다리는 [SPIRE OIDC Discovery Provider](https://github.com/spiffe/spire/blob/main/support/oidc-discovery-provider/README.md)입니다. 이는 트러스트 도메인의 JWT 서명 키에 대한 `/.well-known/openid-configuration`과 JWKS 엔드포인트를 게시하는 독립형 헬퍼입니다. 디스커버리 프로바이더가 실행 중이면 JWT-SVID는 다른 OIDC 토큰과 마찬가지로 검증됩니다. 디스커버리 URL을 페더레이션 발급자로 등록하고, 워크로드의 SPIFFE ID와 일치하는 페더레이션 규칙을 작성하고, 워크로드가 자신의 JWT-SVID를 Anthropic의 토큰 교환 엔드포인트에 제시하도록 하면 됩니다.

이 페이지의 예제는 SPIRE를 사용하며 SPIRE Agent가 실행되는 모든 곳(Kubernetes 파드, 가상 머신, 베어메탈 호스트)에 적용됩니다.

<Note>
  Kubernetes 클러스터에서 SPIRE를 실행하지 않고 대신 클러스터의 네이티브 프로젝티드 서비스 어카운트 토큰으로 인증하려면 [Kubernetes와 함께 WIF 사용하기](https://platform.claude.com/docs/ko/manage-claude/wif-providers/kubernetes)를 참조하세요.
</Note>

## 사전 요구 사항

* [WIF 개념](https://platform.claude.com/docs/ko/manage-claude/workload-identity-federation#concepts)에 대한 이해: 서비스 어카운트, 페더레이션 발급자, 페더레이션 규칙.
* 워크로드 ID가 발급된 SPIFFE 배포(이 페이지의 예제는 SPIRE Server와 Agent를 사용합니다) 및 Claude API를 호출해야 하는 워크로드에 대한 등록 항목.
* 공개적으로 접근 가능한 HTTPS 엔드포인트로 실행 중인 트러스트 도메인의 OIDC 디스커버리 엔드포인트(SPIRE에서는 [OIDC Discovery Provider](https://github.com/spiffe/spire/blob/main/support/oidc-discovery-provider/README.md)), 또는 `inline` 등록을 위해 내보낸 JWKS.
* JWT-SVID의 `iss` 클레임을 페더레이션 발급자의 `issuer_url`로 등록할 값으로 설정하도록 구성된 SPIFFE 발급자. `discovery` 모드의 경우 이는 디스커버리 엔드포인트의 공개 URL입니다(SPIRE에서는 `jwt_issuer` 서버 설정).
* 워크로드에서 사용 가능한 JWT-SVID. WIF는 JWT-SVID만 허용하며 X.509-SVID는 허용하지 않습니다.
* Anthropic 조직의 Claude Console에서 서비스 어카운트, 페더레이션 발급자, 페더레이션 규칙을 생성할 수 있는 권한.

JWT-SVID를 가져올 때 요청할 audience 값은 항상 `https://api.anthropic.com`입니다. 이 값을 spiffe-helper의 `jwt_audience`, Workload API `FetchJWTSVID` 호출, 페더레이션 규칙의 `audience` 매처에 사용하세요.

## SPIRE 구성

이 섹션의 지침은 SPIRE에 특화되어 있습니다. 다른 SPIFFE 발급자를 사용하는 경우 해당 문서에 따라 OIDC 디스커버리 엔드포인트와 JWT-SVID 조회를 구성한 다음 [Anthropic 구성](https://platform.claude.com/docs/ko/manage-claude/wif-providers/spiffe#configure-anthropic)에서 계속하세요.

이미 OIDC Discovery Provider와 함께 SPIRE를 실행 중이라면, Anthropic과 페더레이션하기 위해 SPIRE 측에서 세 가지가 필요합니다. 디스커버리 URL과 일치하는 `jwt_issuer`, Claude API를 호출할 워크로드에 대한 등록 항목, 그리고 해당 워크로드가 Anthropic audience로 JWT-SVID를 가져올 수 있는 방법입니다. 다음 하위 섹션에서 각각을 안내합니다. 구성 스니펫은 전체 SPIRE 배포 구성이 아니라 Anthropic 페더레이션과 관련된 설정만 보여줍니다.

<Tip>
  SPIRE를 처음 설정하시나요? [SPIRE 빠른 시작](https://spiffe.io/docs/latest/try/)에 따라 SPIRE Server와 Agent를 배포한 다음, [OIDC Discovery Provider](https://github.com/spiffe/spire/blob/main/support/oidc-discovery-provider/README.md)를 SPIRE Server와 함께 별도의 서비스로 추가하세요. 디스커버리 모드 페더레이션은 프로바이더가 배포되어 공개적으로 접근 가능한지에 의존합니다. 프로바이더는 기본 SPIRE 설치에 포함되어 있지 않습니다.
</Tip>

### JWT 발급자 확인

Anthropic은 JWT-SVID의 `iss` 클레임을 등록된 페더레이션 발급자와 대조하고 해당 발급자의 디스커버리 문서에서 JWKS를 가져와 JWT-SVID를 검증합니다. 두 개의 SPIRE 설정이 동일한 URL에 대해 일치해야 합니다. SPIRE Server의 `jwt_issuer`(발급되는 모든 JWT-SVID의 `iss` 클레임이 됨)와 OIDC Discovery Provider의 `domains` 목록(디스커버리 문서와 JWKS가 제공되는 호스트를 결정함)입니다. 이 공유 URL이 Anthropic에 등록하는 값입니다.

트러스트 도메인과 발급자 URL은 독립적입니다. 트러스트 도메인(`spiffe://prod.example.com`)은 `sub` 클레임의 범위를 지정합니다. 발급자 URL(`https://oidc-discovery.prod.example.com`)은 Anthropic이 서명 키를 가져오는 곳입니다. 둘이 호스트 이름을 공유할 필요는 없습니다.

SPIRE Server 구성에 `jwt_issuer`가 설정되어 있고 디스커버리 프로바이더의 공개 URL을 가리키는지 확인하세요. 다음 예제는 기본 JWT-SVID 수명도 보여줍니다. SPIRE의 내장 기본값은 5분으로, 지속적인 로테이션이 필요할 만큼 짧습니다([spiffe-helper 실행](https://platform.claude.com/docs/ko/manage-claude/wif-providers/spiffe#run-spiffe-helper) 참조). Anthropic의 토큰 교환 엔드포인트는 수명이 페더레이션 발급자에 구성된 최대값(기본 1시간)을 초과하는 모든 ID 토큰을 거부합니다([검증 규칙](https://platform.claude.com/docs/ko/manage-claude/wif-reference#validation-rules) 참조). 이 검사는 SPIRE뿐만 아니라 모든 SPIFFE 구현에 적용되므로 `default_jwt_svid_ttl`(또는 항목별 재정의)을 해당 최대값 이하로 유지하세요.

```text server.conf
server {
    trust_domain         = "prod.example.com"
    jwt_issuer           = "https://oidc-discovery.prod.example.com"
    default_jwt_svid_ttl = "5m"
    # ...
}
```

OIDC Discovery Provider 구성에서 동일한 호스트 이름이 `domains` 아래에 나타나야 하며, 프로바이더는 SPIRE Server의 API 소켓에 접근할 수 있어야 합니다. 프로바이더는 HTTPS를 통해 디스커버리 문서와 JWKS를 제공합니다. 내장 ACME 지원으로 TLS를 종료하거나, 이를 수행하는 로드 밸런서를 앞에 두세요.

```text oidc-discovery-provider.conf
domains = ["oidc-discovery.prod.example.com"]

server_api {
    address = "unix:///run/spire/sockets/private/api.sock"
}

acme {
    email        = "platform@example.com"
    tos_accepted = true
}
```

<Note>
  예제는 디스커버리 프로바이더를 SPIRE Server의 권한 있는 API 소켓에 연결하는 `server_api`를 사용합니다. 프로바이더는 대신 SPIRE Agent의 Workload API를 통해 번들을 얻는 `workload_api` 블록(`socket_path` 및 `trust_domain` 포함)도 허용합니다. 디스커버리 프로바이더가 Server API에 접근해서는 안 되거나 Server에 도달할 수 없는 노드에서 실행되는 경우 이를 사용하세요.
</Note>

### 워크로드 등록

Claude API를 호출하는 각 워크로드에는 런타임 셀렉터를 SPIFFE ID에 매핑하는 SPIRE 등록 항목이 필요합니다. 워크로드가 이미 등록되어 있다면 페더레이션 규칙의 `subject_prefix`에 사용할 SPIFFE ID를 기록해 두세요. 등록되어 있지 않다면 등록하세요. Kubernetes 파드의 경우 셀렉터는 일반적으로 네임스페이스와 Kubernetes 서비스 어카운트입니다.

```bash CLI
# NODE_UID를 노드의 UID로 바꾸세요:
#   kubectl get node <node-name> -o jsonpath='{.metadata.uid}'
spire-server entry create \
    -spiffeID spiffe://prod.example.com/ns/inference/sa/worker \
    -parentID spiffe://prod.example.com/spire/agent/k8s_psat/prod-cluster/NODE_UID \
    -selector k8s:ns:inference \
    -selector k8s:sa:worker
```

<Note>
  표시된 `parentID`는 단일 노드의 자동 생성된 에이전트 ID입니다. 클러스터 전체 등록의 경우 [SPIRE Kubernetes 빠른 시작](https://spiffe.io/docs/latest/try/getting-started-k8s/)에서 하는 것처럼 항목의 부모를 [노드 별칭](https://spiffe.io/docs/latest/deploying/registering/#mapping-workloads-to-multiple-nodes)으로 지정하여 모든 노드의 워크로드와 일치하도록 하세요.
</Note>

Kubernetes 외부의 워크로드는 `unix:uid:1000`과 같은 호스트 수준 셀렉터를 사용합니다(`unix:path`도 사용 가능하지만 에이전트의 unix 워크로드 어테스터 구성에서 `discover_workload_path = true`가 필요합니다). [spire-controller-manager](https://github.com/spiffe/spire-controller-manager)를 실행하는 클러스터는 `spire-server entry create`를 직접 호출하는 대신 `ClusterSPIFFEID` 커스텀 리소스로 항목을 선언할 수 있습니다.

### spiffe-helper 실행

[spiffe-helper](https://github.com/spiffe/spiffe-helper)는 SPIRE Agent 소켓에 연결하여 지정된 audience에 대한 JWT-SVID를 가져오고, 파일에 기록하며, 만료 전에 다시 가져오는 사이드카 유틸리티입니다. 헬퍼는 기본적으로 데몬 모드로 실행됩니다. 다음 예제는 `daemon_mode = true`를 명시적으로 설정합니다.

```text helper.conf
agent_address = "/run/spire/sockets/agent.sock"
# The JWT-SVID file is written under cert_dir
cert_dir      = "/var/run/secrets/anthropic.com"
daemon_mode   = true

jwt_svids = [{
    jwt_audience       = "https://api.anthropic.com"
    jwt_svid_file_name = "token"
}]
```

Kubernetes에서는 베어러 SVID가 노드의 디스크에 절대 저장되지 않도록 애플리케이션 컨테이너와 메모리 기반 `emptyDir` 볼륨(`medium: Memory`)을 공유하는 사이드카 컨테이너로 spiffe-helper를 실행하세요. 호스트의 SPIRE Agent 소켓을 사이드카에 마운트하고, 공유 볼륨을 두 컨테이너 모두에서 `/var/run/secrets/anthropic.com`에 마운트하며, 애플리케이션 컨테이너에 `ANTHROPIC_IDENTITY_TOKEN_FILE=/var/run/secrets/anthropic.com/token`을 설정하세요. VM과 베어메탈에서는 spiffe-helper를 워크로드와 함께 시스템 서비스로 실행하고 둘 다 공유 디렉터리를 가리키도록 하세요.

## Anthropic 구성

Claude Console에서 [Workload identity](https://platform.claude.com/settings/workload-identity-federation) 페이지(`platform.claude.com/settings/workload-identity-federation`)로 이동하여 **Connect workload**를 클릭하고 **Custom OIDC**를 선택하세요. 마법사가 발급자 등록, 서비스 어카운트 생성, 페더레이션 규칙 생성 과정을 안내합니다.

마법사가 이러한 리소스를 대신 생성해 줍니다. 마법사에 입력하든 [Admin API](https://platform.claude.com/docs/ko/manage-claude/wif-admin-api)로 전송하든 다음 값을 사용하세요:

**페더레이션 발급자:** OIDC Discovery Provider의 공개 URL을 `discovery` 모드로 등록하세요. Anthropic은 이 URL에서 `/.well-known/openid-configuration`을 가져오고 반환된 `jwks_uri`를 따라 트러스트 도메인의 서명 키를 조회합니다.

```json
{
  "name": "spire-prod",
  "issuer_url": "https://oidc-discovery.prod.example.com",
  "jwks": { "type": "discovery" }
}
```

디스커버리 프로바이더가 공용 인터넷에서 접근할 수 없는 경우, JWKS를 직접 가져와(`curl https://oidc-discovery.prod.example.com/keys`) 반환된 `keys` 배열의 내용을 사용하여 `"jwks": {"type": "inline", "keys": [...]}`로 발급자를 등록하세요. `inline` 모드에서 `issuer_url`은 JWT-SVID의 `iss` 클레임과만 비교됩니다. Anthropic은 해당 URL에 접근을 시도하지 않습니다.

<Warning>
  SPIRE는 JWT 서명 키를 자주 로테이션하며, 기본적으로 CA와 동일한 주기(`ca_ttl`, 24시간)로 로테이션합니다. 디스커버리 URL 대신 인라인 JWKS로 발급자를 등록하는 경우 SPIRE가 로테이션할 때마다 JWKS를 업데이트해야 합니다. 워크로드가 새 키를 제시하기 시작하기 전에 새 키를 추가하고, 해당 키로 서명된 토큰이 만료되면 **대체된 키를 제거**하세요. 인라인 JWKS에 남겨진 오래된 키는 무기한 신뢰됩니다.
</Warning>

공개 디스커버리 엔드포인트를 노출하지 않고 JWKS 업데이트를 자동화하려면, `format = "jwks"`로 SPIRE Server [BundlePublisher](https://spiffe.io/docs/latest/deploying/spire_server/#built-in-plugins) 플러그인(`aws_s3`, `gcp_cloudstorage` 또는 `k8s_configmap`)을 구성하여 로테이션할 때마다 JWT 서명 키를 외부 스토리지로 푸시한 다음, [Admin API](https://platform.claude.com/docs/ko/manage-claude/wif-admin-api#federation-issuers)를 통해 발급자의 인라인 키를 업데이트하세요.

**페더레이션 규칙:** JWT-SVID의 `sub`(SPIFFE ID)와 spiffe-helper가 요청하도록 구성한 `aud`를 매칭하세요. SPIFFE ID는 URI 문자열이며 `subject_prefix`는 이를 불투명한 텍스트로 매칭하므로, 정확한 값이나 끝에 `*`가 붙은 접두사 매칭 모두 사용할 수 있습니다. 더 복잡한 패턴에는 CEL `condition`을 사용하세요.

```json
{
  "name": "spire-inference-worker",
  "issuer_id": "fdis_...",
  "match": {
    "subject_prefix": "spiffe://prod.example.com/ns/inference/sa/worker",
    "audience": "https://api.anthropic.com"
  },
  "target": {
    "type": "service_account",
    "service_account_id": "svac_..."
  },
  "workspace_id": "wrkspc_...",
  "oauth_scope": "workspace:developer",
  "token_lifetime_seconds": 600
}
```

`token_lifetime_seconds`는 JWT-SVID가 아니라 교환이 반환하는 Anthropic 액세스 토큰의 수명입니다. SDK는 액세스 토큰을 자동으로 갱신합니다.

워크로드가 허용하는 한 최대한 구체적으로 지정하세요. 해당 경로 아래에 등록된 모든 워크로드가 동일한 Anthropic 서비스 어카운트에 매핑되어야 하는 경우에만 `subject_prefix`를 `spiffe://prod.example.com/ns/inference/*`로 완화하세요. 규칙의 `fdrl_...` ID를 워크로드의 `ANTHROPIC_FEDERATION_RULE_ID` 환경 변수에 추가하세요.

## 토큰 획득 및 사용

Claude SDK는 spiffe-helper가 유지 관리하는 파일에서 JWT-SVID를 읽거나, Java를 제외하고는 토큰 제공자를 통해 SPIFFE Workload API를 직접 호출할 수 있습니다. 파일 경로 방식은 가장 간단한 통합 방법이며 모든 SDK 언어에서 작동합니다. 토큰 제공자 방식은 사이드카가 필요 없지만 애플리케이션 언어로 된 SPIFFE Workload API 클라이언트가 필요합니다.

<Tabs>
  <Tab title="spiffe-helper를 사용한 파일 기반">
    spiffe-helper가 `/var/run/secrets/anthropic.com/token`에 새로운 JWT-SVID를 기록하는 상태에서, `ANTHROPIC_IDENTITY_TOKEN_FILE`을 해당 경로로 설정하고 `ANTHROPIC_FEDERATION_RULE_ID`, `ANTHROPIC_ORGANIZATION_ID`, `ANTHROPIC_SERVICE_ACCOUNT_ID`, `ANTHROPIC_WORKSPACE_ID`도 함께 설정하세요. SDK는 토큰 교환 시마다 파일을 읽으므로 항상 가장 최근에 로테이션된 SVID를 사용하며, Anthropic 액세스 토큰이 만료되기 전에 자동으로 갱신합니다. 각 값의 출처는 [환경 변수](https://platform.claude.com/docs/ko/manage-claude/wif-reference#environment-variables)를 참조하세요.

    <CodeGroup>
      ```bash cURL
      JWT=$(cat "$ANTHROPIC_IDENTITY_TOKEN_FILE")

      ACCESS_TOKEN=$(curl -sS https://api.anthropic.com/v1/oauth/token \
        -H "content-type: application/json" \
        --data @- <<JSON | jq -r .access_token
      {
        "grant_type": "urn:ietf:params:oauth:grant-type:jwt-bearer",
        "assertion": "$JWT",
        "federation_rule_id": "$ANTHROPIC_FEDERATION_RULE_ID",
        "organization_id": "$ANTHROPIC_ORGANIZATION_ID",
        "service_account_id": "$ANTHROPIC_SERVICE_ACCOUNT_ID",
        "workspace_id": "$ANTHROPIC_WORKSPACE_ID"
      }
      JSON
      )

      curl https://api.anthropic.com/v1/messages \
        -H "authorization: Bearer $ACCESS_TOKEN" \
        -H "anthropic-version: 2023-06-01" \
        -H "content-type: application/json" \
        -d '{
          "model": "claude-opus-5-5",
          "max_tokens": 1024,
          "messages": [{"role": "user", "content": "Hello, Claude"}]
        }' | jq -r '.content[] | select(.type == "text") | .text'
      ```

      ```bash CLI
      # spiffe-helper가 기록한 JWT-SVID를 읽습니다(기록 위치:
      # ANTHROPIC_IDENTITY_TOKEN_FILE). 또한 ANTHROPIC_FEDERATION_RULE_ID,
      # ANTHROPIC_ORGANIZATION_ID, ANTHROPIC_SERVICE_ACCOUNT_ID, ANTHROPIC_WORKSPACE_ID도 읽습니다.
      ant messages create \
        --model claude-opus-5-5 \
        --max-tokens 1024 \
        --message '{role: user, content: "Hello, Claude"}'
      ```

      ```python Python
      import anthropic

      # spiffe-helper가 기록한 JWT-SVID를 읽습니다. 기록 위치는
      # ANTHROPIC_IDENTITY_TOKEN_FILE이며, 추가로 ANTHROPIC_FEDERATION_RULE_ID,
      # ANTHROPIC_ORGANIZATION_ID, ANTHROPIC_SERVICE_ACCOUNT_ID, ANTHROPIC_WORKSPACE_ID도 읽습니다.
      client = anthropic.Anthropic()

      message = client.messages.create(
          model="claude-opus-5-5",
          max_tokens=1024,
          messages=[{"role": "user", "content": "Hello, Claude"}],
      )
      print(next(block.text for block in message.content if block.type == "text"))
      ```

      ```typescript TypeScript
      import Anthropic from "@anthropic-ai/sdk";

      // spiffe-helper가 JWT-SVID를 기록하는 파일인
      // ANTHROPIC_IDENTITY_TOKEN_FILE에서 JWT-SVID를 읽고, ANTHROPIC_FEDERATION_RULE_ID,
      // ANTHROPIC_ORGANIZATION_ID, ANTHROPIC_SERVICE_ACCOUNT_ID, ANTHROPIC_WORKSPACE_ID도 함께 읽습니다.
      const client = new Anthropic();

      const message = await client.messages.create({
        model: "claude-opus-5-5",
        max_tokens: 1024,
        messages: [{ role: "user", content: "Hello, Claude" }]
      });
      for (const block of message.content) {
        if (block.type === "text") {
          console.log(block.text);
        }
      }
      ```

      ```csharp C#
      // spiffe-helper가 기록한 JWT-SVID를 읽습니다(기록 위치:
      // ANTHROPIC_IDENTITY_TOKEN_FILE). 또한 ANTHROPIC_FEDERATION_RULE_ID,
      // ANTHROPIC_ORGANIZATION_ID, ANTHROPIC_SERVICE_ACCOUNT_ID, ANTHROPIC_WORKSPACE_ID도 읽습니다.
      using var client = new AnthropicClient();

      var message = await client.Messages.Create(new()
      {
          Model = Model.ClaudeOpus5_5,
          MaxTokens = 1024,
          Messages = [new() { Role = Role.User, Content = "Hello, Claude" }],
      });
      foreach (var block in message.Content)
      {
          if (block.Value is TextBlock textBlock)
          {
              Console.WriteLine(textBlock.Text);
          }
      }
      ```

      ```go Go
      // spiffe-helper가 JWT-SVID를 기록하는 파일인
      // ANTHROPIC_IDENTITY_TOKEN_FILE에서 JWT-SVID를 읽고, ANTHROPIC_FEDERATION_RULE_ID,
      // ANTHROPIC_ORGANIZATION_ID, ANTHROPIC_SERVICE_ACCOUNT_ID, ANTHROPIC_WORKSPACE_ID도 읽습니다.
      client := anthropic.NewClient()

      message, err := client.Messages.New(context.TODO(), anthropic.MessageNewParams{
      	Model:     anthropic.ModelClaudeOpus5_5,
      	MaxTokens: 1024,
      	Messages: []anthropic.MessageParam{
      		anthropic.NewUserMessage(anthropic.NewTextBlock("Hello, Claude")),
      	},
      })
      if err != nil {
      	panic(err)
      }
      for _, block := range message.Content {
      	if textBlock, ok := block.AsAny().(anthropic.TextBlock); ok {
      		fmt.Println(textBlock.Text)
      		break
      	}
      }
      ```

      ```java Java
      // spiffe-helper가 기록하는 JWT-SVID(기록 위치:
      // ANTHROPIC_IDENTITY_TOKEN_FILE)와 함께 ANTHROPIC_FEDERATION_RULE_ID,
      // ANTHROPIC_ORGANIZATION_ID, ANTHROPIC_SERVICE_ACCOUNT_ID, ANTHROPIC_WORKSPACE_ID를 읽습니다.
      AnthropicClient client = AnthropicOkHttpClient.fromEnv();

      var message = client.messages().create(MessageCreateParams.builder()
              .model(Model.CLAUDE_OPUS_5_5)
              .maxTokens(1024)
              .addUserMessage("Hello, Claude")
              .build());

      IO.println(message.content());
      ```

      ```php PHP
      use Anthropic\Client;

      // spiffe-helper가 JWT-SVID를 기록하는 파일인
      // ANTHROPIC_IDENTITY_TOKEN_FILE의 내용과 ANTHROPIC_FEDERATION_RULE_ID,
      // ANTHROPIC_ORGANIZATION_ID, ANTHROPIC_SERVICE_ACCOUNT_ID, ANTHROPIC_WORKSPACE_ID를 읽습니다.
      $client = new Client();

      $message = $client->messages->create(
          model: 'claude-opus-5-5',
          maxTokens: 1024,
          messages: [['role' => 'user', 'content' => 'Hello, Claude']],
      );
      $textBlock = array_find($message->content, static fn ($block): bool => $block->type === 'text');
      echo $textBlock->text, PHP_EOL;
      ```

      ```ruby Ruby
      require "anthropic"

      # spiffe-helper가 다음 파일에 기록하는 JWT-SVID를 읽습니다:
      # ANTHROPIC_IDENTITY_TOKEN_FILE. 또한 ANTHROPIC_FEDERATION_RULE_ID,
      # ANTHROPIC_ORGANIZATION_ID, ANTHROPIC_SERVICE_ACCOUNT_ID, ANTHROPIC_WORKSPACE_ID도 읽습니다.
      client = Anthropic::Client.new

      message = client.messages.create(
        model: "claude-opus-5-5",
        max_tokens: 1024,
        messages: [{role: "user", content: "Hello, Claude"}]
      )
      puts message.content.find { it.type == :text }.text
      ```
    </CodeGroup>
  </Tab>

  <Tab title="SPIFFE Workload API를 통한 토큰 제공자">
    Java를 제외하고, SPIFFE Workload API 클라이언트를 직접 링크하는 워크로드는 spiffe-helper를 건너뛰고 에이전트 소켓에서 새로운 JWT-SVID를 가져오는 토큰 제공자를 SDK에 전달할 수 있습니다. SDK는 각 토큰 교환 전에 토큰 제공자를 호출하므로 워크로드는 항상 만료되지 않은 SVID를 제시합니다. Python([py-spiffe](https://github.com/HewlettPackard/py-spiffe))과 Go([go-spiffe](https://github.com/spiffe/go-spiffe))에는 성숙한 Workload API 클라이언트가 있습니다.

    <CodeGroup exclude="shell, typescript, java, csharp, php, ruby">
      ```python Python
      import os
      import anthropic
      from anthropic import WorkloadIdentityCredentials
      from spiffe import JwtSource

      AUDIENCE = "https://api.anthropic.com"

      # SPIFFE_ENDPOINT_SOCKET 위치의 SPIRE Agent 소켓에 연결합니다.
      jwt_source = JwtSource()


      def fetch_jwt_svid() -> str:
          svid = jwt_source.fetch_svid(audience={AUDIENCE})  # audience is a set of strings
          return svid.token


      client = anthropic.Anthropic(
          credentials=WorkloadIdentityCredentials(
              identity_token_provider=fetch_jwt_svid,
              federation_rule_id=os.environ["ANTHROPIC_FEDERATION_RULE_ID"],
              organization_id=os.environ["ANTHROPIC_ORGANIZATION_ID"],
              service_account_id=os.environ["ANTHROPIC_SERVICE_ACCOUNT_ID"],
              workspace_id=os.environ.get("ANTHROPIC_WORKSPACE_ID"),
          ),
      )

      message = client.messages.create(
          model="claude-opus-5-5",
          max_tokens=1024,
          messages=[{"role": "user", "content": "Hello, Claude"}],
      )
      print(next(block.text for block in message.content if block.type == "text"))
      ```

      ```go Go
      import (
      	"context"
      	"fmt"
      	"os"

      	"github.com/anthropics/anthropic-sdk-go"
      	"github.com/anthropics/anthropic-sdk-go/option"
      	"github.com/spiffe/go-spiffe/v2/svid/jwtsvid"
      	"github.com/spiffe/go-spiffe/v2/workloadapi"
      )
      // ...
      	const audience = "https://api.anthropic.com"

      	ctx := context.Background()
      	source, err := workloadapi.NewJWTSource(ctx)
      	if err != nil {
      		panic(err)
      	}
      	defer source.Close()

      	fetchJWTSVID := func(ctx context.Context) (string, error) {
      		svid, err := source.FetchJWTSVID(ctx, jwtsvid.Params{Audience: audience})
      		if err != nil {
      			return "", err
      		}
      		return svid.Marshal(), nil
      	}

      	client := anthropic.NewClient(
      		option.WithFederationTokenProvider(fetchJWTSVID, option.FederationOptions{
      			FederationRuleID: os.Getenv("ANTHROPIC_FEDERATION_RULE_ID"),
      			OrganizationID:   os.Getenv("ANTHROPIC_ORGANIZATION_ID"),
      			ServiceAccountID: os.Getenv("ANTHROPIC_SERVICE_ACCOUNT_ID"),
      			WorkspaceID:      os.Getenv("ANTHROPIC_WORKSPACE_ID"),
      		}),
      	)

      	message, err := client.Messages.New(ctx, anthropic.MessageNewParams{
      		Model:     anthropic.ModelClaudeOpus5_5,
      		MaxTokens: 1024,
      		Messages: []anthropic.MessageParam{
      			anthropic.NewUserMessage(anthropic.NewTextBlock("Hello, Claude")),
      		},
      	})
      	if err != nil {
      		panic(err)
      	}
      	for _, block := range message.Content {
      		if textBlock, ok := block.AsAny().(anthropic.TextBlock); ok {
      			fmt.Println(textBlock.Text)
      			break
      		}
      	}
      ```
    </CodeGroup>

    <Note>
      다른 언어의 경우 런타임의 SPIFFE Workload API 클라이언트로 JWT-SVID를 가져오거나(또는 `spire-agent api fetch jwt`를 셸로 실행하여) 파일에 기록한 다음, 파일 기반 탭에서와 같이 `ANTHROPIC_IDENTITY_TOKEN_FILE`을 해당 경로로 설정하세요.
    </Note>
  </Tab>
</Tabs>

## 설정 확인

SDK를 연결하기 전에 SPIRE Agent에서 직접 JWT-SVID를 가져와 클레임이 페더레이션 규칙이 기대하는 것과 일치하는지 확인하세요. 다른 SPIFFE 구현을 사용하는 경우 해당 CLI 또는 Workload API 클라이언트로 JWT-SVID를 가져와 동일한 방식으로 페이로드를 디코딩하세요.

<Note>
  Workload API는 호출 프로세스를 어테스트합니다. Kubernetes 등록 항목의 경우 항목의 셀렉터를 충족하고 에이전트 소켓이 마운트된 파드 내부에서 이 명령을 실행하세요(예: `kubectl exec` 사용). VM과 베어메탈에서는 항목의 `unix:` 셀렉터와 일치하는 사용자 또는 프로세스로 실행하세요. 어테스트되지 않은 호스트 셸에서 실행하면 `no identity issued`가 반환되며, 이는 확인 단계에서 가장 흔한 실패입니다.
</Note>

```bash CLI
spire-agent api fetch jwt \
    -audience https://api.anthropic.com \
    -socketPath /run/spire/sockets/agent.sock \
    -output json \
  | jq -r '.[0].svids[0].svid' \
  | jq -rR 'split(".")[1] | gsub("-";"+") | gsub("_";"/") | @base64d | fromjson'
```

`-output json` 플래그는 SVID 응답과 번들 응답을 두 요소로 된 JSON 배열로 반환하므로, `jq -r '.[0].svids[0].svid'`로 순수 토큰을 추출할 수 있습니다. `-output`이 없는 이전 SPIRE 버전에서는 명령이 대신 레이블이 지정된 블록을 출력합니다. 이 경우 기본 출력을 `awk '/^[[:space:]]*eyJ/{print $1; exit}'`로 파이프하여 토큰 줄을 추출하세요. `iss`가 등록한 OIDC Discovery Provider URL인지, `sub`가 워크로드의 SPIFFE ID인지, `aud`에 `https://api.anthropic.com`이 포함되어 있는지 확인하세요. 그런 다음 [토큰 획득 및 사용](https://platform.claude.com/docs/ko/manage-claude/wif-providers/spiffe#acquire-and-use-the-token)의 cURL 예제를 실행하세요. 교환에 성공하면 `sk-ant-oat01-`로 시작하는 `access_token`이 반환됩니다. 교환이 불투명한 `401` `authentication_error` 응답(메시지 `Authentication failed`)으로 실패하면, Claude Console의 [인증 기록 페이지](https://platform.claude.com/settings/workload-identity-federation?tab=history)에서 거부 사유를 확인하고 [실패한 교환 문제 해결](https://platform.claude.com/docs/ko/manage-claude/wif-reference#troubleshoot-a-failed-exchange)을 참조하세요. SPIRE 측에서 가장 흔한 원인은 SPIRE Server의 `jwt_issuer`와 페더레이션 발급자로 등록된 URL 간의 불일치입니다.

## 규칙 범위 지정

SPIFFE ID 경로 규칙은 운영자가 정의하므로, 페더레이션 규칙의 `subject_prefix` 매처는 등록 항목이 사용하는 경로 체계를 반영해야 합니다. 일반적인 체계로는 `spiffe://<trust-domain>/ns/<namespace>/sa/<service-account>`(spire-controller-manager의 `ClusterSPIFFEID` 리소스가 기본으로 생성하는 형식)와 VM 및 베어메탈 워크로드용 `spiffe://<trust-domain>/host/<hostname>/<service>`가 있습니다.

<Warning>
  `spiffe://prod.example.com/*`의 `subject_prefix`는 트러스트 도메인의 모든 워크로드와 일치합니다. `audience` 매처가 없으면 규칙은 워크로드가 관련 없는 신뢰 당사자를 위해 요청한 것을 포함하여 모든 audience에 대해 발급된 JWT-SVID도 허용합니다.
</Warning>

규칙의 `match` 블록을 사용 사례에 맞는 가장 좁은 범위로 제한하세요.

* **하나의 워크로드에 고정:** `subject_prefix`를 끝에 `*` 없이 전체 SPIFFE ID로 설정하세요.
* **항상 audience 설정:** 규칙에 `audience`를 필수로 지정하고 spiffe-helper(또는 Workload API 호출)를 동일한 값으로 구성하여 다른 신뢰 당사자를 위해 발급된 SVID가 거부되도록 하세요.
* **경로 세그먼트로 범위 지정:** `spiffe://prod.example.com/ns/inference/*`를 사용하여 네임스페이스 아래에 등록된 모든 워크로드에 권한을 부여하고, 하나의 규칙을 넓히는 대신 네임스페이스별로 별도의 규칙과 Anthropic 서비스 어카운트를 생성하세요.
* **트러스트 도메인당 하나의 발급자:** 각 SPIRE 트러스트 도메인에는 자체 서명 키와 OIDC Discovery Provider가 있습니다. 각각을 별도의 페더레이션 발급자로 등록하고 규칙을 해당 규칙이 매칭하는 SPIFFE ID를 소유한 발급자에 바인딩하세요.

## 다음 단계

<CardGroup cols={2}>
  <Card title="Okta와 함께 WIF 사용하기" icon="lock" href="https://platform.claude.com/docs/ko/manage-claude/wif-providers/okta">
    Workload Identity Federation으로 Okta 서비스 애플리케이션 ID를 Claude API에 페더레이션합니다.
  </Card>

  <Card title="Workload Identity Federation" icon="cloud" href="https://platform.claude.com/docs/ko/manage-claude/workload-identity-federation">
    수명이 긴 정적 API 키 대신 자체 ID 프로바이더의 수명이 짧은 ID 토큰으로 워크로드를 Claude API에 인증합니다.
  </Card>

  <Card title="WIF 참조" icon="book" href="https://platform.claude.com/docs/ko/manage-claude/wif-reference">
    Workload Identity Federation의 환경 변수, 검증 규칙, 프로필 구성 및 오류 참조.
  </Card>

  <Card title="Kubernetes와 함께 WIF 사용하기" icon="cube" href="https://platform.claude.com/docs/ko/manage-claude/wif-providers/kubernetes">
    프로젝티드 서비스 어카운트 토큰을 사용하여 자체 관리형 Kubernetes 클러스터에서 Claude API에 인증합니다.
  </Card>
</CardGroup>
