Entra Agent ID audited: blueprints, fmi_path and the agent marker claim
An agent identity in Microsoft Entra has no credentials of its own. Its blueprint holds the credential, impersonates the agent through a two-hop token exchange, and the token that comes out carries a marker claim that says which blueprint made it. We read the whole spec to find out what that model actually enforces.
Microsoft Entra Agent ID is the identity layer Microsoft ships for AI agents inside a corporate tenant. It was announced as a preview at Build on May 19, 2025, expanded at Ignite in November 2025, and the Entra release notes list "General Availability - Microsoft Entra Agent ID platform" under April 2026. Since then the surface has kept moving: the Entra agent registry blades were retired on May 1, 2026 in favor of Microsoft Agent 365, the Graph reference for the new object types was updated as recently as September 3, and on the same day a how-to titled "Secure a Model Context Protocol (MCP) server with Microsoft Entra ID" was merged into the public docs repository.
We did not take the product pages at their word. We pulled the 58 Markdown files that make up docs/agent-id in the public MicrosoftDocs/entra-docs repository, the Microsoft Graph beta resource types for agentIdentityBlueprint and agentIdentity, the OpenAPI document of the Auth SDK sidecar from the microsoft-identity-web repository, the sidecar image tag list on the Microsoft Container Registry, the token claims reference and the samples repository. Everything below is from those sources, read on September 13, 2026. Where the documents disagree with each other, we say so.
Three objects, one credential
The key concepts page defines the hierarchy in three sentences that carry most of the design. An agent identity "is the primary identity an AI agent uses to authenticate to systems and access resources. Unlike user accounts, agent identities don't have credentials of their own. They authenticate using tokens issued by their agent identity blueprint." A blueprint "holds credentials and uses them to acquire tokens on behalf of all agent identities created from it." And when a blueprint is added to a tenant, Entra creates a blueprint principal, the object that appears in audit logs and acquires tokens for it.
In Graph terms these are not new primitives but subtypes. The agentIdentityBlueprint resource "inherits from application" and is serialized as #microsoft.graph.agentIdentityBlueprint. The agentIdentity resource "inherits from servicePrincipal", is serialized as #microsoft.graph.agentIdentity, carries an agentIdentityBlueprintId that holds the parent's appId, and has servicePrincipalType fixed to ServiceIdentity. The service principals page is explicit that this is the point: "Agent identities are modeled as single-tenant service principals with a new 'agent' subtype classification." A fourth, optional object, the agent's user account, pairs 1:1 with an agent identity for the cases where a mailbox or a Teams presence is required.
The rules that hang off that hierarchy are what an operator needs to know, and they are scattered across several pages. We collected them.
One blueprint per agent, many agents per blueprint
"Agent identity blueprints can only impersonate their child agent identities. Only a single agent identity blueprint can impersonate an agent identity. An agent identity blueprint can impersonate many agent identities, but no agent identity can be owned by multiple blueprints." Agent identities "are always single-tenant regardless of their parent Agent identity blueprint's tenancy model." Blueprints can be published multitenant and consumed by other tenants, but every identity they spawn lives in one tenant.
The blueprint can do exactly one thing
The blueprint page states that "a blueprint can perform exactly one operation in the tenant: provision or deprovision agent identities", using a dedicated Graph permission, AgentIdentity.CreateAsManager. Blueprints "can't be assigned Azure RBAC roles"; agent identities can, along with Entra directory roles and app roles. "Disabling an agent identity blueprint prevents all its agent identities from authenticating." A compromise of the blueprint's credential "affects all agent identities under it, which is why blueprint count is a security boundary decision."
Permissions flow down, consent stays with humans
A blueprint declares requiredResourceAccess (what it needs, shown at consent time) and inheritablePermissions (which resource apps' grants cascade to every child). Both "are declarations that don't grant authorization by themselves. Administrators must still consent." Agent identities expose inheritedAppRoleAssignments and inheritedOauth2PermissionGrants as read-only relationships. Inheritance "works only within tenant boundaries."
Two limits from the schema are worth recording. A blueprint's requiredResourceAccess may name no more than 50 resource APIs and 400 permissions in total. The managerApplications collection, which lets another application create principals and identities for a blueprint without AgentIdentityBlueprintPrincipal.ReadWrite.All, is capped at 10 values and "currently, only Microsoft first-party application IDs can be set."
The two-hop exchange keyed by fmi_path
The core mechanism is a token exchange that the docs call impersonation. The autonomous flow page publishes both hops. In the first, the blueprint presents its own credential and asks for an exchange token scoped to the token-exchange resource, naming the child it intends to become:
# Hop 1: blueprint -> exchange token T1
POST /oauth2/v2.0/token
Content-Type: application/x-www-form-urlencoded
client_id=AgentBlueprint
&scope=api://AzureADTokenExchange/.default
&fmi_path=AgentIdentity
&client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer
&client_assertion=TUAMI
&grant_type=client_credentials
The fmi_path parameter is the only non-standard piece. The docs define it as "the client ID (app ID) of the agent identity. This parameter tells Microsoft Entra ID which child agent identity the blueprint is impersonating during the token exchange." TUAMI in the example is a managed identity token used as a federated identity credential; the same slot takes a client secret or a certificate, with a warning on every flow page that secrets "shouldn't be used as client credentials in production environments for agent identity blueprints."
In the second hop the child presents T1 as its client assertion and asks for the resource token:
# Hop 2: agent identity -> resource token TR
POST /oauth2/v2.0/token
Content-Type: application/x-www-form-urlencoded
client_id=AgentIdentity
&scope=https://resource.example.com/.default
&client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer
&client_assertion={T1}
&grant_type=client_credentials
The validation rule, quoted from the page: "Microsoft Entra ID validates that T1 (aud) == Agent identity parent app == Agent identity blueprint." The on-behalf-of page adds the finer version: T1's azp must be the blueprint, and T1's sub, "the FMI path", must resolve to the child performing the exchange. In other words, the child never holds a long-lived credential. It holds a short-lived assertion that the directory minted for exactly one blueprint-child pair.
The on-behalf-of flow is the same skeleton with a user token attached. Hop 2 becomes a jwt-bearer grant with assertion={Tc}, client_assertion={T1} and requested_token_use=on_behalf_of. Two constraints matter for anyone wiring a client to it. First, the user token "must be audienced to the blueprint; a token audienced to another resource (for example, Microsoft Graph) is rejected with AADSTS50013." Second, neither blueprints nor agent identities "can initiate interactive /authorize flows", and trying to consent on a child "returns the error AADSTS82014". Consent has to be granted on the blueprint as an inheritable permission. Supported grant types are listed as client_credentials, jwt-bearer and refresh_token. Public clients are not available; every agent entity is a confidential client.
The third flow, the agent's user account impersonation, adds a hop and a grant type that we had not seen documented anywhere else. The blueprint obtains T1 for the agent identity, the agent identity obtains a second exchange token T2 for itself, and then it calls the token endpoint with grant_type=user_fic, user_federated_identity_credential={T2}, [email protected] and requested_token_use=on_behalf_of. The page notes that "the same client ID must be used for both phases to prevent privilege escalation attacks."
What the token says
The token claims reference publishes a full sample access token for an autonomous agent. The parts that differ from an ordinary app-only token:
{
"aud": "00001111-aaaa-2222-bbbb-3333cccc4444",
"appid": "11112222-bbbb-3333-cccc-4444dddd5555",
"idtyp": "app",
"oid": "aaaaaaaa-0000-1111-2222-bbbbbbbbbbbb",
"sub": "bbbbbbbb-1111-2222-3333-cccccccccccc",
"tid": "aaaabbbb-0000-cccc-1111-dddd2222eeee",
"xms_act_fct": "3 9 11",
"xms_sub_fct": "9 3 11",
"xms_tnt_fct": "3 9",
"xms_idrel": "7 10",
"xms_par_app_azp": "30cf4c22-9985-4ef7-8756-91cc888176bd"
}
Four claims do the work. xms_par_app_azp is "the parent application of the authorized party", meaning the blueprint's app ID. xms_act_fct and xms_sub_fct are space-separated integer lists describing the actor and the subject; the two values that matter to a resource server are 11 for "AgentIdentity" and 13 for "AgentIDUser". xms_idrel describes the relationship between the subject and the tenant; 7 is a service principal and 33 is "Federated managed identity". The reference warns that these are multi-valued, unordered, and that "you should ignore any values that aren't relevant."
The three scenarios differ in a way that idtyp alone cannot express. An agent acting on behalf of a human yields idtyp=user, oid of the human, xms_act_fct containing 11. An autonomous agent yields idtyp=app, xms_idrel 7, both facet claims containing 11, and scp "empty or / (unscoped)". An agent running as its own user account yields idtyp=user again, but xms_sub_fct containing 13. The reference says this plainly: "The idtyp value alone doesn't distinguish a human user from an agent's user account."
Here the documents disagree, or at least talk past each other. The downstream API validation how-to lists four checks and says of the fourth: "The xms_par_app_azp claim is present in agent identity tokens and absent in standard app-only tokens." The claims reference lists xms_par_app_azp and the facet claims under "the following optional claims are also supported to identify that the tokens are for agent identities." A reader who follows the how-to will treat presence as a given. A reader who follows the reference will wonder whether the claim must be configured through the blueprint's optionalClaims. The sample token includes it and the sample weather API keys on it, so the practical reading is that it ships by default, but this is one to confirm against your own tenant before you make an authorization decision depend on it. The reference itself advises against that: "It isn't recommended to use the parent application ID for authorization decisions, as it would result in widespread access by many agents." Log it, do not gate on it.
The sidecar as the only thing that talks to login.microsoftonline.com
Microsoft's answer to "who performs the two hops" is a container. The Auth SDK sidecar runs next to the agent, holds the blueprint credential, and exposes HTTP endpoints "on the pod-local network". The stated security boundary is that "the sidecar has no host port. Only services inside the same network, such as your agent container, can request tokens." The image lives at mcr.microsoft.com/entra-sdk/auth-sidecar. We listed its tags through the registry API on September 13: 1.0.0-rc.1, 1.0.0-rc.2, 1.0.0, 1.1.0 and 1.1.1, each in an azurelinux3.0-distroless and a windows variant.
The HTTP contract is small. We read the OpenAPI document in the microsoft-identity-web repository; it declares version 1.0.0 and five routes: GET /Validate, GET /AuthorizationHeader/{apiName}, GET /AuthorizationHeaderUnauthenticated/{apiName}, and /DownstreamApi/{apiName} plus its unauthenticated twin, which proxy the call and return status, headers and body. The published endpoint reference adds /healthz and the rules for the three agent parameters:
# Autonomous agent: AgentIdentity alone
GET /AuthorizationHeaderUnauthenticated/graph-app?AgentIdentity={agentAppId}
# Delegated agent: AgentIdentity + inbound user token (OBO)
GET /AuthorizationHeader/graph?AgentIdentity={agentAppId}
Authorization: Bearer {Tc}
# Agent's user account: AgentIdentity + one of
&AgentUsername=[email protected] # or
&AgentUserId={objectId} # mutually exclusive
# Response
{ "authorizationHeader": "Bearer eyJ0eXAiOiJKV1QiLCJhbGc..." }
Credential source is a configuration switch, not code: ClientSecret for local development, SignedAssertionFromManagedIdentity for Azure, KeyVault for a certificate in Key Vault, StoreWithThumbprint for a local certificate store. The local development walkthrough runs four containers with Docker Compose, including an Ollama container that pulls qwen2.5:1.5b, and warns that this model "is too small for reliable tool calling". The sidecar also supports proof-of-possession: pass optionsOverride.AcquireTokenOptions.PopPublicKey and the header comes back as PoP instead of Bearer. The samples repository that backs all of this was created on April 9, 2026, is MIT-licensed, and on the day we checked had 12 stars, 8 forks, 20 open issues and a last push on August 5. It is reference material, not an ecosystem.
Agents that do not run on Azure
The part most relevant to anyone operating agents on their own infrastructure is that the blueprint credential does not have to be a secret or an Azure managed identity. The third-party agents page documents two integration patterns. The sidecar pattern works "with any containerized agent" and names AWS Bedrock, Ollama with LangChain and n8n. The federation pattern "uses Workload Identity Federation to exchange credentials from external identity providers such as AWS Security Token Service (STS) directly for Microsoft Entra tokens" and lists "GCP Workload Identity → Microsoft Entra Agent ID" and "AWS STS → Microsoft Entra Agent ID" as supported paths. The requirement is "an agent platform that supports OIDC or STS" and a federated identity credential preconfigured on the blueprint.
The federated credential itself is the standard Entra object. From the workload identity federation page: the issuer and subject in the credential are matched against the iss and sub of the external token, the audience must be api://AzureADTokenExchange for the global cloud, "a maximum of 20 federated identity credentials can be added to an application", and a misconfigured credential "is created successfully without error. The error does not become apparent until the token exchange fails." The agentIdentityBlueprint resource exposes the full CRUD for these credentials, including an upsert.
Read together, this means an agent that already carries a SPIFFE-style workload identity or any OIDC token from its runtime can exchange it for an Entra exchange token, then walk the two hops above. We covered the open-standards version of that exchange in our WIMSE and SPIFFE audit; Entra's version is proprietary at the fmi_path hop but standard RFC 7523 everywhere else.
The guardrails Microsoft hard-codes
Two lists in the documentation say more about Microsoft's threat model than any overview page. The first is the set of Microsoft Graph permissions that "are explicitly blocked for agents to prevent misuse or unintended access to sensitive data" and "can't be granted to agent identities through Microsoft Graph or Microsoft Entra admin center." We counted 59 entries in the Graph overview table. The expected ones are there: Directory.ReadWrite.All, RoleManagement.ReadWrite.Directory, Application.ReadWrite.All, User.ReadWrite.All, and every permission that would let an agent mint more agents, from AgentIdentity.Create to AgentIdentityBlueprint.ReadWrite.All. Less expected are the data-plane blocks on application permissions: Files.Read.All, Files.ReadWrite.All, Sites.Read.All, Calendars.Read, Chat.Read.All and ChannelMessage.Read.All. An autonomous agent identity cannot be granted tenant-wide read over files, sites, calendars or chat. It has to go through a user's delegated grant or through an agent user account that is licensed and scoped like a person. Requesting a blocked permission in requiredResourceAccess "is rejected with an HTTP 400 Bad Request response."
The second list is the "patterns to avoid" section of the design patterns page, which reads like a set of scaling limits stated as advice. "Scale-out replicas don't need separate agent identities": multiple instances of the same code "can all run under the same identity simultaneously." "Memory and context management don't require separate agent identities." And the one that matters for high-cardinality systems: "Creating one agent identity per meeting, per document, or per ephemeral object at high volume isn't practical with directory-level identities today." The page does offer an ephemeral-identity variant, where an orchestrator creates a child at runtime and deletes it when the session ends, but notes that "ephemeral agent identity creation happens at runtime, which introduces nondeterministic latency." Agent identities are directory objects. They are cheap compared with app registrations, not compared with a signature.
An MCP server behind Entra, as of September 2026
The newest document in the set is the one most likely to be used. The how-to Secure a Model Context Protocol (MCP) server with Microsoft Entra ID carries an ms.date of September 1, 2026 and was merged on September 3 in commit 2506cb0. It maps the MCP authorization model onto Entra in the way we described in our MCP authorization deep dive: the MCP server is the protected resource, Entra is the authorization server, the agent is the client, and the client names the resource with RFC 8707's resource parameter.
The operational content is a single exact-match rule and its consequences. "Microsoft Entra ID compares that resource value against the Application ID URI (also called the identifier URI) configured on your MCP server's app registration. If they match, Microsoft Entra ID issues a token whose audience (aud) claim is your MCP server. If they don't match, the token request fails." To register an HTTPS Application ID URI that equals the server's URL, the app must first set requestedAccessTokenVersion to 2; the how-to calls this "the step that most people miss." The error for a mismatch is AADSTS9010010, and the first listed cause is a trailing slash: "because an Application ID URI can't end with a slash, a client that sends resource=https://mcp.contoso.com/ never matches the registered https://mcp.contoso.com."
The protected resource metadata document is spelled out too, and it must point at the v2.0 issuer:
# https://mcp.contoso.com/.well-known/oauth-protected-resource
{
"resource": "https://mcp.contoso.com",
"authorization_servers": [
"https://login.microsoftonline.com/<tenant-id>/v2.0"
],
"scopes_supported": ["tools.read", "tools.execute"],
"bearer_methods_supported": ["header"]
}
# and on an unauthenticated request
HTTP/1.1 401 Unauthorized
WWW-Authenticate: Bearer resource_metadata="https://mcp.contoso.com/.well-known/oauth-protected-resource", scope="tools.execute"
For agents, the how-to says to define app roles with allowed member type Applications, assign them to the agent identity, and have the client send a client-credentials request with scope=https://mcp.contoso.com/.default; the roles come back in the roles claim. For servers that cannot move off v1 tokens, it documents the aud optional claim with use_guid, which forces a GUID audience and relaxes the identifier URI restriction. Notably, the document does not mention Client ID Metadata Documents or dynamic client registration at all: on Entra, the MCP client is an app registration or an agent identity that already exists in the tenant. That is the opposite premise from the one we examined in our CIMD audit, where the client identifies itself by URL and the server has never seen it before. Entra's other MCP answer, the enterprise-managed authorization profile that swaps consent screens for ID-JAG assertions, is the subject of a previous post.
What it costs and who it is for
The licensing language is repeated on four pages and worth quoting exactly, because the split is not obvious. "Agent ID is available for all Microsoft Entra customers." But "extending Microsoft Entra security features to agents requires Microsoft Agent 365", which "is included with Microsoft 365 E7 and is available as an add-on to Microsoft E5/A5/Business Premium." The Conditional Access page is narrower still: "Conditional Access for agents requires Microsoft Entra ID P1 or P2 and a Microsoft Agent 365 license for each user. Enforcement of Agent 365 licensing is coming soon." The identity object is free; the policy engine around it is a per-user SKU.
The governance model reflects the buyer. Every blueprint and identity has owners, who are technical administrators, and sponsors, who "provide business accountability for agents, making lifecycle decisions without technical administrative access." Sponsors are "required during the create operation" on a blueprint, and lifecycle workflow templates exist "to prevent orphaned agents" when a sponsor leaves. Access packages, access reviews and risk detections all extend to agent identities. This is an identity system designed for an auditor to ask "who is responsible for this agent" and get a human name back.
What it means for LLM4Agents
LLM4Agents issues agents a wallet and a gateway key, then lets them pay per request in stablecoins over x402. Entra Agent ID issues agents a directory object and a token, then lets them act inside a tenant under policy. The two systems answer different questions, and the interesting part is where they meet.
The first meeting point is inbound. An enterprise agent with an Entra identity that calls LLM4Agents today presents a gateway key, which is exactly the "API key bypasses Conditional Access" case the Microsoft docs describe. If the gateway accepted an Entra-issued bearer token for a registered resource, the customer's Conditional Access policy would gate model calls the way it gates Graph calls, and our sign-in logs would inherit their xms_par_app_azp lineage. The token shape is fully specified: RS256 against the tenant JWKS, issuer, audience, then the marker and facet claims. The blocked-permissions list does not apply to third-party resources, so an LLM4Agents app role on an agent identity is allowed. What the platform gains is a customer-controlled kill switch: disable the blueprint, and every agent under it stops being able to mint a token for us.
The second meeting point is outbound and is the weaker fit. Nothing in this document set touches payment. Agent identities are tenant-bound; the reference says "agent identities can't access resources outside their assigned customer tenant." A token that cannot leave the tenant cannot serve as a machine-to-machine identity across the open web, and it carries no spending authority. The blueprint's directory-level cost model also runs against per-task identities. For the open counterpart, agent identity that a stranger can verify and that binds to a wallet, the relevant specs remain the ones in our ERC-8004 audit and Web Bot Auth, and the threat categories in our 2026 threat model are unchanged by Entra's existence.
The third meeting point is the MCP surface. LLM4Agents exposes tools over MCP. The September how-to tells every Entra tenant exactly how to demand a v2 token with an exact resource match and a PRM document at the well-known path. If a customer's agents run with Entra identities, the MCP endpoints they will trust are the ones that speak that dialect.
Staying on the frontier
The steps below are ordered by how much they unlock relative to what they cost.
First, accept Entra agent tokens at the gateway as a second credential type. Register LLM4Agents as a multitenant resource with an HTTPS Application ID URI, request v2 tokens, and validate the four checks the downstream-API how-to prescribes. Log xms_par_app_azp, xms_act_fct and xms_sub_fct on every request but do not authorize on the parent, per Microsoft's own guidance. Map a validated agent identity to an existing LLM4Agents agent record so billing stays per-agent.
Second, publish protected resource metadata for the MCP server with Entra listed as one of several authorization servers, and add an Entra-flavored test to the MCP conformance suite: trailing-slash resource values, v1 versus v2 audience, app-role roles claim for client-credentials callers. The how-to gives the exact failure codes to assert on.
Third, document the federation path for the other direction. An LLM4Agents-hosted agent that needs to reach a customer's Graph or MCP servers can be given an Entra blueprint whose federated identity credential trusts an OIDC token we issue, with audience api://AzureADTokenExchange. That is the pattern Microsoft documents for AWS and GCP, and there is no reason a third runtime cannot be the issuer. The sidecar image can run in the agent's sandbox as a sibling container, since it needs no host port.
Fourth, keep the wallet separate from the directory. The right design is an Entra identity that authorizes the agent to act inside a tenant and an x402-bound wallet that lets the same agent pay outside it, linked by our own record rather than by any claim Entra emits. Spend caps, the subject of our spend-controls audit, stay on the payment side.
Fifth, watch three moving parts. The registry Graph API is being replaced by an Agent 365 API with a re-registration requirement, and the replacement had no published date when we read the page. The Conditional Access licensing enforcement is "coming soon". And the discrepancy between the optional-claims wording and the presence-guaranteed wording for xms_par_app_azp should be settled by testing against a real tenant before any code depends on it.
Give your agents a wallet, not just a directory entry
LLM4Agents registers agents, funds them in stablecoins and bills them per request over an OpenAI-compatible gateway.
Register an agent