Azure 워크로드는 Microsoft Entra ID에서 발급한 "JSON Web Token"(JSON 웹 토큰), 즉 JWT를 제시한 다음 이를 단기 Anthropic 액세스 토큰으로 교환하여 Claude API에 인증합니다. Entra에서 발급한 토큰을 얻는 일반적인 방법은 두 가지입니다.
http://169.254.169.254/metadata/identity/oauth2/token에서 Azure Instance Metadata Service(IMDS)를 호출하고 할당된 ID에 대한 JWT를 받습니다.AZURE_FEDERATED_TOKEN_FILE에 지정된 경로의 파드에 프로젝션합니다. 워크로드는 해당 토큰을 Entra에서 교환하여 Entra에서 발급한 액세스 토큰을 받습니다.두 경우 모두 Anthropic에 제시하는 Entra 발급 토큰은 테넌트별 Entra 발급자(아래 Anthropic 구성 단계에서 등록할 정확한 URL을 보여줍니다)와 sub 및 oid 클레임에 관리 ID의 객체 ID를 포함합니다. 해당 발급자를 Anthropic에 한 번 등록하고, 예상되는 클레임과 일치하는 페더레이션 규칙을 작성하면, 워크로드가 런타임에 Entra 토큰을 sk-ant-oat01-... 액세스 토큰으로 교환합니다.
AKS 파드는 대안으로 Entra 교환을 건너뛰고 Kubernetes에서 프로젝션한 서비스 계정 토큰을 Anthropic에 직접 제시할 수 있습니다. 이 경로는 Entra 테넌트 대신 AKS 클러스터의 OIDC 발급자를 Anthropic에 등록합니다. 해당 흐름은 Kubernetes를 참조하세요.
Azure가 토큰을 발급할 ID를 설정합니다. 워크로드가 실행되는 위치에 맞는 경로를 선택하세요.
관리 ID에 대한 Entra 발급 토큰은 다음 클레임을 포함합니다.
{
"iss": "https://login.microsoftonline.com/<TENANT_ID>/v2.0",
"sub": "9f8e7d6c-1a2b-3c4d-5e6f-...",
"aud": "https://api.anthropic.com",
"oid": "9f8e7d6c-1a2b-3c4d-5e6f-...",
"tid": "<TENANT_ID>",
"azp": "<CLIENT_ID>",
"exp": 1775527120
}sub와 oid는 동일합니다(관리 ID의 객체 ID). azp는 애플리케이션 또는 클라이언트 ID입니다. 특정 ID 하나를 승인하려면 oid로 매칭하고, 애플리케이션 등록과 연결된 모든 ID를 승인하려면 azp로 매칭하세요. tid 클레임은 테넌트 ID를 반복합니다. 발급자 URL이 이미 테넌트를 고정하므로 이를 매칭하는 것은 심층 방어입니다.
설정 안내를 따라 Claude Console에서 페더레이션 발급자를 등록하고, Anthropic 서비스 계정을 생성하고, 페더레이션 규칙을 생성하세요. Console에서 OIDC 공급자 옵션을 선택하고 다음에 나오는 Entra 관련 값을 입력하세요.
페더레이션 발급자: Entra는 테넌트별 발급자 URL에서 OIDC 디스커버리 문서를 게시하므로 디스커버리 모드를 사용하세요. 페더레이션하는 각 Azure 테넌트마다 자체 발급자 레코드가 필요합니다.
{
"name": "azure-prod-tenant",
"issuer_url": "https://login.microsoftonline.com/<TENANT_ID>/v2.0",
"jwks_source": "discovery"
}토큰 버전에 따라 iss 클레임이 https://sts.windows.net/<TENANT_ID>/일 수 있습니다. 관리 ID 토큰을 디코딩하고(아래 확인 섹션에서 방법을 보여줍니다) 포함된 iss 값을 등록하세요. 두 URL은 동일한 JWKS를 공유하므로 디스커버리 모드는 둘 다 작동합니다.
페더레이션 규칙: 관리 ID의 객체 ID와 테넌트 ID로 매칭합니다.
{
"name": "azure-inference-worker",
"issuer_id": "fdis_...",
"match": {
"audience": "https://api.anthropic.com",
"claims": {
"oid": "9f8e7d6c-1a2b-3c4d-5e6f-...",
"tid": "<TENANT_ID>"
}
},
"target": {
"type": "service_account",
"service_account_id": "svac_..."
},
"workspace_id": "wrkspc_...",
"oauth_scope": "workspace:developer",
"token_lifetime_seconds": 600
}런타임에 워크로드는 Entra 토큰을 가져와 POST /v1/oauth/token에서 교환하고, 반환된 베어러 토큰을 사용하여 Claude를 호출합니다. 다음 예제에서 보듯이 토큰 공급자 콜러블을 제공하면 각 Anthropic SDK가 교환 및 갱신 루프를 처리합니다. cURL 탭은 원시 흐름을 보여줍니다.
AKS에서 AZURE_FEDERATED_TOKEN_FILE에 있는 파일은 Entra에서 발급한 토큰이 아니라 클러스터의 OIDC 발급자가 서명한 Kubernetes 프로젝션 서비스 계정 토큰입니다. 이 페이지에서 설명하는 Entra 중개 경로를 유지하려면 먼저 해당 토큰을 https://login.microsoftonline.com/<TENANT_ID>/oauth2/v2.0/token(페더레이션 client_credentials 그랜트)에서 교환한 다음, 결과로 받은 Entra 액세스 토큰을 ID 토큰으로 Anthropic SDK에 전달하세요.
또는 AKS 클러스터의 OIDC 발급자를 Anthropic에 직접 등록하고 Entra 단계를 건너뛸 수 있습니다. 해당 패턴은 Kubernetes를 참조하세요.
Azure 리소스에서 앞서 보여준 cURL 교환을 실행하고 POST /v1/oauth/token이 sk-ant-oat01-로 시작하는 access_token과 초 단위의 expires_in 값을 포함한 200을 반환하는지 확인하세요. 400 invalid_grant가 발생하면 실패한 교환 문제 해결을 참조하세요. Azure 측에서 가장 흔한 원인은 등록한 issuer_url과 디코딩된 토큰의 iss 클레임 간의 불일치입니다. 이 둘은 정확히 일치해야 합니다. 관리 ID 토큰의 경우 iss 값은 https://login.microsoftonline.com/<TENANT_ID>/v2.0 또는 https://sts.windows.net/<TENANT_ID>/입니다.
oid 클레임은 관리 ID의 GUID이며 안정적인 접두사가 없습니다. *가 포함된
subject_prefix는 테넌트의 임의 ID와 일치하므로, 관리 ID를 보유한 모든
워크로드가 페더레이션된 Anthropic 토큰을 얻을 수 있습니다.
규칙의 match 블록을 사용 사례에 맞는 가장 좁은 범위로 제한하세요.
oid를 정확한 값으로 매칭: claims.oid를 관리 ID의 전체 객체 ID로 설정하고 Azure 토큰에는 절대 subject_prefix를 사용하지 마세요.tid 고정: 발급자 URL이 이미 테넌트를 고정하지만, claims.tid를 추가하면 나중에 발급자 레코드가 편집될 경우 구성 드리프트를 방지할 수 있습니다.audience를 https://api.anthropic.com으로 설정하여 다른 리소스용으로 발급된 토큰이 거부되도록 하세요.Was this page helpful?
import os
import anthropic
import requests
from anthropic import WorkloadIdentityCredentials
IMDS_URL = "http://169.254.169.254/metadata/identity/oauth2/token"
def fetch_entra_token() -> str:
"""Fetch a managed identity token from Azure IMDS."""
response = requests.get(
IMDS_URL,
headers={"Metadata": "true"},
params={"api-version": "2018-02-01", "resource": "https://api.anthropic.com"},
timeout=5,
)
response.raise_for_status()
return response.json()["access_token"]
client = anthropic.Anthropic(
credentials=WorkloadIdentityCredentials(
identity_token_provider=fetch_entra_token,
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-sonnet-4-6",
max_tokens=1024,
messages=[{"role": "user", "content": "Hello from Azure"}],
)
print(message.content[0].text)import os
from pathlib import Path
import httpx
import anthropic
from anthropic import WorkloadIdentityCredentials
def fetch_entra_token_via_federation() -> str:
federated_token = Path(os.environ["AZURE_FEDERATED_TOKEN_FILE"]).read_text()
response = httpx.post(
f"https://login.microsoftonline.com/{os.environ['AZURE_TENANT_ID']}/oauth2/v2.0/token",
data={
"client_id": os.environ["AZURE_CLIENT_ID"],
"grant_type": "client_credentials",
"scope": "https://api.anthropic.com/.default",
"client_assertion_type": "urn:ietf:params:oauth:client-assertion-type:jwt-bearer",
"client_assertion": federated_token,
},
)
response.raise_for_status()
return response.json()["access_token"]
client = anthropic.Anthropic(
credentials=WorkloadIdentityCredentials(
identity_token_provider=fetch_entra_token_via_federation,
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-sonnet-4-6",
max_tokens=1024,
messages=[{"role": "user", "content": "Hello from Azure"}],
)
print(message.content[0].text)