Azure 工作负载通过出示由 Microsoft Entra ID 颁发的 "JSON Web Token"(JSON Web 令牌),即 JWT,然后将其交换为短期有效的 Anthropic 访问令牌,从而向 Claude API 进行身份验证。获取 Entra 颁发的令牌有两种常见方式:
http://169.254.169.254/metadata/identity/oauth2/token 的 Azure 实例元数据服务(IMDS),并接收其所分配标识的 JWT。AZURE_FEDERATED_TOKEN_FILE 所指定的路径。工作负载在 Entra 处交换该令牌,以获取 Entra 颁发的访问令牌。在这两种情况下,您向 Anthropic 出示的 Entra 颁发的令牌都包含租户特定的 Entra 颁发者(下文的配置 Anthropic 步骤展示了需要注册的确切 URL),以及 sub 和 oid 声明中的托管标识对象 ID。您只需向 Anthropic 注册该颁发者一次,编写一条匹配预期声明的联合规则,您的工作负载即可在运行时将其 Entra 令牌交换为 sk-ant-oat01-... 访问令牌。
AKS Pod 也可以跳过 Entra 交换步骤,直接向 Anthropic 出示 Kubernetes 投射的服务账户令牌。该路径会向 Anthropic 注册您的 AKS 集群的 OIDC 颁发者,而非您的 Entra 租户。有关该流程,请参阅 Kubernetes。
设置 Azure 将为其颁发令牌的标识。请选择与您的工作负载运行位置相匹配的路径。
为托管标识颁发的 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)。azp 是应用程序或客户端 ID。匹配 oid 可授权一个特定的标识,匹配 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>/。请解码您的托管标识令牌(下文的"验证"部分展示了具体方法),并注册其中包含的 iss 值。这两个 URL 共享相同的 JWKS,因此发现模式对两者均适用。
联合规则: 匹配托管标识的对象 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 处进行交换,并使用返回的 Bearer 令牌调用 Claude。当您提供令牌提供程序可调用对象时,每个 Anthropic SDK 都会处理交换和刷新循环,如以下示例所示。cURL 选项卡展示了原始流程。
在 AKS 上,AZURE_FEDERATED_TOKEN_FILE 所指向的文件是由您的集群 OIDC 颁发者签名的 Kubernetes 投射服务账户令牌,而非 Entra 颁发的令牌。若要继续使用本页所述的经由 Entra 中转的路径,请先在 https://login.microsoftonline.com/<TENANT_ID>/oauth2/v2.0/token(联合 client_credentials 授权)处交换该令牌,然后将生成的 Entra 访问令牌作为身份令牌传递给 Anthropic SDK。
或者,直接向 Anthropic 注册您的 AKS 集群的 OIDC 颁发者,跳过 Entra 中转步骤。有关该模式,请参阅 Kubernetes。
从您的 Azure 资源中,运行前面展示的 cURL 交换命令,并确认 POST /v1/oauth/token 返回 200,其中包含以 sk-ant-oat01- 开头的 access_token 以及以秒为单位的 expires_in 值。如果返回 400 invalid_grant,请参阅排查交换失败问题;Azure 端最常见的原因是您注册的 issuer_url 与解码后令牌中的 iss 声明不匹配。两者必须完全一致。对于托管标识令牌,iss 值为 https://login.microsoftonline.com/<TENANT_ID>/v2.0 或 https://sts.windows.net/<TENANT_ID>/。
oid 声明是托管标识的 GUID,没有稳定的前缀。带有 * 的
subject_prefix 会匹配租户中的任意标识,因此任何持有托管标识的
工作负载都可能获取联合的 Anthropic 令牌。
将规则的 match 块锁定到适合您用例的最小范围:
oid 作为精确值进行匹配: 将 claims.oid 设置为托管标识的完整对象 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)