Authenticate an MCP Server with Microsoft Entra ID (Azure AD) Managed Identity
When your agent runs in Azure, a managed identity gets it a Microsoft Entra ID (still widely called Azure AD) access token for your MCP server, with no secret in configuration and nothing to rotate.
- Method
- Microsoft Entra ID
- Platform
- Azure
- MCP Specification
- 2026-07-28
How it works
The agent asks Azure for an access token whose audience is the MCP server's Application ID URI, then sends it as Authorization: Bearer <token>. The Azure Identity libraries take that audience as a scope — the Application ID URI with /.default appended, one resource per request — while the local managed-identity endpoint underneath takes the same value as resource=api://…, which Microsoft documents as what lands in the token's aud claim. The server verifies the signature against Entra's JWKS and checks aud, iss and tid before reading the calling workload from appid or oid.
- Best for
- Agents and MCP servers that both run in Azure, in one Entra tenant
- MCP spec
- Out of scope of the spec's OAuth flow, which the spec makes optional
- Works with
- Your own workloads, plus Microsoft Foundry Agent Service; no chat or desktop client can mint one
- Effort
- A day, mostly the app registration and role assignments
The exchange
Each step in the exchange, in order.
- Your agent VM, App Service, Functions
- Entra ID via the local endpoint
- MCP server validates the JWT
-
Whatever you pass as resource is also what appears in the token's aud claim. You rarely write this request yourself: the Azure Identity libraries take the same audience as scope=api://…/.default and use whichever local token endpoint the host provides — on a VM that is IMDS, which requires the Metadata: true header.
Your agent to Entra ID
GET …/identity/oauth2/token resource=api://mcp.example.com -
Entra ID to Your agent
access_token aud = api://mcp.example.com -
Your agent to MCP server
POST /mcp Authorization: Bearer <token> -
Validation is offline against cached JWKS; Entra is never called by the server. The tid check is what rejects a token from another tenant, and the aud check is what stops a token minted for a different Azure resource being replayed here.
Inside MCP server
Verify the signature against Entra's JWKS -
Inside MCP server
Check aud, iss and tid, then read the caller appid = 00001111-aaaa-2222-bbbb-3333cccc4444 -
MCP server to Your agent
200 OK tool result
Connect your agent
Pick your language and SDK.
When to use something else
Use this when
- The agent and the MCP server both run in Azure under one Entra tenant, so the platform issues the identity and your server only has to verify it.
- You want no stored secret: on Azure a managed identity is the client-credentials grant with the credential taken out.
- Tool calls need to be attributed to a specific workload — an
appidoroidclaim — rather than to a key that five services share.
Use something else when
- An MCP client you do not control has to connect. The configuration surfaces in Claude, ChatGPT, Cursor and VS Code take static headers or run the spec OAuth flow; none of them can acquire an Azure token.
- You need a client to onboard itself. Microsoft's own guidance is that many providers do not support Dynamic Client Registration, 'including Microsoft Entra ID', so every client has to be pre-registered or preauthorized by hand.
- The call is made on behalf of a named person: a managed identity token is app-only, so there is no user in it to authorize or to audit.
The specification makes authorization OPTIONAL and says HTTP transports SHOULD, rather than MUST, conform to its OAuth flow, so an Entra ID token presented as a bearer credential sits outside the spec rather than in conflict with it. That paragraph is word-for-word the same on the 2025-11-25 and 2026-07-28 authorization pages, so nothing in this method changed on 28 July 2026.
What lands on you next
A managed identity answers one question well — is this workload allowed in — and only for workloads inside your own tenant. The questions that arrive next are all about callers Azure cannot vouch for.
-
Legal wants these tools in Claude with our Entra sign-in. Is that a rewrite?
-
mcp-entra-oauth-inboundNo — the inbound method is a policy, and there is a first-class one for Entra ID. Exactly one MCP OAuth policy per project, and the Entra token never reaches the MCP client.
-
The token is valid but this app role is not. Where does that check go?
-
require-user-claims-inboundOn the route. Allow or deny on any claim the token carries —
roles,appid,tid, a group — and the caller gets a 403 before your server runs a line of code. -
Can the reporting agent see fewer tools than the admin one?
-
mcp-capability-filter-inboundAs two routes, yes: the filter is a static per-route allow-list with no per-user axis. A hidden tool is refused with
MethodNotFoundbefore the call reaches your server. -
One agent looped overnight and burned the quota on the API behind it.
-
rate-limit-inboundrateLimitBy: "user"counts every tool call against the authenticated identity rather than a shared egress IP, so one workload cannot spend another's budget.
All of these attach to one MCP route's policies.inbound — the same policy engine on the way in and on the way out.
Common questions
Managed identity or service principal — which one for MCP?
The same token either way; managed identity is the version with no credential to store. Use a service principal with a certificate or a federated credential only where a managed identity is unavailable, which in practice means outside Azure.
Why scope=api://…/.default rather than the resource parameter?
Two endpoints, two spellings of one value. The v2.0 token endpoint takes the resource identifier — the Application ID URI — in the scope parameter with .default appended, and rejects the older parameter — AADSTS901002 reports that the 'resource' request parameter isn't supported. The local managed-identity endpoint is the exception and still takes resource=. Either way that value is what appears in the token's aud claim, and every scope in one request must belong to a single resource.
Should the audience be the Application ID URI or the server's URL?
Whatever you registered as the Application ID URI on the app registration, character for character, and validate the same string in the server. Ask for an audience Entra does not know and the request fails with AADSTS500011: the resource principal named {name} wasn't found in the tenant named {tenant}. A malformed one fails earlier with AADSTS70011, the provided value for the input parameter 'scope' isn't valid.
Does Entra ID support the MCP authorization flow?
Only in part, and Microsoft says so plainly: some providers support Dynamic Client Registration, 'but many don't, including Microsoft Entra ID', so each client has to be preconfigured with a client ID and, preferably, preauthorized. Discovery is thinner than it looks too. Entra publishes OpenID Connect Discovery at /{tenant}/v2.0/.well-known/openid-configuration — the third endpoint a conformant client probes, since both higher-priority well-known forms 404 today — and that document omits code_challenge_methods_supported, whose absence the spec says must make a client refuse to proceed.
Can Claude, ChatGPT or Cursor connect to a server protected this way?
No. Their configuration surfaces accept static headers or the spec OAuth flow, and none of them can acquire an Azure token. The one hosted exception is Microsoft Foundry Agent Service, which authenticates to an MCP server with either the agent identity or the project's managed identity.
Can the tool forward the caller's token to Microsoft Graph?
No. The spec is explicit — the MCP server MUST NOT pass through the token it received from the MCP client — and Microsoft says the same about its own platform: pass-through scenarios 'create security vulnerabilities', and you should obtain a new token through the on-behalf-of flow. Foundry enforces it for managed OAuth: send a Microsoft-audience token to a custom MCP endpoint and Agent Service returns 'Cannot pass Microsoft token to untrusted MCP endpoint.'
Is the Azure Functions MCP extension using Entra ID?
No — it gates the endpoint with a shared secret. Hosted in Azure, the extension requires the mcp_extension system key on /runtime/webhooks/mcp: without it in the x-functions-key header or the code query string the client gets a 401. You can drop that requirement by setting system.webhookAuthorizationLevel to Anonymous in host.json, and either way you can layer App Service's built-in MCP server authorization on top as an identity-based access control layer.
Did the 2026-07-28 spec release change anything for this method?
No. Authorization is still OPTIONAL and HTTP transports still only SHOULD conform — that paragraph is unchanged between the two revisions.
One policy engine for APIs, AI, and MCP
Put your MCP servers behind a gateway that speaks every identity provider, filters tools per role, and logs every call.