Azureワークロードは、Microsoft Entra IDによって発行された「JSON Web Token」(JSONウェブトークン)、すなわちJWTを提示し、それを短期間有効なAnthropicアクセストークンと交換することでClaude APIに対して認証を行います。Entraが発行するトークンを取得する一般的な方法は2つあります。
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発行者(登録する正確なURLは以下のAnthropicを設定するステップで示します)と、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です。特定の1つの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値を登録してください。2つの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にあるファイルは、クラスターのOIDC発行者によって署名されたKubernetes投影サービスアカウントトークンであり、Entra発行のトークンではありません。このページで説明している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)