<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Anton Staykov</title>
    <description>The latest articles on DEV Community by Anton Staykov (@astaykov).</description>
    <link>https://dev.to/astaykov</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3827606%2Fbb3aa4b8-90b5-4db5-8ad5-f9bd05ab762b.jpg</url>
      <title>DEV Community: Anton Staykov</title>
      <link>https://dev.to/astaykov</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/astaykov"/>
    <language>en</language>
    <item>
      <title>Authorization options for your AI agents with Entra Agent ID</title>
      <dc:creator>Anton Staykov</dc:creator>
      <pubDate>Thu, 27 Aug 2026 09:43:00 +0000</pubDate>
      <link>https://dev.to/astaykov/authorization-options-for-your-ai-agents-with-entra-agent-id-4p0k</link>
      <guid>https://dev.to/astaykov/authorization-options-for-your-ai-agents-with-entra-agent-id-4p0k</guid>
      <description>&lt;p&gt;Authorization in Entra has changed, and most of that change is not visible in the product UI. The boundaries have always been there; they are just no longer optional side-quests. When you architect an Entra Agent ID, you are not choosing &lt;em&gt;one&lt;/em&gt; authorization mechanism. You are composing five of them, each with its own trust surface. Understanding that composition is the difference between a loosely guarded identity and one that respects the principle of least privilege.&lt;/p&gt;

&lt;p&gt;Consent is authorization, not authentication. But consent is only the start. After you have granted the app permission to call Microsoft Graph, the hard work is to ensure it calls Graph with the minimum scope it actually needs, at the exact moment it needs it, and only on behalf of the principals you intended. That is what this article addresses.&lt;/p&gt;

&lt;h2&gt;
  
  
  User assignment as your outermost gate
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://learn.microsoft.com/en-us/azure/active-directory/manage-apps/assign-user-or-group-access-portal" rel="noopener noreferrer"&gt;User assignment&lt;/a&gt; is the first boundary you should establish. It is blunt, but it is not optional. When you require user or group assignment to an enterprise application, you are saying: "Only the principals I explicitly list in this Entra app object can even attempt to authenticate to this workload."&lt;/p&gt;

&lt;p&gt;For user-facing applications, this is straightforward. You assign Alice and Bob to the app, and only Alice and Bob can sign in.&lt;/p&gt;

&lt;p&gt;For an AI agent, user assignment works similarly. You do not assign the agent's identity to an app. Instead, you assign the &lt;em&gt;users&lt;/em&gt; who are allowed to delegate to or authenticate to the agent. In other words that assignment controls which users can invoke the agent or have the agent act on their behalf.&lt;/p&gt;

&lt;p&gt;Here is the critical part: user assignment gates access at two levels. First, it controls which users can authenticate to the agent itself. Second, and less obvious, it also gates downstream resource access. If a user is not explicitly assigned to a downstream API or resource, the agent cannot access that resource on behalf of that user—even if the agent's identity is assigned to that API. User assignment is not negotiable at any layer of the call chain.&lt;/p&gt;

&lt;p&gt;If you do not require assignment, Entra will still respect the OAuth 2.0 consent framework. However, it will allow any user in your tenant (or any user in a federated tenant, if you have configured cross-tenant access) to attempt authentication. The app sees the request, asks for consent, the user grants it (or the admin pre-consents), and the app gets a token. User assignment closes that loophole. It says: "No request reaches this workload unless the requester is already in my approved list—and no downstream resource request on behalf of that user will succeed if the user is not approved there either."&lt;/p&gt;

&lt;p&gt;For AI agents deployed at scale across teams or business units, user assignment is your guard rail. Without it, you are depending on the app and downstream APIs to reject requests from unknown principals. The better defense is to never admit an unapproved principal to the conversation in the first place, and to enforce that control consistently across all resources the agent might access.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Important:&lt;/strong&gt; &lt;a href="https://learn.microsoft.com/en-us/entra/identity-platform/howto-restrict-your-app-to-a-set-of-users" rel="noopener noreferrer"&gt;User assignment requirement&lt;/a&gt; works for both Agent Identity Blueprints and individual agent identities. However, there is currently no Entra Admin Center UX that exposes user assignment functionality — only the Microsoft Graph API. For practical user assignment at scale, consider using &lt;a href="https://dev.to/astaykov/access-packages-are-the-control-plane-for-entra-agent-id-permissions-4kph"&gt;Access Packages&lt;/a&gt; to facilitate and manage user assignments for AI agents. Another limitation worth mentioning is that at time of writing of this article, only Agent Identity Blueprints support custom roles definitions.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conditional access as authorization policy
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://learn.microsoft.com/en-us/entra/identity/conditional-access/agent-id" rel="noopener noreferrer"&gt;Conditional access&lt;/a&gt; is a policy engine that controls token acquisition. Unlike app roles or group claims, which are attributes embedded in a token, conditional access is evaluated &lt;em&gt;before&lt;/em&gt; the token is issued. If a policy is violated, the agent does not get a token.&lt;/p&gt;

&lt;p&gt;Here are concrete examples of conditional access for AI agents:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Custom Security Attribute filters:&lt;/strong&gt; "If the app is tagged with &lt;code&gt;Agents.Approved:True&lt;/code&gt; then allow access." The agent cannot acquire a token unless it is tagged as approved.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Risk-based controls:&lt;/strong&gt; "If Entra's identity protection detects a risky sign-in pattern, require additional verification or deny access." In on-behalf-of type of acces, the user's authentication context and user's offline calculated risks are evaluated (unusual location, impossible travel, leaked credentials, etc.), and access is blocked if that evaluation crosses threshold. In autonomous agent access the agent's risk is evaluated and access is blocked if agent's risk crosses defined threshold. &lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Device compliance:&lt;/strong&gt; "The device must be compliant with Intune policies." If the user is invoking the AI Agents from a device that is not enrolled or is marked non-compliant, the request is blocked.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Conditional access distinguishes two data access patterns that are critical for AI agents:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Autonomous access:&lt;/strong&gt; The agent acts on its own behalf (application flow). Conditional access evaluates the agent's context (where it is running, what device it is on, what time it is).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;On-behalf-of (OBO) user access:&lt;/strong&gt; The agent acts on behalf of a user. Conditional access evaluates &lt;em&gt;the user's&lt;/em&gt; context (where the user is, their device, their authentication method), not the agent's context.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Critical limitation:&lt;/strong&gt; At the time of writing, Conditional Access accounts for the &lt;em&gt;User&lt;/em&gt; (subject) and &lt;em&gt;Target&lt;/em&gt; (resource), but not the &lt;em&gt;AI Agent&lt;/em&gt; (actor) - when the agents accesses data on-behalf-of the user. This means CA policies cannot directly distinguish between two different agents making requests in the same context - the same user is invoking two different agents, and both happen to require a token to access Work IQ, the conditional access sees these requests identically. &lt;/p&gt;

&lt;p&gt;See &lt;a href="https://learn.microsoft.com/en-us/entra/identity/conditional-access/agent-id" rel="noopener noreferrer"&gt;Conditional Access for Agents in Microsoft Entra&lt;/a&gt; and &lt;a href="https://learn.microsoft.com/en-us/entra/identity/conditional-access/policy-autonomous-agents?tabs=use-the-enhanced-object-picker" rel="noopener noreferrer"&gt;Recommended policies for autonomous agents&lt;/a&gt; for detailed policy patterns.&lt;/p&gt;

&lt;p&gt;The sharp edge is timing. Conditional access gates token acquisition. If your agent acquires a long-lived token and then starts running outside the approved perimeter, the existing token still works. Conditional access is a gate at the door, not a checkpoint at every step. If you need continuous context verification, you need a different mechanism.&lt;/p&gt;

&lt;h2&gt;
  
  
  App roles as delegated authorization claims
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://learn.microsoft.com/en-us/azure/active-directory/develop/howto-add-app-roles-in-apps" rel="noopener noreferrer"&gt;App roles&lt;/a&gt; are a mechanism for the application, or the AI agent, to define its own authorization model. You define a set of roles in your app registration (or Agent Identity Blueprint). You assign users, groups, or service principals to those roles. When the principal authenticates, Entra puts the role membership in the &lt;code&gt;roles&lt;/code&gt; claim of the ID token (or the access token, depending on how you configure the app).&lt;/p&gt;

&lt;p&gt;For a human-facing app, this is straightforward. Your app checks: "Is the user in the &lt;code&gt;Manager&lt;/code&gt; role? If yes, show the budget page. If no, hide it." Entra does the assignment; your app does the enforcement.&lt;/p&gt;

&lt;p&gt;For an AI agent, the model is the same. You can assign humen to a role defined by the Agent Blueprint. In this case, your AI Agent can take authorization decisions for what funcitonality should be avialable to the user. Or, in case of the autonomous agent, you assign the agent's identity to a role defined by downstream API. Then, when your agent acquires a token on behalf of itself (application flow), that token contains the roles assigned to the service principal. Your APIs can inspect the token and make authorization decisions.&lt;/p&gt;

&lt;p&gt;Here is an example. You have an agent that needs to read data from one API and another agent that needs to read and write. You define two app roles in the API app registration: &lt;code&gt;DataReader&lt;/code&gt; and &lt;code&gt;DataWriter&lt;/code&gt;. You assign the read-only agent's service principal to the &lt;code&gt;DataReader&lt;/code&gt; role. You assign the read-write agent to the &lt;code&gt;DataWriter&lt;/code&gt; role. When each agent calls your API, your API code checks the &lt;code&gt;roles&lt;/code&gt; claim. If the token does not include &lt;code&gt;DataWriter&lt;/code&gt;, the write operation fails.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Current UX limitations:&lt;/strong&gt; The Entra Admin Center does not directly expose a UI for defining roles on Agent Identity Blueprints. Roles can be defined and managed via the &lt;a href="https://learn.microsoft.com/en-us/entra/identity-platform/reference-app-manifest" rel="noopener noreferrer"&gt;app manifest&lt;/a&gt;. Role assignment to users and groups can be done via the Microsoft Graph API or, more scalably, via &lt;a href="https://dev.to/astaykov/access-packages-are-the-control-plane-for-entra-agent-id-permissions-4kph"&gt;Access Packages&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Important distinction:&lt;/strong&gt; While the "require user assignment" property exists for Agent Identities, there is no real application scenario where interactive authentication to an Agent happens outside of the Agent Identity Blueprint. The Agent Identity Blueprint is the object that defines the authentication and authorization contract for interactive authentications. The way to add role assignments for an Agent Identity Blueprint is via Access Packages (Add Applications). Requiring explicit role assignment for individual agent identities makes sense in an autonomous agent-to-agent interactions where you want strict controls over the autonomous invokation of AI Agents.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;App-only roles:&lt;/strong&gt; App-only roles are defined on the Blueprint and can be assigned to other agents or clients. When assigned to other workloads, these roles appear in the access token with the Blueprint as the audience (&lt;code&gt;aud&lt;/code&gt; claim). This allows fine-grained authorization delegation between agents.&lt;/p&gt;

&lt;p&gt;The important detail is that &lt;a href="https://learn.microsoft.com/en-us/azure/active-directory/develop/scenario-protected-web-api-app-configuration" rel="noopener noreferrer"&gt;your protected API must be configured to emit roles in the access token&lt;/a&gt;. This requires a specific configuration step. Many teams forget it. The app roles exist, the assignment exists, but the token arrives empty because the app registration does not emit roles into tokens meant for APIs.&lt;/p&gt;

&lt;p&gt;Also note: app roles are defined per app registration, not globally. If you have two APIs, each needs its own set of roles. There is no central app role store.&lt;/p&gt;

&lt;h2&gt;
  
  
  Group membership claims as attribute-based gates
&lt;/h2&gt;

&lt;p&gt;Group membership claims are different from app roles. When you &lt;a href="https://learn.microsoft.com/en-us/entra/identity/conditional-access/policy-autonomous-agents" rel="noopener noreferrer"&gt;emit group claims&lt;/a&gt;, Entra places the groups that the principal belongs to directly into the token.&lt;/p&gt;

&lt;p&gt;Here is the distinction. App roles are defined by the application and assigned by Entra admins within the scope of that app registration. Group claims are based on the broader Entra directory. The principal is a member of the group regardless of the app. The app just asks: "What groups is this principal a member of?" Entra answers by listing them in the token.&lt;/p&gt;

&lt;p&gt;This is useful for attribute-based access control (ABAC). Imagine your organization has groups like &lt;code&gt;Sales-Americas&lt;/code&gt;, &lt;code&gt;Sales-EMEA&lt;/code&gt;, &lt;code&gt;Engineering&lt;/code&gt;, and &lt;code&gt;Finance&lt;/code&gt;. You assign users and workload identities to these groups. Your API receives a token, extracts the &lt;code&gt;groups&lt;/code&gt; claim, and makes decisions based on group membership. "If the token contains the &lt;code&gt;Sales-Americas&lt;/code&gt; group, grant access to the Americas sales data. Otherwise, deny."&lt;/p&gt;

&lt;p&gt;The value of group-based authorization is that it decouples application-specific roles from organizational structure. If you need to add a new group or restructure existing groups, you do not need to change app role definitions. You adjust group membership. The group claim mechanism is still sending the data; your API just interprets it differently.&lt;/p&gt;

&lt;p&gt;There is a configuration step here too. You must tell Entra to emit group claims into tokens for a specific app. By default, groups are not included. Once enabled, Entra sends the group IDs (and optionally display names, but prefer IDs for immutability).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Important: Overage limits and scope configuration.&lt;/strong&gt; When you enable group claims, you must configure scope carefully. Entra has fixed limits on how many group claims can fit in a token:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;OIDC (OpenID Connect):&lt;/strong&gt; 200 groups maximum&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SAML:&lt;/strong&gt; 150 groups maximum&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If a principal belongs to more groups than the limit, Entra sends an "overage claim" instead of individual group IDs. Your API then needs to call Microsoft Graph to fetch the full list. This adds latency and complexity. To avoid overage, &lt;strong&gt;emit only "assigned groups"&lt;/strong&gt; (groups explicitly assigned to the application / blueprint). Be explicit about which groups matter for authorization decisions, and assign only those groups to the target app or agent identity blueprint. This keeps tokens small, predictable, and avoids the overage claim penalty.&lt;/p&gt;

&lt;p&gt;See &lt;a href="https://learn.microsoft.com/en-us/security/zero-trust/develop/configure-tokens-group-claims-app-roles" rel="noopener noreferrer"&gt;Configure group claims and app roles in tokens&lt;/a&gt; for detailed guidance on group claim scope and immutable identifiers.&lt;/p&gt;

&lt;h2&gt;
  
  
  Access packages as the unified control plane
&lt;/h2&gt;

&lt;p&gt;Access packages are where the other mechanisms converge. An &lt;a href="https://learn.microsoft.com/en-us/azure/active-directory/governance/access-packages-create-lifecycle-policies" rel="noopener noreferrer"&gt;access package&lt;/a&gt; is not just governance UI. It is programmable policy choreography. When you assign a principal to an access package, you can configure what happens automatically: group membership additions, app role assignments, Azure role assignments, and so on. All of it coordinated, all of it time-bounded, all of it auditable.&lt;/p&gt;

&lt;p&gt;For an AI agent, access packages are a control plane. You define an access package called "Agent-Production-Access". When you assign an agent's service principal to that package, Entra automatically adds the service principal to the groups that the agent needs, assigns the correct app roles to the agent, and even delegated permissions grants (consent) for downstream APIs and MCP Servers. When the access expires, all of it unwinds. The groups are removed. The app roles are stripped, the consent is undone. The agent loses access.&lt;/p&gt;

&lt;p&gt;This is different from manually assigning groups and roles one by one or going through admin cosnent workflow. With access packages, you are declaring a coherent policy: "This is what production access means for this type of workload. If the workload is approved, give it everything in this package. If it is denied or if the time expires, take everything away." The access package is a unit of authorization. It is atomic in the sense that you do not pick and choose which parts of it to apply. It is an all-or-nothing proposition.&lt;/p&gt;

&lt;p&gt;For enterprises running multiple AI agents, access packages scale the authorization story. You create one access package per authorization context (production, staging, read-only partners, etc.). You assign agents to packages based on their needs. Governance reviews the packages on a schedule and approves or denies. The machinery runs itself.&lt;/p&gt;

&lt;p&gt;There is also integration with approval workflows. An access package can require approval before assignment. You can route approvals to a specific manager or group. You can set expiration dates and schedule re-approval. For AI agents running in production, this is the audit trail that compliance teams expect. "Show me who approved this agent to have access to production data and when that approval expires."&lt;/p&gt;

&lt;h2&gt;
  
  
  Best practices on group emit scope and immutable identifiers
&lt;/h2&gt;

&lt;p&gt;When you enable group claims, you need to decide: should Entra emit &lt;em&gt;all&lt;/em&gt; groups the principal is a member of, or only &lt;em&gt;assigned&lt;/em&gt; groups?&lt;/p&gt;

&lt;p&gt;The answer is clear: &lt;strong&gt;use only groups assigned to the application&lt;/strong&gt;. In a typical enterprise environment, grown over the yers, single user can be member of several thousands of groups. You cannot have a token with six thousand groups in it. And it makes no sense to go over six thousand groups to make authorization decision. Choosing "assigned groups" is tighter and mandatory for production. You explicitly assign the groups relevant for this app to that app. Other groups the principal happens to belong to stay out of the token. This keeps tokens predictable, avoids overage claims, and simplifies authorization logic.&lt;/p&gt;

&lt;p&gt;Immutable identifiers are a separate but equally important consideration. Entra can emit group display names or group IDs in the token. Display names are human-readable but fragile. If a group is renamed, the token still carries the old display name until the token expires. Your API might silently fail to recognize the group and deny access. Or worse - someone may create a group with name that they will grant them access to the app. Group IDs are immutable. A group ID never changes, even if the display name changes. For production authorization logic, prefer group IDs. Your API logic becomes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;if token.groups contains "80d61f4e-4c9e-4f8f-9e1f-2e8f3d5c6b7a":
  grant access
else:
  deny
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Not:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;if token.groups contains "Sales-Americas":
  grant access
else:
  deny
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The first version survives a group rename. The second does not. For large organizations managing many agents and APIs, using immutable group IDs and assigned-only scope is the only way to keep authorization logic maintainable and reliable at scale.&lt;/p&gt;

&lt;h2&gt;
  
  
  Composing the authorization stack
&lt;/h2&gt;

&lt;p&gt;These mechanisms do not compete. They stack. Here is a realistic composition:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;a href="https://docs.azure.cn/en-us/entra/identity/enterprise-apps/what-is-access-management#requiring-user-assignment-for-an-app" rel="noopener noreferrer"&gt;Requiring explicit assignment&lt;/a&gt; gates whether the principal can even attempt authentication.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://learn.microsoft.com/en-us/entra/identity/conditional-access/overview" rel="noopener noreferrer"&gt;Conditional access policies&lt;/a&gt; check location, device, and other context.&lt;/li&gt;
&lt;li&gt;The token arrives with &lt;a href="https://learn.microsoft.com/en-us/entra/identity-platform/howto-add-app-roles-in-apps" rel="noopener noreferrer"&gt;app roles&lt;/a&gt; and &lt;a href="https://learn.microsoft.com/en-us/entra/identity/hybrid/connect/how-to-connect-fed-group-claims" rel="noopener noreferrer"&gt;group claims&lt;/a&gt; embedded.&lt;/li&gt;
&lt;li&gt;Your API inspects the token, checks app roles and groups, and makes fine-grained decisions.&lt;/li&gt;
&lt;li&gt;All of this is orchestrated through an &lt;a href="https://learn.microsoft.com/en-us/entra/agent-id/agent-access-packages" rel="noopener noreferrer"&gt;access package&lt;/a&gt; that defines the coherent policy for a given context (production, partner, read-only, etc.).&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Each layer has a different trust surface. User assignment is coarse. Conditional access is context-based. App roles and groups are attribute-based. Access packages are policy-coordinated. Together, they form a defense in depth.&lt;/p&gt;

&lt;p&gt;When you are designing authorization for an AI agent, ask yourself:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Should this agent be able to authenticate at all right now? (User assignment)&lt;/li&gt;
&lt;li&gt;Under what conditions (location, device, time) should token acquisition be allowed? (Conditional access)&lt;/li&gt;
&lt;li&gt;If the token is issued, what roles and group memberships should it carry? (App roles and group claims)&lt;/li&gt;
&lt;li&gt;How is the agent's access provisioned, reviewed, and revoked? (Access packages and lifecycle management)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you can answer each question with a specific, enforceable policy, you have authorization architecture. If you are relying on the API layer alone to deny requests, you are carrying unnecessary risk.&lt;/p&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/en-us/azure/active-directory/manage-apps/assign-user-or-group-access-portal" rel="noopener noreferrer"&gt;User assignment to enterprise applications&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/en-us/entra/identity/conditional-access/agent-id" rel="noopener noreferrer"&gt;Conditional Access for Agents in Microsoft Entra&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/en-us/entra/identity/conditional-access/policy-autonomous-agents?tabs=use-the-enhanced-object-picker" rel="noopener noreferrer"&gt;Recommended policies for autonomous agents in Microsoft Entra&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/en-us/azure/active-directory/develop/howto-add-app-roles-in-apps" rel="noopener noreferrer"&gt;How to add app roles in apps&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/en-us/entra/identity-platform/reference-app-manifest" rel="noopener noreferrer"&gt;Understanding the app manifest&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/en-us/azure/active-directory/develop/scenario-protected-web-api-app-configuration" rel="noopener noreferrer"&gt;Protected Web API app configuration&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/en-us/security/zero-trust/develop/configure-tokens-group-claims-app-roles" rel="noopener noreferrer"&gt;Configure group claims and app roles in tokens&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/en-us/azure/active-directory/develop/how-to-emit-saml-claims-based-on-application-roles" rel="noopener noreferrer"&gt;How to emit SAML claims based on application roles&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>entraagentid</category>
      <category>identitygovernance</category>
      <category>microsoftentra</category>
      <category>aiagents</category>
    </item>
    <item>
      <title>Flexibility with admin consent in Entra: how to scale consent flows for AI agents</title>
      <dc:creator>Anton Staykov</dc:creator>
      <pubDate>Tue, 25 Aug 2026 11:43:00 +0000</pubDate>
      <link>https://dev.to/astaykov/flexibility-with-admin-consent-in-entra-how-to-scale-consent-flows-for-ai-agents-2h1p</link>
      <guid>https://dev.to/astaykov/flexibility-with-admin-consent-in-entra-how-to-scale-consent-flows-for-ai-agents-2h1p</guid>
      <description>&lt;p&gt;Most organizations treat tenant-wide admin consent in Microsoft Entra as a binary decision. Either a central identity team approves every request, or somebody receives a broad administrative role so the queue can move faster.&lt;/p&gt;

&lt;p&gt;That is a false choice.&lt;/p&gt;

&lt;p&gt;Microsoft Entra supports a more precise control model: define which consent requests are acceptable in an app consent policy, bind that policy to a custom directory role, and delegate only the authority to approve requests inside that envelope. The role action that makes this possible is &lt;a href="https://learn.microsoft.com/entra/identity/role-based-access-control/custom-consent-permissions#granting-permissions-to-apps-on-behalf-of-all-admin-consent" rel="noopener noreferrer"&gt;&lt;code&gt;microsoft.directory/servicePrincipals/managePermissionGrantsForAll.{id}&lt;/code&gt;&lt;/a&gt;, where &lt;code&gt;{id}&lt;/code&gt; identifies the policy that constrains the consent authority.&lt;/p&gt;

&lt;p&gt;That is not a smaller version of Global Administrator. It is a different architectural idea: &lt;em&gt;delegate the decision without delegating the directory&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;AI agents make this distinction urgent. As organizations create more agent identities and authorize them against Microsoft Graph and other APIs, consent stops being an occasional setup task. It becomes a recurring control-plane operation. Scaling that operation with broad roles is the identity equivalent of solving traffic congestion by issuing everyone a master key.&lt;/p&gt;

&lt;h2&gt;
  
  
  The usual admin consent model does not scale
&lt;/h2&gt;

&lt;p&gt;Tenant-wide admin consent is deliberately sensitive. A grant can authorize an application to access organizational data or perform privileged operations across the tenant, so Microsoft recommends reviewing the requested permissions carefully before granting it (&lt;a href="https://learn.microsoft.com/entra/identity/enterprise-apps/grant-admin-consent" rel="noopener noreferrer"&gt;grant tenant-wide admin consent&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;The operational response is usually centralization. Every request lands with a small identity or security team. That team validates the application, checks the requested permissions, finds the business owner, and either approves or rejects the request.&lt;/p&gt;

&lt;p&gt;Centralization gives you consistency, but it also creates a queue. The queue grows with every SaaS integration, internal application, automation workload, and AI agent. Eventually the organization reaches for a built-in role such as Application Administrator or Cloud Application Administrator to distribute the work.&lt;/p&gt;

&lt;p&gt;That relieves the queue by expanding authority. It does not make the consent decision more precise.&lt;/p&gt;

&lt;p&gt;Microsoft Entra already has a control surface for that precision. It is just underused.&lt;/p&gt;

&lt;h2&gt;
  
  
  An app consent policy is a permission envelope
&lt;/h2&gt;

&lt;p&gt;An &lt;a href="https://learn.microsoft.com/entra/identity/enterprise-apps/manage-app-consent-policies" rel="noopener noreferrer"&gt;app consent policy&lt;/a&gt; describes the conditions under which a consent event is allowed. A policy contains one or more include condition sets and can contain exclude condition sets. A request must match at least one include condition set and must not match any exclude condition set for the policy to pass.&lt;/p&gt;

&lt;p&gt;That evaluation model matters. You are not handing a delegate a list of instructions and hoping they follow it. You are placing a policy boundary in the authorization path.&lt;/p&gt;

&lt;p&gt;The condition sets can describe characteristics such as the permission type, the resource application, specific permissions, client applications, and publisher status (&lt;a href="https://learn.microsoft.com/graph/api/resources/permissiongrantconditionset?view=graph-rest-1.0" rel="noopener noreferrer"&gt;permission grant condition set resource&lt;/a&gt;). This lets an organization express consent envelopes such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;delegated &lt;code&gt;Mail.Read&lt;/code&gt; and &lt;code&gt;Files.Read&lt;/code&gt; permissions for Microsoft Graph,&lt;/li&gt;
&lt;li&gt;selected application permissions exposed by one internal finance API,&lt;/li&gt;
&lt;li&gt;permissions requested only by explicitly identified client applications,&lt;/li&gt;
&lt;li&gt;delegated permissions from verified publishers, with sensitive resources excluded.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The important word is &lt;em&gt;selected&lt;/em&gt;. The policy does not need to authorize every permission exposed by an API. It can describe the exact delegated scopes and application roles the organization has decided are appropriate for this delegation model (&lt;a href="https://learn.microsoft.com/entra/identity/enterprise-apps/manage-app-consent-policies#app-consent-policy-segments" rel="noopener noreferrer"&gt;manage app consent policies&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;Today, Microsoft exposes this policy model through Microsoft Graph. The relevant resources are &lt;code&gt;permissionGrantPolicy&lt;/code&gt; and &lt;code&gt;permissionGrantConditionSet&lt;/code&gt;, exposed under &lt;code&gt;/policies/permissionGrantPolicies&lt;/code&gt; (&lt;a href="https://learn.microsoft.com/entra/identity/enterprise-apps/manage-app-consent-policies#manage-app-consent-policies" rel="noopener noreferrer"&gt;manage app consent policies with Microsoft Graph&lt;/a&gt;).&lt;/p&gt;

&lt;h2&gt;
  
  
  The custom role binds authority to the envelope
&lt;/h2&gt;

&lt;p&gt;The policy defines what may be approved. The custom role defines who may approve it.&lt;/p&gt;

&lt;p&gt;Microsoft documents a specific custom-role permission for tenant-wide admin consent:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;microsoft.directory/servicePrincipals/managePermissionGrantsForAll.{id}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;{id}&lt;/code&gt; suffix is not decoration. It binds the role permission to an app consent policy. The role assignee can grant tenant-wide consent for delegated permissions and application permissions only when the consent request satisfies that policy (&lt;a href="https://learn.microsoft.com/entra/identity/role-based-access-control/custom-consent-permissions#granting-permissions-to-apps-on-behalf-of-all-admin-consent" rel="noopener noreferrer"&gt;app consent permissions for custom roles&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;That gives architects a much better delegation primitive than "make this person a Cloud Application Administrator." The delegate receives a defined consent capability, not general application administration authority.&lt;/p&gt;

&lt;p&gt;For example, a data platform team could receive authority to grant a fixed set of delegated scopes against an internal analytics API. A Microsoft 365 integration team could receive a different role tied to a policy for a small set of approved Graph permissions. Neither team needs a role whose authority extends across unrelated application-management operations.&lt;/p&gt;

&lt;p&gt;Custom roles and app consent permissions require Microsoft Entra ID P1. The Entra admin center does not currently support adding these app-consent permissions to a custom role definition, so this configuration is available through Microsoft Graph rather than the Entra admin center UX (&lt;a href="https://learn.microsoft.com/entra/identity/role-based-access-control/custom-consent-permissions" rel="noopener noreferrer"&gt;license and tooling requirements&lt;/a&gt;). That is implementation friction, but it does not weaken the control model.&lt;/p&gt;

&lt;h2&gt;
  
  
  Policy authorship and consent execution are different jobs
&lt;/h2&gt;

&lt;p&gt;The strongest version of this architecture separates two responsibilities.&lt;/p&gt;

&lt;p&gt;The central identity or security team authors the permission envelope. It decides which resource applications, delegated scopes, application roles, client properties, and exclusions belong in the policy. That team also controls the custom role definition and its assignments through the documented Microsoft Entra role-management interfaces (&lt;a href="https://learn.microsoft.com/entra/identity/role-based-access-control/custom-create" rel="noopener noreferrer"&gt;create a custom role&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;The delegated administrator evaluates individual requests and grants consent only when Entra determines that the request fits the referenced policy.&lt;/p&gt;

&lt;p&gt;Those are not the same privilege. Microsoft exposes separate custom-role permissions for creating, reading, updating, and deleting permission grant policies, distinct from &lt;code&gt;managePermissionGrantsForAll.{id}&lt;/code&gt; (&lt;a href="https://learn.microsoft.com/entra/identity/role-based-access-control/custom-consent-permissions#managing-app-consent-policies" rel="noopener noreferrer"&gt;managing app consent policies through custom roles&lt;/a&gt;). A consent delegate should not automatically receive policy-authoring permissions.&lt;/p&gt;

&lt;p&gt;That separation is the control.&lt;/p&gt;

&lt;p&gt;If the same operator can widen the allowlist and approve the newly allowed request, the policy becomes a speed bump. If one team defines the envelope and another operates inside it, the policy becomes an enforceable delegation boundary.&lt;/p&gt;

&lt;h2&gt;
  
  
  Watch for alternate paths around the policy
&lt;/h2&gt;

&lt;p&gt;There is one architectural trap worth stating plainly: app consent policies are not the only authorization mechanism that can allow a principal to grant permissions.&lt;/p&gt;

&lt;p&gt;Microsoft documents that policies are evaluated independently and that a principal needs only one authorization path that permits a consent event. Ownership of a resource service principal and broad Microsoft Graph application permissions can provide other ways to create grants in some scenarios (&lt;a href="https://learn.microsoft.com/entra/identity/enterprise-apps/manage-app-consent-policies#multiple-policies-or-authorization-mechanisms-to-grant-consent" rel="noopener noreferrer"&gt;multiple policies or authorization mechanisms&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;That means the restricted role is only as restricted as the delegate's &lt;em&gt;effective&lt;/em&gt; authority.&lt;/p&gt;

&lt;p&gt;Do not combine the policy-bound consent role with broad application-management permissions and then describe the result as least privilege. Review the delegate's other directory roles, Microsoft Graph permissions, ownership assignments, and group-derived access. The consent policy should be the intended authorization path, not one path among several wider ones.&lt;/p&gt;

&lt;p&gt;This is also why role naming matters. "Finance API Consent Approver" communicates a bounded function. "Application Admin Lite" invites future privilege creep. Names do not enforce security, but they tell future operators whether a role is a contract or a convenience.&lt;/p&gt;

&lt;h2&gt;
  
  
  A domain-based consent operating model
&lt;/h2&gt;

&lt;p&gt;The practical enterprise model is not one policy for every application and not one policy for the entire tenant. Both extremes create maintenance problems.&lt;/p&gt;

&lt;p&gt;Define envelopes around stable trust boundaries. A boundary might be a business domain, an API platform, a class of low-impact delegated permissions, or a controlled set of client applications. The condition-set model supports matching on multiple properties, so the policy can reflect more than a permission name alone (&lt;a href="https://learn.microsoft.com/graph/api/resources/permissiongrantconditionset?view=graph-rest-1.0" rel="noopener noreferrer"&gt;permission grant condition set properties&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;Then assign the corresponding custom role to administrators who understand that domain. They can evaluate business purpose and operational context without receiving unrelated directory authority.&lt;/p&gt;

&lt;p&gt;This changes the central identity team's job. It stops being the human endpoint for every consent request and becomes the designer of consent boundaries. The team owns policy quality, role lifecycle, exception handling, and periodic review. Domain administrators operate within the encoded boundary.&lt;/p&gt;

&lt;p&gt;The result is not decentralized consent without governance. It is &lt;em&gt;federated consent with a centrally defined authorization ceiling&lt;/em&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  AI agents turn a useful feature into an architectural requirement
&lt;/h2&gt;

&lt;p&gt;Microsoft Entra Agent ID supports delegated and application permission models for agents. Delegated authorization is represented by an &lt;code&gt;oAuth2PermissionGrant&lt;/code&gt;, while application authorization is represented by an &lt;code&gt;appRoleAssignment&lt;/code&gt;; Microsoft also documents direct creation of these objects and consent-based flows for granting agents access (&lt;a href="https://learn.microsoft.com/entra/agent-id/grant-agent-access-microsoft-365" rel="noopener noreferrer"&gt;grant agents access to Microsoft 365 resources&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;That means agent adoption feeds the same consent control plane already used for applications, but at a different operational scale. A blueprint may produce many agent identities, and different agents may need different access as they move from experimentation into production (&lt;a href="https://learn.microsoft.com/entra/agent-id/key-concepts" rel="noopener noreferrer"&gt;Agent ID key concepts&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;The wrong response is to multiply broadly privileged administrators at the same rate.&lt;/p&gt;

&lt;p&gt;The better response is to decide which permission combinations are routine, defensible, and delegable, then encode those combinations in consent policies. High-risk or unusual requests stay with the central authority. Known patterns move through policy-bound delegates.&lt;/p&gt;

&lt;p&gt;This complements the argument in &lt;a href="https://dev.to/astaykov/why-consent-is-the-trust-contract-for-entra-agent-id-c03"&gt;Why consent is the trust contract for Entra Agent ID&lt;/a&gt;. Consent still answers who authorized an agent, for which resource, and with what permissions. Granular delegation answers the next operational question: who is trusted to make that authorization decision without receiving power over the rest of the tenant?&lt;/p&gt;

&lt;p&gt;There is an important current boundary. Microsoft states that custom Microsoft Entra roles cannot be assigned to agent identities (&lt;a href="https://learn.microsoft.com/entra/agent-id/authorization-agent-id#microsoft-entra-role-assignments-for-agent-identities" rel="noopener noreferrer"&gt;Microsoft Entra role assignments for agent identities&lt;/a&gt;). This pattern therefore does not make an AI agent the holder of the restricted consent role. It makes the consent process around AI agents more scalable for supported human role assignees.&lt;/p&gt;

&lt;h2&gt;
  
  
  The question this architecture creates
&lt;/h2&gt;

&lt;p&gt;Once consent policy becomes an enforceable envelope, a more interesting question appears.&lt;/p&gt;

&lt;p&gt;What if an AI agent could assess a consent request, inspect the requested permissions and business context, compare the request with organizational policy, and recommend approval or rejection? What if, one day, the platform supported that agent making the decision inside a boundary it could not rewrite?&lt;/p&gt;

&lt;p&gt;That is not the capability described here. Current Agent IDs cannot hold custom Entra roles, and a probabilistic reasoning system should not be mistaken for an authorization boundary (&lt;a href="https://learn.microsoft.com/entra/agent-id/authorization-agent-id" rel="noopener noreferrer"&gt;Agent ID authorization restrictions&lt;/a&gt;). But granular consent delegation gives that future discussion a serious starting point. The policy remains deterministic. The permission ceiling remains centrally owned. The assessment could become intelligent.&lt;/p&gt;

&lt;p&gt;The immediate opportunity is less speculative and more valuable: stop treating admin consent as authority that can only be centralized or broadly delegated. Microsoft Entra already lets you define the exact consent envelope and hand out only the key that fits that lock.&lt;/p&gt;

&lt;p&gt;As AI agents increase the volume and variety of consent decisions, that flexibility stops being an obscure role-management feature. It becomes part of the architecture.&lt;/p&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/entra/identity/enterprise-apps/manage-app-consent-policies" rel="noopener noreferrer"&gt;Manage app consent policies&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/graph/api/resources/permissiongrantconditionset?view=graph-rest-1.0" rel="noopener noreferrer"&gt;Permission grant condition set resource type&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/entra/identity/role-based-access-control/custom-consent-permissions" rel="noopener noreferrer"&gt;App consent permissions for custom roles in Microsoft Entra ID&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/entra/identity/role-based-access-control/custom-create" rel="noopener noreferrer"&gt;Create a custom role in Microsoft Entra ID&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/entra/identity/enterprise-apps/grant-admin-consent" rel="noopener noreferrer"&gt;Grant tenant-wide admin consent to an application&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/graph/permissions-grant-via-msgraph" rel="noopener noreferrer"&gt;Grant or revoke API permissions programmatically&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/entra/agent-id/authorization-agent-id" rel="noopener noreferrer"&gt;Authorization in Microsoft Entra Agent ID&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/entra/agent-id/grant-agent-access-microsoft-365" rel="noopener noreferrer"&gt;Grant agents access to Microsoft 365 resources&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/entra/agent-id/key-concepts" rel="noopener noreferrer"&gt;Microsoft Entra Agent ID key concepts&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>aiagents</category>
      <category>zerotrust</category>
      <category>microsoftentra</category>
      <category>cloudsecurity</category>
    </item>
    <item>
      <title>Dynamic client registration and CIMD are not a trust framework</title>
      <dc:creator>Anton Staykov</dc:creator>
      <pubDate>Mon, 17 Aug 2026 10:37:00 +0000</pubDate>
      <link>https://dev.to/astaykov/dynamic-client-registration-and-cimd-are-not-a-trust-framework-fh7</link>
      <guid>https://dev.to/astaykov/dynamic-client-registration-and-cimd-are-not-a-trust-framework-fh7</guid>
      <description>&lt;p&gt;The MCP specification ships with a strong opinion about how AI clients should onboard with authorization servers: dynamically, at runtime, without pre-coordination (&lt;a href="https://modelcontextprotocol.io/specification/latest/basic/authorization" rel="noopener noreferrer"&gt;MCP authorization spec&lt;/a&gt;). The AI community has largely nodded along. Enterprise security teams have largely not been consulted.&lt;/p&gt;

&lt;p&gt;That mismatch is the subject of this article.&lt;/p&gt;

&lt;p&gt;DCR and CIMD are genuinely useful protocol mechanisms. They are not a trust framework. And in an era where supply chain attacks are the dominant enterprise threat vector, confusing the two is a governance failure waiting for a specific client name to trigger it.&lt;/p&gt;

&lt;h2&gt;
  
  
  What DCR was designed to remove
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://datatracker.ietf.org/doc/html/rfc7591" rel="noopener noreferrer"&gt;RFC 7591 - OAuth 2.0 Dynamic Client Registration Protocol&lt;/a&gt; solves a real problem. The traditional OAuth onboarding sequence requires a developer to pre-register their client application with the authorization server, receive a &lt;code&gt;client_id&lt;/code&gt; and &lt;code&gt;client_secret&lt;/code&gt;, configure redirect URIs, and coordinate with whoever manages the identity provider before a single token can flow. For SaaS products, open developer ecosystems, and multi-tenant platforms, this ceremony is expensive. Teams wait days for provisioning. Integration partners hit identity-team backlogs. CI pipelines stall on human approvals.&lt;/p&gt;

&lt;p&gt;DCR automates the ceremony. The client POSTs a JSON body describing itself to the authorization server's &lt;code&gt;registration_endpoint&lt;/code&gt;. The server returns a &lt;code&gt;client_id&lt;/code&gt; and &lt;code&gt;client_secret&lt;/code&gt; immediately. No human approval. No pre-coordination. Developer velocity restored.&lt;/p&gt;

&lt;p&gt;That is a genuine improvement in the right context. Public MCP servers, open developer tooling, and hobbyist integrations benefit from it directly. The friction reduction is real, and dismissing it is wrong.&lt;/p&gt;

&lt;p&gt;The problem is not the protocol. The problem is the assumption that registration equals authorization, and that authorization equals trust.&lt;/p&gt;

&lt;h2&gt;
  
  
  What CIMD adds to the picture
&lt;/h2&gt;

&lt;p&gt;Client Information Meta Data (CIMD) refers to the structured metadata fields a client submits in the DCR request, and through &lt;a href="https://datatracker.ietf.org/doc/html/rfc7592" rel="noopener noreferrer"&gt;RFC 7592 (Dynamic Client Registration Management)&lt;/a&gt;, the management endpoint that lets a registered client later read back and update its own registration record.&lt;/p&gt;

&lt;p&gt;The metadata fields are richer than they appear at first. A client can declare its &lt;code&gt;client_name&lt;/code&gt;, &lt;code&gt;client_uri&lt;/code&gt;, &lt;code&gt;redirect_uris&lt;/code&gt;, &lt;code&gt;grant_types&lt;/code&gt;, &lt;code&gt;scope&lt;/code&gt;, &lt;code&gt;logo_uri&lt;/code&gt;, and most interestingly a &lt;code&gt;software_statement&lt;/code&gt; (&lt;a href="https://datatracker.ietf.org/doc/html/rfc7591#section-2.3" rel="noopener noreferrer"&gt;RFC 7591 §2.3&lt;/a&gt;). A software statement is a signed JWT issued by a third party the authorization server trusts, asserting facts about the client software: who built it, what version it is, what it is intended to do. It is the closest thing RFC 7591 has to attestation.&lt;/p&gt;

&lt;p&gt;CIMD improves signal quality. It gives authorization servers structured, machine-readable metadata to work with. It makes client inventory possible in principle: if you log registrations, you at least know the name each client claimed. Software statements, when an authorization server validates them against a trusted issuer, move the model from "self-asserted identity" to "third-party asserted identity."&lt;/p&gt;

&lt;p&gt;The critical word is &lt;em&gt;when&lt;/em&gt;. Software statement validation is optional in RFC 7591. Most MCP server implementations do not enforce it. A client that omits the &lt;code&gt;software_statement&lt;/code&gt; field registers exactly as successfully as one that includes it. The registration endpoint cannot distinguish a vetted corporate tool from a novel command-line script someone assembled over a weekend and pointed at your server.&lt;/p&gt;

&lt;p&gt;CIMD raises the quality of information available at registration time. It does not answer whether your organization approved this client to exist here.&lt;/p&gt;

&lt;h2&gt;
  
  
  The enterprise security gap
&lt;/h2&gt;

&lt;p&gt;Security teams in regulated industries (financial services, healthcare, critical infrastructure, government) operate on a fundamentally different assumption about software trust than DCR's default model. They run approved-software programs. Patch management is gated on testing and change-board approval. In the most security-mature environments I have worked in across thousands of enterprise security conversations, even Windows Updates are staged, validated, and controlled before rollout to the fleet.&lt;/p&gt;

&lt;p&gt;This is not bureaucratic paranoia. It is the appropriate operational posture given how supply chain attacks actually work.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.cisa.gov/news-events/news/cisa-insights-solarwinds" rel="noopener noreferrer"&gt;SolarWinds&lt;/a&gt; demonstrated that a trusted vendor's signed software update is a viable attack vehicle. &lt;a href="https://nvd.nist.gov/vuln/detail/CVE-2024-3094" rel="noopener noreferrer"&gt;CVE-2024-3094 (XZ Utils)&lt;/a&gt; showed that a two-year social engineering campaign can insert a backdoor into a compression library that ships inside essentially every Linux distribution. The lesson from both is not "trust nothing." The lesson is that &lt;em&gt;how you establish and revoke software trust matters as much as whether a signature is present&lt;/em&gt;, and that self-asserted identity for software is precisely the attack surface adversaries aim for.&lt;/p&gt;

&lt;p&gt;Against that backdrop, consider what unrestricted DCR on an enterprise MCP server looks like. Any AI client (any command-line tool, any agentic orchestrator, any LLM-driven system) that discovers the server's &lt;code&gt;registration_endpoint&lt;/code&gt; can register itself, receive credentials, and start calling your server's tools. The server knows the client's claimed &lt;code&gt;client_name&lt;/code&gt;. It does not know whether your security team reviewed the client's code, whether the vendor passed your procurement process, whether the version running is the one that was reviewed, or whether the registration request is originating from inside your network perimeter or from an endpoint that was compromised last week.&lt;/p&gt;

&lt;p&gt;The SOC analyst's question is not theoretical: "Show me every MCP client registered on this server, who approved each one, under which policy, and when that approval was granted." Unrestricted DCR makes that question unanswerable, or worse, the answer is "anyone who sent a POST to the right endpoint."&lt;/p&gt;

&lt;p&gt;Small organizations face the same question. The compliance regime may be less formal, but the threat is not.&lt;/p&gt;

&lt;h2&gt;
  
  
  Entra's deliberate answer: explicit registration as a trust decision
&lt;/h2&gt;

&lt;p&gt;This is where the AI community tends to frame a gap. It is not a gap. It is architecture.&lt;/p&gt;

&lt;p&gt;Microsoft Entra ID has no &lt;a href="https://datatracker.ietf.org/doc/html/rfc7591" rel="noopener noreferrer"&gt;RFC 7591&lt;/a&gt; &lt;code&gt;registration_endpoint&lt;/code&gt;. There is no URL on &lt;code&gt;login.microsoftonline.com&lt;/code&gt; that accepts a DCR POST and returns &lt;code&gt;client_id&lt;/code&gt; credentials at runtime. Every application that can receive Entra-issued tokens must be explicitly registered, through the Azure portal, the Microsoft Graph API, the Azure CLI, or ARM templates, by a principal holding at minimum the &lt;a href="https://learn.microsoft.com/entra/identity/role-based-access-control/permissions-reference#application-developer" rel="noopener noreferrer"&gt;Application Developer role&lt;/a&gt; (&lt;a href="https://learn.microsoft.com/entra/identity-platform/quickstart-register-app" rel="noopener noreferrer"&gt;register an application in Microsoft Entra ID&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;That deliberate registration is not the end of the trust chain. It is the beginning of it. A registered application that has not been explicitly consented still receives no tokens. Consent in Entra is the authorization gate: the act that says "this application is authorized to call this API, with these permissions, under this policy." A well-named AI client with a convincing &lt;code&gt;client_name&lt;/code&gt; and a polished &lt;code&gt;client_uri&lt;/code&gt; gets nothing from Entra without a human trust decision in the chain. (For the full governance architecture behind this claim, see the article &lt;a href="https://dev.to/astaykov/why-consent-is-the-trust-contract-for-entra-agent-id-c03"&gt;Why consent is the trust contract for Entra Agent ID&lt;/a&gt;.)&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://modelcontextprotocol.io/specification/latest/basic/authorization" rel="noopener noreferrer"&gt;MCP authorization specification&lt;/a&gt; is careful here: it states that clients SHOULD attempt DCR when the authorization server's &lt;a href="https://datatracker.ietf.org/doc/html/rfc8414" rel="noopener noreferrer"&gt;RFC 8414&lt;/a&gt; metadata includes a &lt;code&gt;registration_endpoint&lt;/code&gt;. "Should," not "must." A well-behaved MCP client encountering an authorization server that publishes no &lt;code&gt;registration_endpoint&lt;/code&gt; (which is what every Entra tenant presents) falls back to static registration. That fallback is the intended behavior. The friction is intentional, and its source is a governance posture, not a missing feature.&lt;/p&gt;

&lt;p&gt;The honest tradeoff: Entra's model maximizes governance precision and eliminates runtime surprise about who can request tokens. It creates real friction for MCP clients that expect to self-register on first contact. That friction is a feature for regulated enterprise deployments. It is a genuine obstacle for open, developer-facing MCP servers where the audience is trusted developers and the stakes per registration are low. Both statements are true simultaneously.&lt;/p&gt;

&lt;h2&gt;
  
  
  APIM AI Gateway: the bridge, and where the gap remains
&lt;/h2&gt;

&lt;p&gt;When you place &lt;a href="https://learn.microsoft.com/azure/api-management/genai-gateway-capabilities" rel="noopener noreferrer"&gt;Azure API Management's AI Gateway&lt;/a&gt; in front of your MCP server, you gain a set of controls that together approach what a governance-controlled DCR layer would look like, without implementing one. Understanding exactly what APIM provides, and what it does not, is what allows you to design the right architecture rather than assume a gap has been closed when it has not.&lt;/p&gt;

&lt;p&gt;The first piece is discovery integration. APIM can expose the &lt;code&gt;.well-known/oauth-protected-resource&lt;/code&gt; endpoint defined in &lt;a href="https://datatracker.ietf.org/doc/html/rfc9728" rel="noopener noreferrer"&gt;RFC 9728&lt;/a&gt;, which tells DCR-capable MCP clients where to find the authorization server protecting this resource (&lt;a href="https://learn.microsoft.com/azure/api-management/secure-mcp-servers" rel="noopener noreferrer"&gt;secure access to MCP servers&lt;/a&gt;). When APIM points to Entra as the authorization server, the MCP client reaches Entra, discovers no &lt;code&gt;registration_endpoint&lt;/code&gt; in Entra's RFC 8414 metadata, and falls back to static registration. The discovery flow runs correctly. The governance gate holds. APIM participates in the DCR protocol dance without becoming a DCR endpoint.&lt;/p&gt;

&lt;p&gt;The second piece is runtime client enforcement via the &lt;a href="https://learn.microsoft.com/azure/api-management/validate-azure-ad-token-policy" rel="noopener noreferrer"&gt;&lt;code&gt;validate-azure-ad-token&lt;/code&gt; policy&lt;/a&gt;. This policy accepts a &lt;code&gt;&amp;lt;client-application-ids&amp;gt;&lt;/code&gt; block, an explicit allowlist of Entra application IDs whose tokens are valid at this gateway:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;validate-azure-ad-token&lt;/span&gt; &lt;span class="na"&gt;tenant-id=&lt;/span&gt;&lt;span class="s"&gt;"your-entra-tenant-id"&lt;/span&gt;
    &lt;span class="na"&gt;header-name=&lt;/span&gt;&lt;span class="s"&gt;"Authorization"&lt;/span&gt;
    &lt;span class="na"&gt;failed-validation-httpcode=&lt;/span&gt;&lt;span class="s"&gt;"401"&lt;/span&gt;
    &lt;span class="na"&gt;failed-validation-error-message=&lt;/span&gt;&lt;span class="s"&gt;"Unauthorized. Access token is missing or invalid."&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;client-application-ids&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;application-id&amp;gt;&lt;/span&gt;approved-mcp-client-app-id&lt;span class="nt"&gt;&amp;lt;/application-id&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/client-application-ids&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/validate-azure-ad-token&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A client that was registered in Entra and consented (both explicit human decisions), but whose application ID is absent from this list, still cannot pass the gateway. That is two distinct human approval steps between "an AI client exists" and "that AI client can invoke your MCP server tools." This is the runtime equivalent of an enterprise approved-software list, enforced at the gateway rather than in application code.&lt;/p&gt;

&lt;p&gt;For scenarios where Entra registration still introduces too much friction (third-party client integrations, short-lived automation, partner tooling), APIM's subscription key model is the highest-control alternative. Every MCP client consumer receives a key provisioned explicitly by an APIM administrator. No dynamic discovery. No self-registration. Full explicit provisioning per consumer, with gateway-level revocation that does not wait for a consent withdrawal to propagate through the identity layer.&lt;/p&gt;

&lt;p&gt;The observability dimension is underweighted in most governance architecture conversations. APIM routes all MCP tool invocations through a single plane with &lt;a href="https://learn.microsoft.com/azure/api-management/api-management-howto-app-insights" rel="noopener noreferrer"&gt;Application Insights integration&lt;/a&gt; and the &lt;a href="https://learn.microsoft.com/azure/api-management/llm-emit-token-metric-policy" rel="noopener noreferrer"&gt;&lt;code&gt;llm-emit-token-metric&lt;/code&gt; policy&lt;/a&gt; for per-consumer token tracking. The SOC analyst's question, "show me every client calling this server, what it consumed, and when", becomes answerable from a single observability plane. That audit trail is precisely what unrestricted DCR alone never provides.&lt;/p&gt;

&lt;p&gt;The honest gap deserves explicit naming. Neither APIM nor Entra today offers a governance-controlled RFC 7591 registration endpoint: a &lt;code&gt;/register&lt;/code&gt; that issues credentials only to clients that have passed a policy gate: an Entra consent check, an access package approval, or a security review workflow. APIM and Entra together approximate this behavior through a different mechanism: explicit Entra app registration as the consent prerequisite, gateway policy as the runtime enforcement layer. The interoperability friction with DCR-native MCP clients is real. The architectural slot for a "governed DCR endpoint" is currently unoccupied in the Microsoft stack.&lt;/p&gt;

&lt;p&gt;That gap is worth naming because it is where the tooling will likely evolve. An architecture where APIM exposes an RFC 7591-compliant &lt;code&gt;/register&lt;/code&gt; endpoint that, on receiving a DCR request, triggers an Entra app registration, initiates an admin consent workflow, and optionally assigns an access package, before returning credentials to the registering client, would close it elegantly. That architecture does not exist today as a native product feature. What exists is the layered governance model described above.&lt;/p&gt;

&lt;h2&gt;
  
  
  A governance stack, not a binary choice
&lt;/h2&gt;

&lt;p&gt;The practical architecture decision is not "DCR or no DCR." It is: what governance controls stand between a client expressing interest and a client receiving credentials?&lt;/p&gt;

&lt;p&gt;For open, developer-facing MCP servers where the audience is trusted developers, the data sensitivity is low, and the expected client population is diverse and changing, DCR with software statement validation and a lean consent model is appropriate. Lean does not mean absent: even here, logging registrations, validating software statements when present, and auditing active clients periodically are the minimum defensible posture.&lt;/p&gt;

&lt;p&gt;For enterprise MCP servers that expose organizational data, internal APIs, financial workflows, or records subject to regulatory requirements, the bar is different. Every client should be named. Every registration should be a deliberate trust decision. Every token scope should be consented explicitly under a policy that your security and governance teams own.&lt;/p&gt;

&lt;p&gt;APIM plus Entra delivers that model today. Entra's explicit registration and consent layer ensures no client receives tokens without a human decision in the chain. APIM's AI Gateway enforcement layer ensures no token-bearing client bypasses your runtime policy. The observability layer ensures that "who approved this, with which permissions, and when" remains answerable across the full client lifecycle.&lt;/p&gt;

&lt;p&gt;CIMD metadata enriches the picture on the way in: software statements, declared grant types, client URIs. Use them where your authorization server or gateway policy can act on them. Do not treat "richer metadata at registration time" as a substitute for governance state. Those are different properties, and conflating them is the precise mistake that makes a SOC analyst's life difficult six months after a pilot launches.&lt;/p&gt;

&lt;p&gt;DCR is useful. CIMD is useful. Neither is a trust framework. The security teams that built enterprise software approval programs did not do it for the friction. They did it because supply chain attacks are real, and the moment you stop asking "who approved this software to exist here" is the moment a well-named binary with a convincing &lt;code&gt;client_name&lt;/code&gt; receives credentials to your organizational data.&lt;/p&gt;

&lt;p&gt;APIM AI Gateway and Entra's explicit registration model is the closest the Microsoft stack comes today to answering that question at MCP server scale. The remaining gap is a named target. The governance floor is already in place.&lt;/p&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://datatracker.ietf.org/doc/html/rfc7591" rel="noopener noreferrer"&gt;RFC 7591 - OAuth 2.0 Dynamic Client Registration Protocol&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://datatracker.ietf.org/doc/html/rfc7592" rel="noopener noreferrer"&gt;RFC 7592 - OAuth 2.0 Dynamic Client Registration Management Protocol&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://datatracker.ietf.org/doc/html/rfc9728" rel="noopener noreferrer"&gt;RFC 9728 - OAuth 2.0 Protected Resource Metadata&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://datatracker.ietf.org/doc/html/rfc8414" rel="noopener noreferrer"&gt;RFC 8414 - OAuth 2.0 Authorization Server Metadata&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://modelcontextprotocol.io/specification/latest/basic/authorization" rel="noopener noreferrer"&gt;MCP authorization specification&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/entra/identity-platform/quickstart-register-app" rel="noopener noreferrer"&gt;Register an application in Microsoft Entra ID&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/entra/identity/role-based-access-control/permissions-reference#application-developer" rel="noopener noreferrer"&gt;Application Developer role - Microsoft Entra&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/microsoft-365/copilot/extensibility/plugin-authentication-dynamic-client-registration" rel="noopener noreferrer"&gt;Configure dynamic client registration - Microsoft 365 Copilot extensibility&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/azure/api-management/genai-gateway-capabilities" rel="noopener noreferrer"&gt;AI gateway capabilities in Azure API Management&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/azure/api-management/secure-mcp-servers" rel="noopener noreferrer"&gt;Secure access to MCP servers in Azure API Management&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/azure/api-management/mcp-server-overview" rel="noopener noreferrer"&gt;MCP server overview in Azure API Management&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/azure/api-management/validate-azure-ad-token-policy" rel="noopener noreferrer"&gt;validate-azure-ad-token policy&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/azure/api-management/credentials-overview" rel="noopener noreferrer"&gt;About API credentials and credential manager in API Management&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/azure/api-management/api-management-howto-app-insights" rel="noopener noreferrer"&gt;Application Insights integration in API Management&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/azure/api-management/llm-emit-token-metric-policy" rel="noopener noreferrer"&gt;llm-emit-token-metric policy&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/entra/identity-platform/permissions-consent-overview" rel="noopener noreferrer"&gt;Permissions and consent overview - Microsoft Entra&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.cisa.gov/news-events/news/cisa-insights-solarwinds" rel="noopener noreferrer"&gt;CISA insights: SolarWinds&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://nvd.nist.gov/vuln/detail/CVE-2024-3094" rel="noopener noreferrer"&gt;NVD CVE-2024-3094 (XZ Utils backdoor)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>oauth</category>
      <category>security</category>
      <category>zerotrust</category>
      <category>apimanagement</category>
    </item>
    <item>
      <title>Why consent is the trust contract for Entra Agent ID</title>
      <dc:creator>Anton Staykov</dc:creator>
      <pubDate>Wed, 12 Aug 2026 11:07:00 +0000</pubDate>
      <link>https://dev.to/astaykov/why-consent-is-the-trust-contract-for-entra-agent-id-c03</link>
      <guid>https://dev.to/astaykov/why-consent-is-the-trust-contract-for-entra-agent-id-c03</guid>
      <description>&lt;p&gt;If your AI agent can read customer mailboxes, create directory objects, or call internal APIs, your real architecture question is not "can it authenticate?" The hard question is "who authorized this, under which policy, for how long, and with which guardrails?" In Microsoft Entra, that question is answered through &lt;a href="https://learn.microsoft.com/en-us/entra/identity-platform/permissions-consent-overview" rel="noopener noreferrer"&gt;permissions and consent&lt;/a&gt;. That is why consent is not a user-experience detail. It is the trust contract.&lt;/p&gt;

&lt;p&gt;This matters even more for agent identities. Microsoft Entra Agent ID deliberately treats agents as a special operational class, with explicit authorization controls and blocked high-risk privileges rather than unconstrained app-like power (&lt;a href="https://learn.microsoft.com/en-us/entra/agent-id/authorization-agent-id" rel="noopener noreferrer"&gt;authorization model&lt;/a&gt;, &lt;a href="https://learn.microsoft.com/en-us/graph/api/resources/agentid-platform-overview?view=graph-rest-1.0" rel="noopener noreferrer"&gt;Graph overview for Agent ID&lt;/a&gt;). If your mental model is still "grant broad access once and move on," your governance will fail faster than your pilot succeeds.&lt;/p&gt;

&lt;p&gt;In [[A-transformational-AI-agent-does-not-start-with-the-permissions-it-needs]], I argued that strong agent platforms start narrow and earn scope in context. This article is the governance layer under that claim: why consent is the control surface, how to operate it at enterprise scale, and how access packages turn theory into an auditable runtime model.&lt;/p&gt;

&lt;h2&gt;
  
  
  Consent is authorization, not authentication
&lt;/h2&gt;

&lt;p&gt;Consent in Entra is the act that authorizes an application identity to access a protected resource with specific permissions (&lt;a href="https://learn.microsoft.com/en-us/entra/identity/enterprise-apps/user-admin-consent-overview" rel="noopener noreferrer"&gt;user and admin consent overview&lt;/a&gt;, &lt;a href="https://learn.microsoft.com/en-us/entra/identity-platform/permissions-consent-overview" rel="noopener noreferrer"&gt;permissions and consent overview&lt;/a&gt;). Authentication answers "who are you?" Consent answers "what are you allowed to do against this API or resource?"&lt;/p&gt;

&lt;p&gt;That distinction sounds obvious until production pressure arrives. Teams often secure sign-in, configure Conditional Access, and then assume the authorization problem is solved. It is not. A signed-in actor without the right consent grant is still unauthorized, and a correctly consented actor can still be over-privileged if governance posture is weak (&lt;a href="https://learn.microsoft.com/en-us/entra/identity-platform/permissions-consent-overview" rel="noopener noreferrer"&gt;permissions and consent overview&lt;/a&gt;, &lt;a href="https://learn.microsoft.com/en-us/entra/identity/enterprise-apps/configure-user-consent?pivots=portal" rel="noopener noreferrer"&gt;configure user consent&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;For mixed technical leadership, this is the key reframing: consent is where legal risk, security posture, and delivery velocity intersect. It is the formal boundary between "agent capability requested" and "agent capability approved."&lt;/p&gt;

&lt;h2&gt;
  
  
  User consent and admin consent are different risk gates
&lt;/h2&gt;

&lt;p&gt;Entra supports delegated permissions where an app or agent acts on behalf of a signed-in user, and application permissions where it acts autonomously without a user context (&lt;a href="https://learn.microsoft.com/en-us/entra/identity-platform/permissions-consent-overview#types-of-permissions" rel="noopener noreferrer"&gt;permissions and consent overview&lt;/a&gt;, &lt;a href="https://learn.microsoft.com/en-us/entra/agent-id/grant-agent-access-microsoft-365" rel="noopener noreferrer"&gt;grant agents access&lt;/a&gt;). Those two modes map to different consent paths and different blast radii.&lt;/p&gt;

&lt;p&gt;User consent is viable when non-admin users are allowed to consent to the app and permission set by tenant policy (&lt;a href="https://learn.microsoft.com/en-us/entra/identity/enterprise-apps/user-admin-consent-overview#user-consent" rel="noopener noreferrer"&gt;user and admin consent overview&lt;/a&gt;). Admin consent is required for application permissions and for delegated permissions that are admin-restricted or otherwise outside permitted user-consent boundaries (&lt;a href="https://learn.microsoft.com/en-us/entra/identity/enterprise-apps/user-admin-consent-overview#admin-consent" rel="noopener noreferrer"&gt;user and admin consent overview&lt;/a&gt;, &lt;a href="https://learn.microsoft.com/en-us/entra/agent-id/grant-agent-access-microsoft-365" rel="noopener noreferrer"&gt;grant agents access&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;That is not bureaucracy. That is architecture. User consent is typically scoped to the user context. Admin consent can authorize tenant-wide impact. Treating those as interchangeable is how over-privileged agents are born.&lt;/p&gt;

&lt;h2&gt;
  
  
  Consent policy is a leadership decision encoded in platform behavior
&lt;/h2&gt;

&lt;p&gt;Most organizations do not leave consent wide open, and they should not. Entra gives administrators tenant-wide choices: disable user consent entirely, allow only verified publishers plus selected low-impact permissions, or allow broader user consent where risk tolerance supports it (&lt;a href="https://learn.microsoft.com/en-us/entra/identity/enterprise-apps/configure-user-consent?pivots=portal" rel="noopener noreferrer"&gt;configure user consent&lt;/a&gt;, &lt;a href="https://learn.microsoft.com/en-us/entra/identity/enterprise-apps/user-admin-consent-overview#user-consent-settings" rel="noopener noreferrer"&gt;user and admin consent overview&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;If user consent is limited, Entra can route blocked requests through the admin consent workflow so business demand is visible and reviewable instead of hidden in shadow tooling (&lt;a href="https://learn.microsoft.com/en-us/entra/identity/enterprise-apps/user-admin-consent-overview#admin-consent-workflow" rel="noopener noreferrer"&gt;user and admin consent overview&lt;/a&gt;, &lt;a href="https://learn.microsoft.com/en-us/entra/identity/enterprise-apps/configure-admin-consent-workflow" rel="noopener noreferrer"&gt;configure admin consent workflow&lt;/a&gt;). This is where governance stops being a "no" function and becomes a control function.&lt;/p&gt;

&lt;p&gt;For leadership teams, the operational takeaway is clear: consent policy is not a tactical identity setting. It is a policy expression of enterprise risk appetite. Decide it explicitly, document it, and align agent deployment patterns to it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Consent phishing proves whether your model is real
&lt;/h2&gt;

&lt;p&gt;Consent phishing is the attack pattern where users are tricked into granting OAuth permissions to malicious applications, often through convincing app names and realistic prompts (&lt;a href="https://learn.microsoft.com/en-us/entra/identity/enterprise-apps/protect-against-consent-phishing#what-is-consent-phishing" rel="noopener noreferrer"&gt;protect against consent phishing&lt;/a&gt;). This is not theoretical noise. It is exactly what happens when the organization treats consent as a user-interface click instead of a control plane.&lt;/p&gt;

&lt;p&gt;Microsoft guidance is explicit: harden user consent settings, favor verified publishers, audit app permissions, investigate suspicious grants, and integrate detection and remediation workflows (&lt;a href="https://learn.microsoft.com/en-us/entra/identity/enterprise-apps/protect-against-consent-phishing#best-practices-for-hardening-against-consent-phishing-attacks" rel="noopener noreferrer"&gt;protect against consent phishing&lt;/a&gt;). If those controls are absent, your "agent security strategy" is just language.&lt;/p&gt;

&lt;p&gt;A mature model assumes two truths at once: legitimate agents need controlled permission growth, and adversaries will try to exploit the same consent mechanics. Governance has to handle both.&lt;/p&gt;

&lt;h2&gt;
  
  
  Delegated vs application permissions for agents, with protocol constructs that matter
&lt;/h2&gt;

&lt;p&gt;For agent identities, delegated mode is used when the agent acts in a user context. Application mode is used when the agent acts autonomously (&lt;a href="https://learn.microsoft.com/en-us/entra/agent-id/authorization-agent-id#microsoft-graph-permissions-for-agent-ids" rel="noopener noreferrer"&gt;authorization in Agent ID&lt;/a&gt;, &lt;a href="https://learn.microsoft.com/en-us/entra/agent-id/grant-agent-access-microsoft-365#when-to-use-delegated-or-application-permissions" rel="noopener noreferrer"&gt;grant agents access&lt;/a&gt;). That split directly affects token semantics.&lt;/p&gt;

&lt;p&gt;In delegated scenarios, effective scopes are carried in the &lt;code&gt;scp&lt;/code&gt; claim. In app-only scenarios, effective app permissions are carried in the &lt;code&gt;roles&lt;/code&gt; claim (&lt;a href="https://learn.microsoft.com/en-us/entra/agent-id/agent-token-claims#core-token-claim-types" rel="noopener noreferrer"&gt;token claims for agents&lt;/a&gt;, &lt;a href="https://learn.microsoft.com/en-us/entra/agent-id/grant-agent-access-microsoft-365#when-to-use-delegated-or-application-permissions" rel="noopener noreferrer"&gt;grant agents access&lt;/a&gt;). You do not need packet-level protocol traces to govern correctly, but you do need leadership alignment that these constructs define runtime authority.&lt;/p&gt;

&lt;p&gt;The advanced nuance for Agent ID is permission inheritance at blueprint level. This is not default behavior. Entra documents that inheritance only happens when you explicitly configure inheritable permissions on the blueprint and the corresponding permissions are actually granted on the blueprint principal (&lt;a href="https://learn.microsoft.com/en-us/entra/agent-id/concept-inheritable-permissions" rel="noopener noreferrer"&gt;inheritable permissions concept&lt;/a&gt;, &lt;a href="https://learn.microsoft.com/en-us/entra/agent-id/configure-inheritable-permissions-blueprints" rel="noopener noreferrer"&gt;configure inheritable permissions&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;That should drive a stricter approval posture: treat blueprint consent as baseline capability setup, not as a full production access request. Keep blueprint consent narrow, and push broader or time-bound operational access through governed assignment workflows such as access packages (&lt;a href="https://learn.microsoft.com/en-us/entra/agent-id/agent-access-packages" rel="noopener noreferrer"&gt;access packages for agent identities&lt;/a&gt;).&lt;/p&gt;

&lt;h2&gt;
  
  
  Agent ID already blocks high-risk permission paths, by design
&lt;/h2&gt;

&lt;p&gt;A healthy governance discussion should also acknowledge built-in platform guardrails. Agent IDs can use delegated and application permission models, but Entra explicitly blocks a range of high-risk Graph permissions for agents and restricts role assignment options compared to unconstrained identity types (&lt;a href="https://learn.microsoft.com/en-us/entra/agent-id/authorization-agent-id" rel="noopener noreferrer"&gt;authorization in Agent ID&lt;/a&gt;, &lt;a href="https://learn.microsoft.com/en-us/graph/api/resources/agentid-platform-overview?view=graph-rest-1.0#microsoft-graph-permissions-blocked-for-agents" rel="noopener noreferrer"&gt;Agent ID Graph overview blocked permissions&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;This design choice reinforces the principle of least privilege for autonomous systems. It does not remove governance responsibility, but it reduces failure modes where someone "temporarily" grants sweeping directory control and forgets to remove it.&lt;/p&gt;

&lt;p&gt;In plain terms: the platform is already telling you the right operating model. Narrow by default. Escalate deliberately. Keep high-risk paths off the table unless there is an explicit and supported control route.&lt;/p&gt;

&lt;h2&gt;
  
  
  Access packages are the practical admin-consent engine for agent scale
&lt;/h2&gt;

&lt;p&gt;Consent alone is necessary, but not sufficient, when you run fleets of agents. You need a repeatable, policy-owned mechanism for requesting, approving, granting, expiring, and reviewing access. For Entra Agent ID, access packages provide that mechanism (&lt;a href="https://learn.microsoft.com/en-us/entra/agent-id/agent-access-packages" rel="noopener noreferrer"&gt;access packages for agent identities&lt;/a&gt;, &lt;a href="https://learn.microsoft.com/en-us/entra/id-governance/agent-id-governance-overview" rel="noopener noreferrer"&gt;governing agent identities&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;This is why I keep connecting this to [[Access-Packages-Are-the-Control-Plane-for-Entra-Agent-ID-Permissions]]. Access packages let you operationalize consent and authorization as workflow, not as ad hoc privilege assignment. They can include directory roles, OAuth delegated and application permissions, and group memberships in one governed package (&lt;a href="https://learn.microsoft.com/en-us/entra/agent-id/agent-access-packages" rel="noopener noreferrer"&gt;access packages for agent identities&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;Crucially, policy can require one, two, or three approval stages, with explicit approver definitions and decision timeouts (&lt;a href="https://learn.microsoft.com/en-us/entra/id-governance/entitlement-management-access-package-approval-policy" rel="noopener noreferrer"&gt;approval policy settings&lt;/a&gt;, &lt;a href="https://learn.microsoft.com/en-us/entra/id-governance/entitlement-management-access-package-create#specify-approval-settings" rel="noopener noreferrer"&gt;access package create stages&lt;/a&gt;). That means you can model governance depth based on data sensitivity, not gut feel.&lt;/p&gt;

&lt;h2&gt;
  
  
  Dynamic approvals, SoD checks, and custom logic make governance adaptive
&lt;/h2&gt;

&lt;p&gt;Static approver lists are often too rigid for enterprise reality. Entra entitlement management supports dynamic approval determination through custom extensions and Logic Apps integration, so approvers can be derived through business logic at request time (&lt;a href="https://learn.microsoft.com/en-us/entra/id-governance/entitlement-management-dynamic-approval" rel="noopener noreferrer"&gt;dynamic approval&lt;/a&gt;, &lt;a href="https://learn.microsoft.com/en-us/entra/id-governance/entitlement-management-logic-apps-integration" rel="noopener noreferrer"&gt;Logic Apps integration&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;Separation-of-duties controls let you mark access packages or groups as incompatible, preventing users or identities with one assignment from requesting conflicting assignments (&lt;a href="https://learn.microsoft.com/en-us/entra/id-governance/entitlement-management-access-package-incompatible" rel="noopener noreferrer"&gt;SoD checks&lt;/a&gt;). This is the difference between "we approved safely once" and "we remain safe over time."&lt;/p&gt;

&lt;p&gt;If you need one sentence for the board slide, use this: access packages are not just an approval screen. They are a programmable authorization lifecycle with governance state.&lt;/p&gt;

&lt;h2&gt;
  
  
  A practical operating model for mixed technical leadership
&lt;/h2&gt;

&lt;p&gt;If you lead a platform team, security team, or product portfolio touching agent identities, this model is battle-tested and aligned to Microsoft guidance:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Start with narrow required access&lt;/strong&gt; on blueprints and explicit inheritable-permission design (inheritance is opt-in, not automatic), so admins can review baseline intent clearly and avoid turning blueprint consent into broad standing access (&lt;a href="https://learn.microsoft.com/en-us/entra/agent-id/concept-inheritable-permissions" rel="noopener noreferrer"&gt;inheritable permissions concept&lt;/a&gt;, &lt;a href="https://learn.microsoft.com/en-us/entra/agent-id/configure-inheritable-permissions-blueprints" rel="noopener noreferrer"&gt;configure inheritable permissions&lt;/a&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Set tenant consent policy intentionally&lt;/strong&gt; (usually verified-publisher plus low-risk where possible), and force higher-risk asks through admin review paths (&lt;a href="https://learn.microsoft.com/en-us/entra/identity/enterprise-apps/configure-user-consent?pivots=portal" rel="noopener noreferrer"&gt;configure user consent&lt;/a&gt;, &lt;a href="https://learn.microsoft.com/en-us/entra/identity/enterprise-apps/user-admin-consent-overview" rel="noopener noreferrer"&gt;user and admin consent overview&lt;/a&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Route runtime growth through access packages&lt;/strong&gt; instead of direct permanent grants, including approver stages, SoD, and expiry settings (&lt;a href="https://learn.microsoft.com/en-us/entra/agent-id/agent-access-packages" rel="noopener noreferrer"&gt;access packages for agent identities&lt;/a&gt;, &lt;a href="https://learn.microsoft.com/en-us/entra/id-governance/entitlement-management-access-package-approval-policy" rel="noopener noreferrer"&gt;approval policy&lt;/a&gt;, &lt;a href="https://learn.microsoft.com/en-us/entra/id-governance/entitlement-management-access-package-incompatible" rel="noopener noreferrer"&gt;SoD checks&lt;/a&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Treat sponsor and owner accountability as first-class controls&lt;/strong&gt;, because governance is not complete if no human is accountable for extension and renewal decisions (&lt;a href="https://learn.microsoft.com/en-us/entra/id-governance/agent-id-governance-overview" rel="noopener noreferrer"&gt;governing agent identities&lt;/a&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Assume consent abuse attempts and instrument for response&lt;/strong&gt;, including consent-phishing hardening and suspicious-grant investigation workflows (&lt;a href="https://learn.microsoft.com/en-us/entra/identity/enterprise-apps/protect-against-consent-phishing" rel="noopener noreferrer"&gt;protect against consent phishing&lt;/a&gt;).&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This operating model gives engineering enough autonomy to ship and iterate, while preserving the control evidence leadership needs for audit and risk committees.&lt;/p&gt;

&lt;h2&gt;
  
  
  The core decision
&lt;/h2&gt;

&lt;p&gt;You can run agents on standing privilege and hope nothing goes wrong. Many teams do exactly that during pilots. It looks fast right until the first real governance review.&lt;/p&gt;

&lt;p&gt;Or you can run agents on governed consent, with policy-owned approval paths, explicit token authority constructs, and time-bound assignment lifecycle. That model scales beyond demos because it aligns with how Entra is designed to authorize applications and agents (&lt;a href="https://learn.microsoft.com/en-us/entra/identity-platform/permissions-consent-overview" rel="noopener noreferrer"&gt;permissions and consent overview&lt;/a&gt;, &lt;a href="https://learn.microsoft.com/en-us/entra/agent-id/authorization-agent-id" rel="noopener noreferrer"&gt;authorization in Agent ID&lt;/a&gt;, &lt;a href="https://learn.microsoft.com/en-us/entra/agent-id/agent-access-packages" rel="noopener noreferrer"&gt;access packages for agent identities&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;That is the underrated shift in enterprise agent architecture right now: the winning platform is not the one with the most tools. It is the one where consent remains the trust contract, even as agent capability grows.&lt;/p&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/en-us/entra/agent-id/authorization-agent-id" rel="noopener noreferrer"&gt;Authorization in Microsoft Entra Agent ID&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/en-us/entra/agent-id/grant-agent-access-microsoft-365" rel="noopener noreferrer"&gt;Grant agents access to Microsoft 365 resources&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/en-us/entra/agent-id/agent-token-claims" rel="noopener noreferrer"&gt;Token claims reference for agents&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/en-us/entra/agent-id/concept-inheritable-permissions" rel="noopener noreferrer"&gt;Inheritable permissions and required resource access in Microsoft Entra Agent ID&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/en-us/entra/agent-id/configure-inheritable-permissions-blueprints" rel="noopener noreferrer"&gt;Configure inheritable permissions for agent identity blueprints&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/en-us/entra/identity-platform/permissions-consent-overview" rel="noopener noreferrer"&gt;Overview of permissions and consent in the Microsoft identity platform&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/en-us/entra/identity/enterprise-apps/user-admin-consent-overview" rel="noopener noreferrer"&gt;Overview of user and admin consent&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/en-us/entra/identity/enterprise-apps/configure-user-consent?pivots=portal" rel="noopener noreferrer"&gt;Configure how users consent to applications&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/en-us/entra/identity/enterprise-apps/configure-admin-consent-workflow" rel="noopener noreferrer"&gt;Configure the admin consent workflow&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/en-us/entra/identity/enterprise-apps/protect-against-consent-phishing" rel="noopener noreferrer"&gt;Protect against consent phishing&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/en-us/entra/agent-id/agent-access-packages" rel="noopener noreferrer"&gt;Access packages for Agent identities&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/en-us/entra/id-governance/agent-id-governance-overview" rel="noopener noreferrer"&gt;Governing Agent Identities&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/en-us/entra/id-governance/entitlement-management-access-package-approval-policy" rel="noopener noreferrer"&gt;Change approval and requestor information settings for an access package in entitlement management&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/en-us/entra/id-governance/entitlement-management-access-package-create" rel="noopener noreferrer"&gt;Create an access package in entitlement management&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/en-us/entra/id-governance/entitlement-management-dynamic-approval" rel="noopener noreferrer"&gt;Externally determine the approval requirements for an access package using custom extensions&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/en-us/entra/id-governance/entitlement-management-logic-apps-integration" rel="noopener noreferrer"&gt;Trigger Logic Apps with custom extensions in entitlement management&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/en-us/entra/id-governance/entitlement-management-access-package-incompatible" rel="noopener noreferrer"&gt;Configure separation of duties checks for an access package in entitlement management&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/en-us/graph/api/resources/agentid-platform-overview?view=graph-rest-1.0" rel="noopener noreferrer"&gt;Microsoft Entra Agent ID APIs in Microsoft Graph overview&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>entraagentid</category>
      <category>identitygovernance</category>
      <category>microsoftentra</category>
      <category>aiagents</category>
    </item>
    <item>
      <title>From JWT-SVID to Entra Agent ID: a working SPIFFE PoC</title>
      <dc:creator>Anton Staykov</dc:creator>
      <pubDate>Thu, 06 Aug 2026 11:19:00 +0000</pubDate>
      <link>https://dev.to/astaykov/from-jwt-svid-to-entra-agent-id-a-working-spiffe-poc-ic4</link>
      <guid>https://dev.to/astaykov/from-jwt-svid-to-entra-agent-id-a-working-spiffe-poc-ic4</guid>
      <description>&lt;p&gt;The architecture diagram was the easy part.&lt;/p&gt;

&lt;p&gt;In &lt;a href="https://dev.to/astaykov/your-spiffe-workload-can-authenticate-as-an-entra-agent-id-but-mind-the-trust-gap-3969"&gt;Your SPIFFE workload can authenticate as an Entra Agent ID, but mind the trust gap&lt;/a&gt;, I argued that SPIFFE and Microsoft Entra Agent ID belong on opposite sides of a narrow trust contract. SPIRE proves which workload is running. Entra decides which governed agent identity that workload may use. A federated identity credential connects those statements through an exact issuer, subject, and audience match, which is how &lt;a href="https://learn.microsoft.com/graph/api/resources/federatedidentitycredentials-overview?view=graph-rest-1.0" rel="noopener noreferrer"&gt;Microsoft Entra workload identity federation validates an external JWT&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;That article described the architecture and its trust gap. This one runs it.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://github.com/astaykov/agentid-spiffe" rel="noopener noreferrer"&gt;&lt;code&gt;agentid-spiffe&lt;/code&gt; proof of concept&lt;/a&gt; deploys a complete agent loop with SPIRE, public OIDC discovery, Microsoft Entra Agent ID, and a downstream Microsoft MCP Server for enterprise call. The interesting result is not the chat interface or the model. It is the moment a JWT-SVID issued to one attested process becomes the client assertion for an Agent Identity Blueprint, without a client secret or certificate copied into the workload.&lt;/p&gt;

&lt;p&gt;This is a conceptual walkthrough with selected configuration and code. It is deliberately not a SPIRE deployment guide. Follow the &lt;a href="https://github.com/astaykov/agentid-spiffe#readme" rel="noopener noreferrer"&gt;repository README&lt;/a&gt; to run the sample. Here, we are going to trace the identity as it changes hands.&lt;/p&gt;

&lt;h2&gt;
  
  
  Five SPIFFE concepts in one minute
&lt;/h2&gt;

&lt;p&gt;A &lt;em&gt;trust domain&lt;/em&gt; is the administrative and cryptographic boundary within which SPIFFE identities are issued. Its name forms the authority portion of every SPIFFE ID. The PoC uses &lt;code&gt;poc.local&lt;/code&gt;, so its workload identity begins with &lt;code&gt;spiffe://poc.local/&lt;/code&gt;. SPIFFE defines that URI as the stable name of a workload, independent of an IP address, host name, or cloud provider account (&lt;a href="https://spiffe.io/docs/latest/spiffe-about/spiffe-concepts/" rel="noopener noreferrer"&gt;SPIFFE concepts&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;The &lt;em&gt;SPIRE Server&lt;/em&gt; is the authority for that trust domain. It stores registration entries, evaluates attestation results, and signs the identity documents issued inside the domain. The &lt;em&gt;SPIRE Agent&lt;/em&gt; runs near workloads, attests to the server as a node, exposes the local Workload API, and performs workload attestation before returning identity documents to a process (&lt;a href="https://spiffe.io/docs/latest/spire-about/spire-concepts/" rel="noopener noreferrer"&gt;SPIRE concepts&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Workload attestation&lt;/em&gt; is the decision that connects runtime evidence to a registered SPIFFE ID. In this sample, the SPIRE Server registers a Unix selector for a process running as UID 0. When the FastAPI process calls the local Workload API, the SPIRE Agent evaluates that evidence and returns the identity registered for the matching selector. SPIRE documents selectors as the attested properties used to match a workload to a registration entry (&lt;a href="https://spiffe.io/docs/latest/deploying/registering/" rel="noopener noreferrer"&gt;registering workloads&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;The returned identity can take two common forms. An X.509-SVID is a certificate suited to mutual TLS. A &lt;em&gt;JWT-SVID&lt;/em&gt; is a signed bearer token whose &lt;code&gt;sub&lt;/code&gt; is the workload's SPIFFE ID and whose &lt;code&gt;aud&lt;/code&gt; names the intended recipient (&lt;a href="https://github.com/spiffe/spiffe/blob/main/standards/JWT-SVID.md" rel="noopener noreferrer"&gt;JWT-SVID specification&lt;/a&gt;). This PoC needs the JWT form because Microsoft Entra accepts an external JWT as the client assertion in its &lt;a href="https://learn.microsoft.com/entra/identity-platform/v2-oauth2-client-creds-grant-flow#third-case-access-token-request-with-a-federated-credential" rel="noopener noreferrer"&gt;federated client credential flow&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;That is all the SPIFFE theory required for this walkthrough. The rest is the handoff.&lt;/p&gt;

&lt;h2&gt;
  
  
  The components in play
&lt;/h2&gt;

&lt;p&gt;The PoC places several processes in one Azure Container App image so the identity path is easy to inspect. That packaging is a lab convenience, not an architectural recommendation (&lt;a href="https://github.com/astaykov/agentid-spiffe#architecture" rel="noopener noreferrer"&gt;repository architecture&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;Inside the container, five components matter:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The SPIRE Server owns the &lt;code&gt;poc.local&lt;/code&gt; trust domain and signs JWT-SVIDs.&lt;/li&gt;
&lt;li&gt;The SPIRE Agent exposes a Unix socket implementing the Workload API.&lt;/li&gt;
&lt;li&gt;The SPIRE OIDC Discovery Provider publishes the JWT signing keys.&lt;/li&gt;
&lt;li&gt;FastAPI retrieves the JWT-SVID and performs the Entra token exchanges.&lt;/li&gt;
&lt;li&gt;The browser SPA supplies an incoming user token when the sample runs in user-context mode.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Outside the container, Microsoft Entra holds the agent identity blueprint, its blueprint principal, the child agent identity, the federated identity credential, and the delegated grants. The final Agent ID token is passed to the Microsoft MCP Server for enterprise. The model call proves that the token is usable, but it is downstream of the identity mechanism we care about.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;SPIFFE workload plane                     Microsoft Entra control plane

+---------------------------+            +-----------------------------+
| FastAPI agent workload    |            | Agent Identity Blueprint    |
+---------+-----------------+            +-------------------+---------+
          |                                                  |
          | runtime evidence                                 |
          v                                                  |
+---------+-----------+                                      | Federated Identity
| SPIRE Agent         |                                      | Credential
| Workload API        |                                      |
+-------+-------------+                                      |
        | attestation                                        |
        v                                                    |
+-------+-----------------+      signing keys       +--------+--------+
| SPIRE Server            |------------------------&amp;gt;| OIDC metadata   |
| issues JWT-SVID         |                         | and public JWKS |
+-----------+-------------+                         +-------+---------+
            |                                               ^
            | short-lived JWT-SVID                          |
            v                                               | validates
+-----------+-------------+  JWT-SVID + fmi_path            |
| FastAPI agent workload  |--------------------------------&amp;gt;|
+-------+-----------------+                                 |
        ^                                                   |
        | T1 exchange token                                 |
        +---------------------------------------------------+
        |
        | T1 as child assertion
        v
+-------------+-------------+                +-----------------------------+
| Child agent identity      |---------------&amp;gt;| Downstream MCP resource     |
| requests resource token   |    Agent ID    | validates Entra token       |
+---------------------------+    token       +-----------------------------+
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The diagram contains two identity planes. The SPIFFE plane ends when the workload receives its JWT-SVID. The Entra plane begins when that JWT is presented as the blueprint's client assertion.&lt;/p&gt;

&lt;h2&gt;
  
  
  How the process becomes a SPIFFE workload
&lt;/h2&gt;

&lt;p&gt;The container entrypoint starts the SPIRE Server, creates a join token for the co-located agent, and registers one workload entry (&lt;a href="https://github.com/astaykov/agentid-spiffe/blob/main/src/entrypoint.sh" rel="noopener noreferrer"&gt;entrypoint source&lt;/a&gt;):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;spire-server entry create &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-parentID&lt;/span&gt; spiffe://poc.local/agent/node1 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-spiffeID&lt;/span&gt; spiffe://poc.local/agent/identity-1 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-selector&lt;/span&gt; unix:uid:0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Read that registration from right to left. A process matching &lt;code&gt;unix:uid:0&lt;/code&gt;, attested through the registered parent agent, receives &lt;code&gt;spiffe://poc.local/agent/identity-1&lt;/code&gt; as its workload identity. The SPIFFE ID is not generated from the process name or container name. It is the explicit result of registration policy and attested selectors, which is the model SPIRE uses to issue identities through its Workload API (&lt;a href="https://spiffe.io/docs/latest/deploying/registering/" rel="noopener noreferrer"&gt;SPIRE workload registration&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;The selector is intentionally broad because every process in the image runs in the same lab boundary. A production deployment would use workload and node attestors that express the real platform boundary. That decision is outside this PoC. We only need a reliably attested process from which to request the JWT-SVID.&lt;/p&gt;

&lt;p&gt;The server configuration then defines the JWT issuer and signing profile (&lt;a href="https://github.com/astaykov/agentid-spiffe/blob/main/src/spire/server.conf" rel="noopener noreferrer"&gt;SPIRE server configuration in the PoC&lt;/a&gt;):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;server&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;trust_domain&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"poc.local"&lt;/span&gt;
    &lt;span class="nx"&gt;jwt_issuer&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"__OIDC_ISSUER_URL__"&lt;/span&gt;
    &lt;span class="nx"&gt;jwt_key_type&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"rsa-2048"&lt;/span&gt;
    &lt;span class="nx"&gt;default_jwt_svid_ttl&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"10m"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;trust_domain&lt;/code&gt; controls the SPIFFE ID namespace. &lt;code&gt;jwt_issuer&lt;/code&gt; becomes the JWT-SVID &lt;code&gt;iss&lt;/code&gt; claim. &lt;code&gt;jwt_key_type&lt;/code&gt; makes SPIRE sign the assertion with RSA so that the token uses RS256. These values are related, but only the issuer crosses directly into the Entra configuration.&lt;/p&gt;

&lt;p&gt;And this is where the most important interoperability detail appears.&lt;/p&gt;

&lt;h2&gt;
  
  
  The issuer is a public protocol surface, not a string
&lt;/h2&gt;

&lt;p&gt;It is tempting to treat the federated identity credential's &lt;code&gt;issuer&lt;/code&gt; as an identifier. Configure the same URL in SPIRE and Entra, make the strings match, and move on.&lt;/p&gt;

&lt;p&gt;That is not enough.&lt;/p&gt;

&lt;p&gt;Microsoft Entra documents two requirements that make the issuer an operational endpoint. First, RS256 is the supported signing algorithm for workload identity federation assertions. Other algorithms might work, but Microsoft does not test them. A robust implementation therefore needs an RS256-signed JWT, which is why this PoC explicitly configures &lt;code&gt;jwt_key_type = "rsa-2048"&lt;/code&gt; (&lt;a href="https://learn.microsoft.com/entra/workload-id/workload-identity-federation-considerations#supported-signing-algorithms-and-issuers" rel="noopener noreferrer"&gt;supported signing algorithms and issuers&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;Second, the issuer must be a publicly reachable HTTPS URL that follows OIDC discovery. Microsoft Entra uses the configured issuer to retrieve the metadata and signing keys needed to validate the external token (&lt;a href="https://learn.microsoft.com/entra/workload-id/workload-identity-federation-create-trust#other-identity-providers-example" rel="noopener noreferrer"&gt;configure trust with another identity provider&lt;/a&gt;). For the issuer used by this PoC, the primary discovery URL is formed by appending &lt;code&gt;/.well-known/openid-configuration&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Issuer
https://&amp;lt;container-app-host&amp;gt;/spiffe-oidc

Discovery document
https://&amp;lt;container-app-host&amp;gt;/spiffe-oidc/.well-known/openid-configuration
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The discovery document points Entra to the public key set:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"issuer"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://&amp;lt;container-app-host&amp;gt;/spiffe-oidc"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"jwks_uri"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://&amp;lt;container-app-host&amp;gt;/spiffe-oidc/keys"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"subject_types_supported"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"public"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Entra follows &lt;code&gt;jwks_uri&lt;/code&gt;, finds the public key whose &lt;code&gt;kid&lt;/code&gt; matches the JWT-SVID header, and uses that key to verify the RS256 signature. The private signing key remains inside SPIRE. OIDC discovery publishes only the public half required by verifiers, following the standard discovery and JWKS model (&lt;a href="https://openid.net/specs/openid-connect-discovery-1_0.html" rel="noopener noreferrer"&gt;OpenID Connect Discovery 1.0&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;This is why an exact &lt;code&gt;iss&lt;/code&gt; match can still fail. The URL might be spelled correctly while the discovery document is missing, inaccessible, malformed, or pointing at a JWKS that does not contain the active signing key. In that state, Entra knows &lt;em&gt;where&lt;/em&gt; the assertion claims to come from but cannot cryptographically prove that the issuer signed it.&lt;/p&gt;

&lt;p&gt;The sample runs SPIRE's OIDC Discovery Provider locally, then proxies its metadata and key set through the Container App's public HTTPS origin (&lt;a href="https://github.com/astaykov/agentid-spiffe/blob/main/src/backend/main.py" rel="noopener noreferrer"&gt;OIDC proxy implementation&lt;/a&gt;). The proxy rewrites &lt;code&gt;issuer&lt;/code&gt; and &lt;code&gt;jwks_uri&lt;/code&gt; to their public values, removes an empty authorization endpoint, populates &lt;code&gt;subject_types_supported&lt;/code&gt;, and adds &lt;code&gt;"use": "sig"&lt;/code&gt; to each published key for compatibility. It exposes both the OpenID Connect discovery path shown above and the RFC 8414 path form for an issuer containing a path:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://&amp;lt;container-app-host&amp;gt;/.well-known/openid-configuration/spiffe-oidc
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Both documents describe the same issuer and point to the same JWKS. That extra route avoids relying on ambiguous discovery behavior when the issuer itself contains &lt;code&gt;/spiffe-oidc&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The OIDC endpoint is not incidental web plumbing. It is part of the credential chain. If it disappears or publishes the wrong key, the JWT-SVID stops being a credential Entra can validate.&lt;/p&gt;

&lt;h2&gt;
  
  
  The federated identity credential binds one workload to the blueprint
&lt;/h2&gt;

&lt;p&gt;Once the public issuer works, the post-deployment configuration creates a federated identity credential on the &lt;em&gt;agent identity blueprint application&lt;/em&gt;. It does not create the credential on the child agent identity (&lt;a href="https://github.com/astaykov/agentid-spiffe/blob/main/scripts/configure-postdeploy.ps1" rel="noopener noreferrer"&gt;FIC configuration source&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;The effective object is small:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"spire-jwt-svid-blueprint"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"issuer"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://&amp;lt;container-app-host&amp;gt;/spiffe-oidc"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"subject"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"spiffe://poc.local/agent/identity-1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"audiences"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"api://AzureADTokenExchange"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here is a decoded JWT-SVID captured during one of the PoC runs. The signature is omitted, but the header and payload are unchanged:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  "alg": "RS256",
  "kid": "KNd3DSGQAvCP1U2rHUd3rBzf9oX1y7BG",
  "typ": "JWT"
}.{
  "aud": [
    "api://AzureADTokenExchange"
  ],
  "exp": 1785157382,
  "iat": 1785156782,
  "iss": "https://spiffe.proudhill-d482409c.westeurope.azurecontainerapps.io/spiffe-oidc",
  "sub": "spiffe://poc.local/agent/identity-1"
}.[Signature]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The header says that SPIRE signed the assertion with RS256 and identifies the public verification key through &lt;code&gt;kid&lt;/code&gt;. The payload names Entra's token exchange as the only audience, carries a ten-minute validity window, exposes the public OIDC issuer, and uses the registered SPIFFE ID as its subject. Entra retrieves the issuer's JWKS, selects &lt;code&gt;KNd3DSGQAvCP1U2rHUd3rBzf9oX1y7BG&lt;/code&gt;, verifies the omitted signature, and then evaluates the three FIC claim matches (&lt;a href="https://learn.microsoft.com/entra/workload-id/workload-identity-federation#how-it-works" rel="noopener noreferrer"&gt;workload identity federation mechanics&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;Those three trust properties map directly to the incoming JWT-SVID:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;JWT-SVID iss  == FIC issuer
JWT-SVID sub  == FIC subject
JWT-SVID aud  == FIC audiences[0]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Microsoft Entra compares those values exactly and case-sensitively when evaluating a standard federated identity credential (&lt;a href="https://learn.microsoft.com/graph/api/resources/federatedidentitycredentials-overview?view=graph-rest-1.0#how-do-federated-identity-credentials-work" rel="noopener noreferrer"&gt;federated identity credential matching&lt;/a&gt;). The FIC therefore says something precise: a token from this public issuer, for this one SPIFFE workload, minted specifically for Azure AD token exchange, may authenticate this blueprint.&lt;/p&gt;

&lt;p&gt;It does not grant the workload Microsoft Graph permissions. It does not authorize the model to use an MCP tool. It only replaces the blueprint's stored client secret or certificate with a short-lived external assertion. Authorization remains attached to the child agent identity and the downstream resource, consistent with the &lt;a href="https://learn.microsoft.com/entra/agent-id/agent-identities" rel="noopener noreferrer"&gt;Agent ID identity and authorization model&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The application requests the JWT-SVID only when MSAL needs it
&lt;/h2&gt;

&lt;p&gt;The FastAPI application opens the local SPIRE Workload API through the SPIRE Agent's Unix socket. Its assertion provider asks for one JWT-SVID with &lt;code&gt;api://AzureADTokenExchange&lt;/code&gt; as the audience (&lt;a href="https://github.com/astaykov/agentid-spiffe/blob/main/src/backend/main.py" rel="noopener noreferrer"&gt;backend source&lt;/a&gt;):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;SpiffeJwtProvider&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__call__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;svid&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;_ensure_source&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;fetch_svid&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;audience&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_audience&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;svid&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;token&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That audience matters. The &lt;a href="https://github.com/spiffe/spiffe/blob/main/standards/JWT-SVID.md#72-audience" rel="noopener noreferrer"&gt;JWT-SVID specification recommends a single audience&lt;/a&gt; because a JWT-SVID is a bearer token and every listed audience becomes a potential recipient. This token exists for one purpose: presentation to Entra's workload identity federation endpoint.&lt;/p&gt;

&lt;p&gt;The provider is passed to MSAL as a callable client assertion rather than as a static token:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;blueprint_app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;msal&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;ConfidentialClientApplication&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;BLUEPRINT_CLIENT_ID&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;client_credential&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;client_assertion&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;_jwt_svid_provider&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="n"&gt;authority&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;AUTHORITY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;MSAL can invoke a callable assertion provider when it needs a fresh credential, which fits a short-lived SVID better than capturing one token at process startup (&lt;a href="https://learn.microsoft.com/python/api/msal/msal.application.confidentialclientapplication" rel="noopener noreferrer"&gt;MSAL Python confidential client application&lt;/a&gt;). SPIRE remains responsible for issuing the workload credential. MSAL remains responsible for Entra token acquisition and caching.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;code&gt;fmi_path&lt;/code&gt; turns blueprint authentication into one agent identity
&lt;/h2&gt;

&lt;p&gt;The first Entra request authenticates the blueprint with the JWT-SVID and asks for an exchange token scoped to one child agent identity:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;blueprint_app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;acquire_token_for_client&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;api://AzureADTokenExchange/.default&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="n"&gt;fmi_path&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;agent_app_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;fmi_path&lt;/code&gt; value is the child agent identity's client ID. Microsoft documents that parameter as the instruction telling Entra which agent identity the blueprint is acting on behalf of (&lt;a href="https://learn.microsoft.com/entra/agent-id/autonomous-agent-authentication-authorization-flow#request-a-token-for-the-agent-identity-blueprint" rel="noopener noreferrer"&gt;request a token for the agent identity blueprint&lt;/a&gt;). The result is T1, an exchange token bound to the blueprint-to-child path.&lt;/p&gt;

&lt;p&gt;The child agent identity has no independent client secret, certificate, or FIC. The application creates a second MSAL confidential client for the child and supplies T1 through another assertion callback:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;assertion_cb&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;blueprint_app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;acquire_token_for_client&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;api://AzureADTokenExchange/.default&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="n"&gt;fmi_path&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;agent_app_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;access_token&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="n"&gt;agent_app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;msal&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;ConfidentialClientApplication&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;agent_app_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;client_credential&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;client_assertion&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;assertion_cb&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="n"&gt;authority&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;AUTHORITY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;From there, the flow can branch. In app-only mode, the child requests a resource token using its application permissions. In the PoC's user-context mode, it combines T1 with the incoming user's access token in an on-behalf-of exchange. Microsoft documents that the second request switches &lt;code&gt;client_id&lt;/code&gt; from the blueprint to the child agent identity and presents both the blueprint exchange token and user assertion (&lt;a href="https://learn.microsoft.com/entra/agent-id/agent-on-behalf-of-oauth-flow#protocol-steps" rel="noopener noreferrer"&gt;Agent ID OBO protocol&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;The JWT-SVID has already done its job before that branch. It authenticated the blueprint. T1 then authenticated the child agent identity. The final access token is scoped to the downstream resource and represents either the autonomous agent or the agent acting in the user's context, depending on the selected flow (&lt;a href="https://learn.microsoft.com/entra/agent-id/autonomous-agent-authentication-authorization-flow" rel="noopener noreferrer"&gt;authenticate and acquire tokens for autonomous agents&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;That separation is the reason this is more than generic workload federation. The external workload does not become an ordinary app registration. It enters a blueprint-controlled path and emerges as a governed child agent identity.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the PoC proves
&lt;/h2&gt;

&lt;p&gt;The sample proves four useful things.&lt;/p&gt;

&lt;p&gt;First, a JWT-SVID can serve as the real client assertion for an Entra Agent ID blueprint. Microsoft lists SPIFFE and SPIRE as a supported workload identity federation scenario, and Agent ID supports direct workload identity exchange for third-party agent platforms (&lt;a href="https://learn.microsoft.com/entra/workload-id/workload-identity-federation#supported-scenarios" rel="noopener noreferrer"&gt;supported workload identity federation scenarios&lt;/a&gt;, &lt;a href="https://learn.microsoft.com/entra/agent-id/configure-third-party-agents#integration-patterns-for-third-party-agents" rel="noopener noreferrer"&gt;third-party Agent ID integration patterns&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;Second, the SPIFFE ID maps cleanly into the FIC subject. The workload identity &lt;code&gt;spiffe://poc.local/agent/identity-1&lt;/code&gt; remains visible at the trust boundary instead of being translated into an unrelated shared secret.&lt;/p&gt;

&lt;p&gt;Third, the public issuer surface is as important as the token. Federation succeeds only when the JWT &lt;code&gt;iss&lt;/code&gt;, FIC issuer, discovery document &lt;code&gt;issuer&lt;/code&gt;, discovery URL, JWKS location, active &lt;code&gt;kid&lt;/code&gt;, and RS256 signature all describe the same cryptographic authority. Microsoft Entra retrieves the external keys through that OIDC issuer contract before accepting the assertion (&lt;a href="https://learn.microsoft.com/entra/workload-id/workload-identity-federation#how-it-works" rel="noopener noreferrer"&gt;workload identity federation mechanics&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;Fourth, the identity that calls the downstream API is still the child agent identity. SPIRE authenticates the workload to the blueprint. It does not bypass Agent ID's parent-child token flow, consent, or downstream authorization model (&lt;a href="https://learn.microsoft.com/entra/agent-id/agent-identities" rel="noopener noreferrer"&gt;agent identities&lt;/a&gt;).&lt;/p&gt;

&lt;h2&gt;
  
  
  What the PoC deliberately does not prove
&lt;/h2&gt;

&lt;p&gt;The repository is unusually clear about its shortcuts, and that clarity matters.&lt;/p&gt;

&lt;p&gt;SPIRE state is ephemeral. Every Container App start deletes the server and agent data, regenerating the CA material, JWT signing key, and JWT &lt;code&gt;kid&lt;/code&gt;. Microsoft Entra can retain previously retrieved issuer metadata and keys, so exchanges can fail after a restart until its view refreshes (&lt;a href="https://github.com/astaykov/agentid-spiffe#spiffe-jwt-svid-to-entra-agent-id-poc" rel="noopener noreferrer"&gt;repository warning&lt;/a&gt;). This is the loudest reason not to mistake the sample for a production SPIRE topology.&lt;/p&gt;

&lt;p&gt;The agent uses a join token and &lt;code&gt;insecure_bootstrap = true&lt;/code&gt;, while the server and agent are co-located with the workload. Those choices remove infrastructure that would distract from the exchange, but they are explicitly marked as PoC-only in the &lt;a href="https://github.com/astaykov/agentid-spiffe/blob/main/src/spire/agent.conf" rel="noopener noreferrer"&gt;agent configuration&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The backend also logs the full JWT-SVID to simplify troubleshooting while the FIC is being wired up (&lt;a href="https://github.com/astaykov/agentid-spiffe/blob/main/src/backend/main.py" rel="noopener noreferrer"&gt;logging code&lt;/a&gt;). Do not carry that behavior into production. A JWT-SVID is a bearer credential susceptible to replay during its validity window, so it must not enter application logs, traces, prompts, or tool output (&lt;a href="https://github.com/spiffe/spiffe/blob/main/standards/JWT-SVID.md#71-replay-protection" rel="noopener noreferrer"&gt;JWT-SVID replay considerations&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;None of those shortcuts invalidate the demonstrated identity exchange. They define its boundary. The sample proves that a correctly issued JWT-SVID, signed with RS256 and backed by public OIDC discovery, can authenticate an Agent ID blueprint. It does not prove that one ephemeral container is how an organization should operate SPIRE.&lt;/p&gt;

&lt;h2&gt;
  
  
  The trust contract is now executable
&lt;/h2&gt;

&lt;p&gt;The previous article ended with two control planes and one narrow trust contract. The PoC turns that sentence into running code.&lt;/p&gt;

&lt;p&gt;SPIRE attests the FastAPI process and issues &lt;code&gt;spiffe://poc.local/agent/identity-1&lt;/code&gt; a short-lived JWT-SVID. The OIDC discovery endpoint makes the active RSA public key available at a predictable public URL. The federated identity credential authorizes one exact issuer, SPIFFE ID, and audience to authenticate the blueprint. &lt;code&gt;fmi_path&lt;/code&gt; narrows the resulting T1 token to one child agent identity. That child obtains the final resource token under its own authorization boundary.&lt;/p&gt;

&lt;p&gt;No shared client secret crosses the container boundary. No X.509 certificate is copied into the agent. No custom token validator is added to Entra. The bridge uses the SPIFFE JWT identity format, standard OIDC discovery, Entra workload identity federation, and Agent ID's documented blueprint exchange (&lt;a href="https://learn.microsoft.com/entra/workload-id/workload-identity-federation" rel="noopener noreferrer"&gt;workload identity federation&lt;/a&gt;, &lt;a href="https://learn.microsoft.com/entra/agent-id/autonomous-agent-authentication-authorization-flow" rel="noopener noreferrer"&gt;Agent ID token acquisition&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;The issuer configuration is the hinge. It must be exact, public, standards-shaped, and cryptographically aligned with the JWT in flight. Get that right, and the SPIFFE workload can enter Entra as a governed agent identity without ever owning a long-lived Entra credential.&lt;/p&gt;

&lt;p&gt;That is the result this PoC was built to isolate.&lt;/p&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/astaykov/agentid-spiffe" rel="noopener noreferrer"&gt;SPIFFE JWT-SVID to Entra Agent ID PoC&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/astaykov/your-spiffe-workload-can-authenticate-as-an-entra-agent-id-but-mind-the-trust-gap-3969"&gt;Your SPIFFE workload can authenticate as an Entra Agent ID, but mind the trust gap&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/astaykov/your-agentic-platform-doesnt-need-a-single-secret-managed-identities-workload-identity-3602"&gt;Your agentic platform doesn't need a single secret&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://spiffe.io/docs/latest/spiffe-about/spiffe-concepts/" rel="noopener noreferrer"&gt;SPIFFE concepts&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://spiffe.io/docs/latest/spire-about/spire-concepts/" rel="noopener noreferrer"&gt;SPIRE concepts&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://spiffe.io/docs/latest/deploying/registering/" rel="noopener noreferrer"&gt;Registering workloads&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/spiffe/spiffe/blob/main/standards/JWT-SVID.md" rel="noopener noreferrer"&gt;JWT-SVID specification&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://openid.net/specs/openid-connect-discovery-1_0.html" rel="noopener noreferrer"&gt;OpenID Connect Discovery 1.0&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/entra/workload-id/workload-identity-federation" rel="noopener noreferrer"&gt;Workload identity federation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/graph/api/resources/federatedidentitycredentials-overview?view=graph-rest-1.0" rel="noopener noreferrer"&gt;Overview of federated identity credentials&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/entra/workload-id/workload-identity-federation-create-trust" rel="noopener noreferrer"&gt;Configure an application to trust an external identity provider&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/entra/workload-id/workload-identity-federation-considerations" rel="noopener noreferrer"&gt;Important considerations for federated identity credentials&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/entra/agent-id/configure-third-party-agents" rel="noopener noreferrer"&gt;Integrate third-party agents with Microsoft Entra Agent ID&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/entra/agent-id/autonomous-agent-authentication-authorization-flow" rel="noopener noreferrer"&gt;Authenticate and acquire tokens for autonomous agents&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/entra/agent-id/agent-on-behalf-of-oauth-flow" rel="noopener noreferrer"&gt;Agent OAuth on-behalf-of flow&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/entra/agent-id/agent-identities" rel="noopener noreferrer"&gt;Agent identities in Microsoft Entra Agent ID&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/entra/identity-platform/v2-oauth2-client-creds-grant-flow#third-case-access-token-request-with-a-federated-credential" rel="noopener noreferrer"&gt;OAuth 2.0 client credentials flow with a federated credential&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/python/api/msal/msal.application.confidentialclientapplication" rel="noopener noreferrer"&gt;MSAL Python ConfidentialClientApplication&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>entraagentid</category>
      <category>spiffe</category>
      <category>workloadidentity</category>
      <category>identitysecurity</category>
    </item>
    <item>
      <title>Your SPIFFE workload can authenticate as an Entra Agent ID, but mind the trust gap</title>
      <dc:creator>Anton Staykov</dc:creator>
      <pubDate>Tue, 04 Aug 2026 09:13:00 +0000</pubDate>
      <link>https://dev.to/astaykov/your-spiffe-workload-can-authenticate-as-an-entra-agent-id-but-mind-the-trust-gap-3969</link>
      <guid>https://dev.to/astaykov/your-spiffe-workload-can-authenticate-as-an-entra-agent-id-but-mind-the-trust-gap-3969</guid>
      <description>&lt;p&gt;SPIFFE with Microsoft Entra Agent ID sounds almost too tidy.&lt;/p&gt;

&lt;p&gt;Let SPIRE attest an AI agent running in Kubernetes, on-premises, or in another cloud. Give that workload a short-lived SPIFFE identity. Present the resulting token to Microsoft Entra. Receive an access token for a governed Entra Agent ID. No client secret, no certificate copied into a container, no cloud-specific identity shim.&lt;/p&gt;

&lt;p&gt;The architecture is viable. Microsoft explicitly lists &lt;a href="https://learn.microsoft.com/entra/workload-id/workload-identity-federation#supported-scenarios" rel="noopener noreferrer"&gt;SPIFFE and SPIRE among its supported workload identity federation scenarios&lt;/a&gt;, and Entra Agent ID supports &lt;a href="https://learn.microsoft.com/entra/agent-id/configure-third-party-agents#use-workload-identity-federation-direct-identity-exchange" rel="noopener noreferrer"&gt;third-party agents through direct workload identity federation&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;But the clean diagram hides a trust gap.&lt;/p&gt;

&lt;p&gt;SPIFFE proves &lt;em&gt;which workload is running&lt;/em&gt;. Microsoft Entra decides &lt;em&gt;which agent identity that workload may act as&lt;/em&gt;. A federated identity credential connects those two statements. If that mapping, its issuer surface, or its audience design is wrong, a short-lived token merely helps the wrong workload become the wrong agent faster.&lt;/p&gt;

&lt;p&gt;That is the pattern worth understanding. Not "SPIFFE replaces Entra" and not "Entra understands SPIFFE natively." The real design has two identity control planes joined by one exact, case-sensitive trust contract.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;SPIFFE workload plane                     Microsoft Entra control plane

+---------------------------+            +-----------------------------+
| AI agent workload         |            | Agent identity blueprint    |
|                           |            |                             |
| spiffe://example.com/     |            | FIC matches:                |
| agent/invoice-reconciler  |            |   iss + sub + aud           |
+-------------+-------------+            +--------------+--------------+
              |                                         |
              | 1. Attestation                          |
              v                                         |
+-------------+-------------+                           |
| SPIRE Server + Agent      |                           |
|                           |                           |
| Issues short-lived        |                           |
| JWT-SVID                  |                           |
+-------------+-------------+                           |
              |                                         |
              | 2. JWT-SVID + fmi_path                  |
              +----------------------------------------&amp;gt;|
                                                        |
                                          3. Returns T1 |
                                                        v
                                         +--------------+--------------+
                                         | Child agent identity        |
                                         |                             |
                                         | T1 exchanged for a          |
                                         | resource-scoped token       |
                                         +--------------+--------------+
                                                        |
              4. Entra access token                     |
              &amp;lt;-----------------------------------------+

The SPIFFE workload authenticates the blueprint.
The child agent identity is the principal authorized on downstream APIs.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  SPIFFE and Entra answer different questions
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://spiffe.io/docs/latest/spiffe-about/overview/" rel="noopener noreferrer"&gt;SPIFFE&lt;/a&gt; defines a platform-neutral identity framework for software workloads. Its central identifier is the SPIFFE ID, a URI such as &lt;code&gt;spiffe://example.com/agent/invoice-reconciler&lt;/code&gt;. A SPIFFE implementation such as SPIRE attests a workload using runtime evidence, then makes short-lived identity documents available through the &lt;a href="https://spiffe.io/docs/latest/deploying/svids/" rel="noopener noreferrer"&gt;SPIFFE Workload API&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Microsoft Entra Agent ID starts one layer higher. An &lt;a href="https://learn.microsoft.com/entra/agent-id/agent-identities" rel="noopener noreferrer"&gt;agent identity is a specialized service principal&lt;/a&gt; used to request tokens, receive tokens, and act either autonomously or in a user's context. The agent identity does not hold its own credential. Its &lt;a href="https://learn.microsoft.com/entra/agent-id/agent-identities#authorizing-agent-identities" rel="noopener noreferrer"&gt;agent identity blueprint holds credentials and acquires tokens on behalf of child agent identities&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;That division is useful:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;SPIRE answers: &lt;em&gt;is this the workload that my runtime policy registered?&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;Entra answers: &lt;em&gt;which enterprise agent identity may this authenticated workload use, and what may that identity access?&lt;/em&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The distinction matters because workload authentication is not downstream authorization. A valid SPIFFE identity does not grant Microsoft Graph permissions, an Azure role, or consent to an API scope. It only supplies evidence that Entra can accept as a credential for the blueprint. The resulting agent identity remains the principal that needs authorization on the target resource, a separation Microsoft also makes explicit for &lt;a href="https://learn.microsoft.com/entra/agent-id/agent-identities" rel="noopener noreferrer"&gt;agent identity token acquisition&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Think of SPIRE as the passport office and Entra as the border authority. One establishes that the document belongs to a particular workload. The other decides which governed identity crosses the border and what that identity may do after entry.&lt;/p&gt;

&lt;h2&gt;
  
  
  Only one of the two SVID forms fits the exchange
&lt;/h2&gt;

&lt;p&gt;SPIFFE defines two common forms of SPIFFE Verifiable Identity Document, and they are not interchangeable.&lt;/p&gt;

&lt;p&gt;An &lt;a href="https://github.com/spiffe/spiffe/blob/main/standards/X509-SVID.md" rel="noopener noreferrer"&gt;X.509-SVID&lt;/a&gt; is an X.509 certificate in which the workload's SPIFFE ID appears as the single URI Subject Alternative Name. It is a strong fit for workload-to-workload mutual TLS. The workload proves possession of the corresponding private key while both sides validate certificate chains against SPIFFE trust bundles.&lt;/p&gt;

&lt;p&gt;A &lt;a href="https://github.com/spiffe/spiffe/blob/main/standards/JWT-SVID.md" rel="noopener noreferrer"&gt;JWT-SVID&lt;/a&gt; is a signed bearer JWT. Its &lt;code&gt;sub&lt;/code&gt; claim must be the workload's SPIFFE ID, and it must contain both &lt;code&gt;aud&lt;/code&gt; and &lt;code&gt;exp&lt;/code&gt;. That shape fits Microsoft Entra workload identity federation because Entra's client credentials flow accepts a JWT from an external identity provider as the &lt;code&gt;client_assertion&lt;/code&gt; (&lt;a href="https://learn.microsoft.com/entra/identity-platform/v2-oauth2-client-creds-grant-flow#third-case-access-token-request-with-a-federated-credential" rel="noopener noreferrer"&gt;federated client credential flow&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;This is the first catch: &lt;em&gt;an X.509-SVID is not a federated identity assertion for this flow&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;You cannot hand Entra an X.509-SVID and expect it to extract the URI SAN, validate the SPIFFE certificate chain, and perform the workload identity exchange. Microsoft documents the external credential as a JWT assertion with an issuer, subject, and audience that match a configured federated identity credential (&lt;a href="https://learn.microsoft.com/entra/workload-id/workload-identity-federation#how-it-works" rel="noopener noreferrer"&gt;workload identity federation mechanics&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;Keep X.509-SVIDs for mTLS inside the workload plane. Mint a dedicated JWT-SVID for the Entra token exchange. Trying to make one credential format do both jobs weakens the design rather than simplifying it.&lt;/p&gt;

&lt;h2&gt;
  
  
  The bridge is an exact three-claim contract
&lt;/h2&gt;

&lt;p&gt;A federated identity credential, or FIC, tells Entra which external JWT may authenticate a workload identity. Three values form the contract:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;issuer&lt;/code&gt; must match the incoming JWT's &lt;code&gt;iss&lt;/code&gt; claim.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;subject&lt;/code&gt; must match the incoming JWT's &lt;code&gt;sub&lt;/code&gt; claim.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;audiences&lt;/code&gt; must contain the audience Entra expects in the incoming JWT, normally &lt;code&gt;api://AzureADTokenExchange&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Microsoft performs this match &lt;a href="https://learn.microsoft.com/graph/api/resources/federatedidentitycredentials-overview?view=graph-rest-1.0#how-do-federated-identity-credentials-work" rel="noopener noreferrer"&gt;exactly and case-sensitively&lt;/a&gt;. Wildcards are not supported in standard FIC property values, and an application or agent identity blueprint can have at most 20 FICs (&lt;a href="https://learn.microsoft.com/entra/workload-id/workload-identity-federation-considerations#general-federated-identity-credential-considerations" rel="noopener noreferrer"&gt;FIC considerations&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;For one production agent workload, the incoming JWT-SVID could look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"iss"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://oidc.example.com"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"sub"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"spiffe://example.com/agent/invoice-reconciler"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"aud"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"api://AzureADTokenExchange"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"exp"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1784304000&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The corresponding FIC on the agent identity blueprint's application object is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"spiffe-production-invoice-agent"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"issuer"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://oidc.example.com"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"subject"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"spiffe://example.com/agent/invoice-reconciler"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"audiences"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"api://AzureADTokenExchange"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Incoming JWT-SVID                         Federated identity credential

iss: https://oidc.example.com       ===&amp;gt; issuer: https://oidc.example.com

sub: spiffe://example.com/          ===&amp;gt; subject: spiffe://example.com/
  agent/invoice-reconciler                    agent/invoice-reconciler

aud: api://AzureADTokenExchange     ===&amp;gt; audiences:
                 api://AzureADTokenExchange

      EXACT, CASE-SENSITIVE MATCH

This mapping authenticates the blueprint. It does not grant downstream
permissions or carry a user's delegated authorization context.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;subject&lt;/code&gt; is where SPIFFE fits naturally. The &lt;a href="https://github.com/spiffe/spiffe/blob/main/standards/JWT-SVID.md#31-subject" rel="noopener noreferrer"&gt;JWT-SVID specification requires &lt;code&gt;sub&lt;/code&gt; to equal the workload's SPIFFE ID&lt;/a&gt;, so the FIC binds one precise SPIFFE workload identity to one blueprint credential path.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;audience&lt;/code&gt; is where architecture becomes policy. SPIFFE allows site-specific audiences and recommends scoping them to the intended recipient (&lt;a href="https://github.com/spiffe/spiffe/blob/main/standards/JWT-SVID.md#32-audience" rel="noopener noreferrer"&gt;JWT-SVID audience requirements&lt;/a&gt;). For this token, the intended recipient is Entra's token exchange. The workload should therefore request a JWT-SVID with the single audience &lt;code&gt;api://AzureADTokenExchange&lt;/code&gt;, matching the FIC.&lt;/p&gt;

&lt;p&gt;Do not reuse a JWT-SVID minted for an internal API. Do not add Entra as a second audience to a token already used elsewhere. A recipient of a multi-audience bearer token can replay that token to another listed audience, which is why the SPIFFE specification &lt;a href="https://github.com/spiffe/spiffe/blob/main/standards/JWT-SVID.md#72-audience" rel="noopener noreferrer"&gt;strongly recommends single-audience JWT-SVIDs&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The token flow, without the hand-waving
&lt;/h2&gt;

&lt;p&gt;The autonomous flow has seven steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;SPIRE attests the agent workload according to its registration and runtime selectors.&lt;/li&gt;
&lt;li&gt;The workload requests a JWT-SVID from the local Workload API with &lt;code&gt;api://AzureADTokenExchange&lt;/code&gt; as its sole audience.&lt;/li&gt;
&lt;li&gt;SPIRE issues a short-lived JWT-SVID whose &lt;code&gt;sub&lt;/code&gt; is the workload's SPIFFE ID.&lt;/li&gt;
&lt;li&gt;The workload sends that JWT to the Microsoft identity platform token endpoint as &lt;code&gt;client_assertion&lt;/code&gt;, requests &lt;code&gt;api://AzureADTokenExchange/.default&lt;/code&gt;, and supplies the child agent identity's client ID in &lt;code&gt;fmi_path&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Entra discovers the external issuer's signing keys, validates the JWT, compares &lt;code&gt;iss&lt;/code&gt;, &lt;code&gt;sub&lt;/code&gt;, and &lt;code&gt;aud&lt;/code&gt; against the FIC on the blueprint, and returns the blueprint exchange token &lt;code&gt;T1&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;The workload sends &lt;code&gt;T1&lt;/code&gt; as the &lt;code&gt;client_assertion&lt;/code&gt; in a second request whose &lt;code&gt;client_id&lt;/code&gt; is the child agent identity and whose scope names the downstream resource.&lt;/li&gt;
&lt;li&gt;Entra validates the blueprint-to-child relationship and issues a resource-scoped access token in which the agent identity, not the SPIFFE workload, is the enterprise principal.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The first request uses the JWT-SVID to authenticate the blueprint. The &lt;code&gt;fmi_path&lt;/code&gt; parameter selects the child agent identity for which the blueprint is requesting an exchange token, exactly as documented in the &lt;a href="https://learn.microsoft.com/entra/agent-id/autonomous-agent-authentication-authorization-flow#request-a-token-for-the-agent-identity-blueprint" rel="noopener noreferrer"&gt;autonomous agent authentication flow&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;POST https://login.microsoftonline.com/{tenant-id}/oauth2/v2.0/token
Content-Type: application/x-www-form-urlencoded

client_id={agent-identity-blueprint-client-id}
&amp;amp;scope=api%3A%2F%2FAzureADTokenExchange%2F.default
&amp;amp;fmi_path={agent-identity-client-id}
&amp;amp;client_assertion_type=urn%3Aietf%3Aparams%3Aoauth%3Aclient-assertion-type%3Ajwt-bearer
&amp;amp;client_assertion={jwt-svid}
&amp;amp;grant_type=client_credentials
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That request returns &lt;code&gt;T1&lt;/code&gt;, not the Microsoft Graph token. The second request switches &lt;code&gt;client_id&lt;/code&gt; from the blueprint to the child agent identity and uses &lt;code&gt;T1&lt;/code&gt; as its assertion (&lt;a href="https://learn.microsoft.com/entra/agent-id/autonomous-agent-authentication-authorization-flow#request-an-agent-identity-token" rel="noopener noreferrer"&gt;request an agent identity token&lt;/a&gt;):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;POST https://login.microsoftonline.com/{tenant-id}/oauth2/v2.0/token
Content-Type: application/x-www-form-urlencoded

client_id={agent-identity-client-id}
&amp;amp;scope=https%3A%2F%2Fgraph.microsoft.com%2F.default
&amp;amp;client_assertion_type=urn%3Aietf%3Aparams%3Aoauth%3Aclient-assertion-type%3Ajwt-bearer
&amp;amp;client_assertion={agent-blueprint-token-T1}
&amp;amp;grant_type=client_credentials
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The blueprint authenticates with the external assertion, but the child agent identity becomes the principal in the final resource token. That distinction is not administrative trivia. If the downstream API authorizes the blueprint rather than the agent identity, the architecture has collapsed the attribution and least-privilege boundary it adopted Agent ID to create.&lt;/p&gt;

&lt;h2&gt;
  
  
  A SPIFFE bundle endpoint is not OIDC discovery
&lt;/h2&gt;

&lt;p&gt;This is the catch most diagrams omit.&lt;/p&gt;

&lt;p&gt;SPIFFE defines how trust bundles carry X.509 authorities and JWT signing keys. Its &lt;a href="https://github.com/spiffe/spiffe/blob/main/standards/SPIFFE_Federation.md" rel="noopener noreferrer"&gt;federation specification&lt;/a&gt; defines bundle endpoints through which one SPIFFE trust domain can retrieve another domain's current bundle. JWT-SVID signing keys appear in a SPIFFE bundle as JWK entries whose &lt;code&gt;use&lt;/code&gt; value is &lt;code&gt;jwt-svid&lt;/code&gt; (&lt;a href="https://github.com/spiffe/spiffe/blob/main/standards/JWT-SVID.md#6-representation-in-the-spiffe-bundle" rel="noopener noreferrer"&gt;JWT-SVID key publication&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;Microsoft Entra expects something different. The configured FIC issuer must be a URL compatible with OIDC discovery, and Entra uses that issuer URL to retrieve the keys required to validate the external token (&lt;a href="https://learn.microsoft.com/entra/workload-id/workload-identity-federation-create-trust#other-identity-providers" rel="noopener noreferrer"&gt;external issuer configuration&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;A SPIFFE bundle endpoint and an OIDC discovery endpoint both publish trust material over HTTPS, but they are not the same protocol surface. Similar nouns do not create interoperability.&lt;/p&gt;

&lt;p&gt;The production architecture therefore needs an issuer service that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;exposes &lt;code&gt;/.well-known/openid-configuration&lt;/code&gt; for the configured HTTPS issuer;&lt;/li&gt;
&lt;li&gt;exposes a JWKS endpoint referenced by that discovery document;&lt;/li&gt;
&lt;li&gt;publishes the active SPIRE JWT signing keys in an Entra-compatible JWKS;&lt;/li&gt;
&lt;li&gt;keeps the JWT-SVID &lt;code&gt;iss&lt;/code&gt; claim exactly aligned with the configured issuer URL;&lt;/li&gt;
&lt;li&gt;rotates published keys without creating a window in which valid JWT-SVIDs cannot be verified.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That service is part of your identity control plane. Treating it as a static JSON file on an incidental web server is the identity equivalent of putting a production certificate chain in a shared folder and calling it PKI.&lt;/p&gt;

&lt;p&gt;Microsoft also documents that the identity platform stores only the first 100 signing keys downloaded from an external OIDC endpoint (&lt;a href="https://learn.microsoft.com/entra/workload-id/workload-identity-federation#how-it-works" rel="noopener noreferrer"&gt;workload identity federation mechanics&lt;/a&gt;). That limit should be easy to respect, but it makes stale-key accumulation an operational concern rather than harmless clutter.&lt;/p&gt;

&lt;h2&gt;
  
  
  Short-lived bearer tokens can still be replayed
&lt;/h2&gt;

&lt;p&gt;JWT-SVIDs reduce credential lifetime. They do not eliminate bearer-token behavior.&lt;/p&gt;

&lt;p&gt;The SPIFFE specification is direct: &lt;a href="https://github.com/spiffe/spiffe/blob/main/standards/JWT-SVID.md#71-replay-protection" rel="noopener noreferrer"&gt;JWT-SVIDs are susceptible to replay&lt;/a&gt;. A short &lt;code&gt;exp&lt;/code&gt; narrows the window. A &lt;code&gt;jti&lt;/code&gt; can support replay detection, but JWT-SVID validators are not required to track &lt;code&gt;jti&lt;/code&gt; uniqueness. Confidential transport remains mandatory because possession of the token is sufficient to present it during its valid lifetime (&lt;a href="https://github.com/spiffe/spiffe/blob/main/standards/JWT-SVID.md#73-transport-security" rel="noopener noreferrer"&gt;JWT-SVID transport security&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;For this exchange, that produces a clear baseline:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;request the JWT-SVID only when an Entra token is needed;&lt;/li&gt;
&lt;li&gt;use one audience, &lt;code&gt;api://AzureADTokenExchange&lt;/code&gt;;&lt;/li&gt;
&lt;li&gt;keep JWT-SVID lifetime as short as the exchange path tolerates;&lt;/li&gt;
&lt;li&gt;never log the assertion or place it in agent prompts, tool output, or distributed traces;&lt;/li&gt;
&lt;li&gt;send it directly to Entra over TLS;&lt;/li&gt;
&lt;li&gt;cache the resulting resource token according to its lifetime, not the source assertion.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Short-lived credentials change the incident from "rotate a secret that may have escaped months ago" to "contain a replay window measured in minutes." That is a major improvement. It is not immunity.&lt;/p&gt;

&lt;h2&gt;
  
  
  Rotation now crosses an organizational boundary
&lt;/h2&gt;

&lt;p&gt;SPIRE owns workload attestation and JWT signing-key rotation. The team operating the OIDC facade owns discovery availability and JWKS publication. Microsoft Entra owns external-token validation and FIC matching. The Agent ID team owns blueprint and child identity boundaries.&lt;/p&gt;

&lt;p&gt;That is four operational responsibilities, even if one platform team implements all of them.&lt;/p&gt;

&lt;p&gt;Microsoft currently documents RS256 as the supported signing algorithm for workload identity federation token exchange, while noting that other algorithms may work but are not tested (&lt;a href="https://learn.microsoft.com/entra/workload-id/workload-identity-federation-considerations#supported-signing-algorithms-and-issuers" rel="noopener noreferrer"&gt;supported signing algorithms&lt;/a&gt;). An architect should therefore require the SPIRE JWT signing profile exposed to Entra to use RS256 rather than treating JOSE algorithm choice as an implementation detail.&lt;/p&gt;

&lt;p&gt;FIC changes also take time to propagate. Microsoft warns that token requests made shortly after a FIC change can fail with &lt;code&gt;AADSTS70021&lt;/code&gt; until regional caches update, and recommends retry logic (&lt;a href="https://learn.microsoft.com/entra/workload-id/workload-identity-federation-considerations#time-for-federated-credential-changes-to-propagate" rel="noopener noreferrer"&gt;FIC propagation&lt;/a&gt;). Key rollover and FIC rollout need staged tests, overlap, monitoring, and a rollback path. "The tokens are short-lived" does not make control-plane changes atomic.&lt;/p&gt;

&lt;h2&gt;
  
  
  The 20-credential limit is an architecture signal
&lt;/h2&gt;

&lt;p&gt;Standard FICs bind exact issuer and subject values, and each application or blueprint is limited to 20 credentials (&lt;a href="https://learn.microsoft.com/graph/api/resources/federatedidentitycredentials-overview?view=graph-rest-1.0#design-considerations" rel="noopener noreferrer"&gt;FIC design considerations&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;That limit should stop an enterprise from attaching every SPIFFE workload in a fleet to one blueprint. It reinforces the boundary model from [[Enterprise-architect-decision-framework-for-Entra-Agent-ID-blueprints-on-non-Microsoft-platforms]]: one blueprint should represent a deliberate trust and blast-radius boundary, not become a universal token vending machine.&lt;/p&gt;

&lt;p&gt;Microsoft has introduced &lt;a href="https://learn.microsoft.com/entra/workload-id/workload-identities-flexible-federated-identity-credentials" rel="noopener noreferrer"&gt;flexible federated identity credentials&lt;/a&gt; that can match subject patterns and selected custom claims, but the feature is currently preview. Pattern matching also changes the risk model. An exact SPIFFE ID binds one registered workload to the blueprint. A claims expression binds a class of workloads. That can reduce object count, but it increases the importance of SPIRE registration policy and namespace governance.&lt;/p&gt;

&lt;p&gt;Scale is not permission to blur identity boundaries. If 20 exact bindings are insufficient, first ask whether the blueprint boundary is too broad. Only then consider a flexible expression.&lt;/p&gt;

&lt;h2&gt;
  
  
  Workload federation authenticates the client in both autonomous and OBO flows
&lt;/h2&gt;

&lt;p&gt;The source assertion proves the workload's identity. That makes the JWT-SVID a &lt;em&gt;client credential&lt;/em&gt;, not a statement about whether the resulting flow is autonomous or delegated.&lt;/p&gt;

&lt;p&gt;Microsoft separates autonomous agent authorization from on-behalf-of authorization, but both flows still require the client to authenticate. In the autonomous flow, the SPIFFE-backed FIC authenticates the blueprint before the child agent identity requests a resource token with its own application permissions. In an OBO flow, the same client-authentication path can be combined with an incoming user assertion so that both agent and user context participate (&lt;a href="https://learn.microsoft.com/entra/identity-platform/v2-oauth2-on-behalf-of-flow" rel="noopener noreferrer"&gt;Microsoft identity platform OBO flow&lt;/a&gt;, &lt;a href="https://learn.microsoft.com/entra/agent-id/agent-on-behalf-of-oauth-flow" rel="noopener noreferrer"&gt;Agent ID OBO flow&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;For Agent ID, this remains a two-stage exchange. First, the blueprint presents the JWT-SVID as its &lt;code&gt;client_assertion&lt;/code&gt;, includes the child agent identity in &lt;code&gt;fmi_path&lt;/code&gt;, and receives &lt;code&gt;T1&lt;/code&gt;. The child agent identity then performs the OBO exchange by presenting both &lt;code&gt;T1&lt;/code&gt; as its client credential and the incoming user access token as the user assertion:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;POST https://login.microsoftonline.com/{tenant-id}/oauth2/v2.0/token
Content-Type: application/x-www-form-urlencoded

client_id={agent-identity-client-id}
&amp;amp;scope=https%3A%2F%2Fresource.example.com%2Fscope1
&amp;amp;client_assertion_type=urn%3Aietf%3Aparams%3Aoauth%3Aclient-assertion-type%3Ajwt-bearer
&amp;amp;client_assertion={agent-blueprint-token-T1}
&amp;amp;grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Ajwt-bearer
&amp;amp;assertion={incoming-user-access-token}
&amp;amp;requested_token_use=on_behalf_of
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The distinction between &lt;code&gt;client_assertion&lt;/code&gt; and &lt;code&gt;assertion&lt;/code&gt; is the architecture. &lt;code&gt;client_assertion&lt;/code&gt; proves which client is making the exchange. &lt;code&gt;assertion&lt;/code&gt; carries the signed-in user's delegated context. Workload identity federation replaces the client's stored secret; it does not replace the user token, user consent, delegated permissions, or OBO protocol.&lt;/p&gt;

&lt;p&gt;This also sharpens the connection to [[The-industry-does-not-have-an-agent-identity-propagation-standard-yet]]. SPIFFE can authenticate the workload at a cross-platform boundary, and Entra can combine that client authentication with an Entra-issued user assertion. SPIFFE still does not independently preserve a human subject across identity provider domains, capture consent, or define who is accountable for translating user context.&lt;/p&gt;

&lt;p&gt;Do not put a user's identifier into a private JWT-SVID claim and call the problem solved. The JWT-SVID authenticates the client. The incoming access token and OBO exchange carry delegated authorization.&lt;/p&gt;

&lt;h2&gt;
  
  
  The production decision
&lt;/h2&gt;

&lt;p&gt;Use this pattern when all of the following are true:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the organization already operates SPIRE as a workload identity authority;&lt;/li&gt;
&lt;li&gt;the agent runs outside Azure or across heterogeneous infrastructure;&lt;/li&gt;
&lt;li&gt;the agent needs a governed Microsoft Entra identity for autonomous or OBO access;&lt;/li&gt;
&lt;li&gt;eliminating blueprint secrets is worth operating an OIDC discovery and JWKS publication surface;&lt;/li&gt;
&lt;li&gt;SPIFFE registration policy and Entra blueprint ownership have accountable operators;&lt;/li&gt;
&lt;li&gt;exact workload-to-blueprint mappings fit the intended blast-radius model.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Do not use it as a shortcut around delegated consent or OBO. Use the JWT-SVID to authenticate the client, then supply the incoming user token through the documented &lt;code&gt;assertion&lt;/code&gt; parameter. Reject the pattern when your only available credential is an X.509-SVID or when nobody owns the issuer surface and key-rotation path.&lt;/p&gt;

&lt;p&gt;The final architecture is not "SPIFFE authenticates to Entra." That phrase hides too much.&lt;/p&gt;

&lt;p&gt;SPIRE attests a workload. A dedicated JWT-SVID presents that workload identity to a deliberately operated OIDC issuer surface. An exact FIC authorizes that issuer, SPIFFE ID, and audience to authenticate an agent identity blueprint. Entra then issues a resource token for the governed agent identity, whose permissions remain independently controlled.&lt;/p&gt;

&lt;p&gt;Two control planes. One narrow trust contract. No shared secret.&lt;/p&gt;

&lt;p&gt;That is a strong architecture, provided you operate all three parts of the sentence.&lt;/p&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://spiffe.io/docs/latest/spiffe-about/overview/" rel="noopener noreferrer"&gt;SPIFFE overview&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://spiffe.io/docs/latest/deploying/svids/" rel="noopener noreferrer"&gt;Working with SVIDs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/spiffe/spiffe/blob/main/standards/JWT-SVID.md" rel="noopener noreferrer"&gt;JWT-SVID specification&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/spiffe/spiffe/blob/main/standards/X509-SVID.md" rel="noopener noreferrer"&gt;X.509-SVID specification&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/spiffe/spiffe/blob/main/standards/SPIFFE_Federation.md" rel="noopener noreferrer"&gt;SPIFFE federation specification&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/entra/workload-id/workload-identity-federation" rel="noopener noreferrer"&gt;Microsoft Entra workload identity federation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/entra/workload-id/workload-identity-federation-considerations" rel="noopener noreferrer"&gt;Important considerations for federated identity credentials&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/entra/workload-id/workload-identity-federation-create-trust" rel="noopener noreferrer"&gt;Configure an app to trust an external identity provider&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/entra/agent-id/configure-third-party-agents" rel="noopener noreferrer"&gt;Integrate third-party agents with Microsoft Entra Agent ID&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/entra/agent-id/agent-identities" rel="noopener noreferrer"&gt;Agent identities in Microsoft Entra Agent ID&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/entra/agent-id/autonomous-agent-authentication-authorization-flow" rel="noopener noreferrer"&gt;Authenticate and acquire tokens for autonomous agents&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/entra/identity-platform/v2-oauth2-on-behalf-of-flow" rel="noopener noreferrer"&gt;Microsoft identity platform and OAuth 2.0 On-Behalf-Of flow&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/entra/agent-id/agent-on-behalf-of-oauth-flow" rel="noopener noreferrer"&gt;Agent OAuth flows: On-behalf-of flow&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/graph/api/resources/federatedidentitycredentials-overview?view=graph-rest-1.0" rel="noopener noreferrer"&gt;Federated identity credentials overview&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/entra/identity-platform/v2-oauth2-client-creds-grant-flow#third-case-access-token-request-with-a-federated-credential" rel="noopener noreferrer"&gt;OAuth 2.0 client credentials flow with a federated credential&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/entra/workload-id/workload-identities-flexible-federated-identity-credentials" rel="noopener noreferrer"&gt;Flexible federated identity credentials&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.rfc-editor.org/rfc/rfc7523" rel="noopener noreferrer"&gt;RFC 7523: JWT profile for OAuth 2.0 client authentication and authorization grants&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>entraagentid</category>
      <category>spiffe</category>
      <category>workloadidentity</category>
      <category>identitysecurity</category>
    </item>
    <item>
      <title>Microsoft Graph least privilege has two control planes</title>
      <dc:creator>Anton Staykov</dc:creator>
      <pubDate>Tue, 28 Jul 2026 09:37:00 +0000</pubDate>
      <link>https://dev.to/astaykov/microsoft-graph-least-privilege-has-two-control-planes-4hnf</link>
      <guid>https://dev.to/astaykov/microsoft-graph-least-privilege-has-two-control-planes-4hnf</guid>
      <description>&lt;p&gt;The Microsoft Graph least-privilege story has improved a lot. That is not the problem.&lt;/p&gt;

&lt;p&gt;The problem is that many teams are still looking at only half of it.&lt;/p&gt;

&lt;p&gt;Granular permissions such as &lt;code&gt;User.Create&lt;/code&gt;, &lt;code&gt;User.EnableDisableAccount.All&lt;/code&gt;, &lt;code&gt;User.RevokeSessions.All&lt;/code&gt;, and &lt;code&gt;Application.ReadWrite.OwnedBy&lt;/code&gt; are a better design surface than the old habit of reaching for &lt;code&gt;Directory.ReadWrite.All&lt;/code&gt; whenever an app needed to do anything useful in the directory. Microsoft Graph permissions intentionally follow a &lt;code&gt;{resource}.{operation}.{constraint}&lt;/code&gt; naming pattern, which lets engineers express the operation an app is allowed to perform instead of throwing the app into a tenant-wide bucket (&lt;a href="https://learn.microsoft.com/graph/permissions-overview#permissions-naming-pattern" rel="noopener noreferrer"&gt;Microsoft Graph permissions overview&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;That shift matters. But it does not finish the authorization model.&lt;/p&gt;

&lt;p&gt;The less understood truth is this: Microsoft Graph least privilege has &lt;em&gt;two control planes&lt;/em&gt;. The first is the Graph permission plane: what the client application is allowed to request from Microsoft Graph. The second is the Microsoft Entra control plane: who or what is allowed to control the application object, the service principal, and the directory objects the app will act on.&lt;/p&gt;

&lt;p&gt;If you design only the first plane, your architecture can still be wildly overprivileged.&lt;/p&gt;

&lt;h2&gt;
  
  
  The first plane: Graph permissions authorize the client
&lt;/h2&gt;

&lt;p&gt;Start with the piece engineers usually know.&lt;/p&gt;

&lt;p&gt;Microsoft Graph supports delegated access and app-only access. In delegated access, the app calls Microsoft Graph on behalf of a signed-in user. In app-only access, the app calls Microsoft Graph as itself, without a signed-in user (&lt;a href="https://learn.microsoft.com/graph/permissions-overview#permission-types" rel="noopener noreferrer"&gt;Microsoft Graph permissions overview&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;Delegated permissions are the easiest place to see the intersection model. Microsoft states it plainly: in a delegated scenario, the privileges an app has are determined by the Microsoft Graph permissions granted to the app &lt;em&gt;and&lt;/em&gt; the user's own permissions (&lt;a href="https://learn.microsoft.com/graph/permissions-overview#delegated-permissions" rel="noopener noreferrer"&gt;Microsoft Graph delegated permissions&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;That sentence should be printed on every onboarding app design review.&lt;/p&gt;

&lt;p&gt;If your custom HR onboarding app has been granted the &lt;em&gt;delegated&lt;/em&gt; permission &lt;code&gt;User.Create&lt;/code&gt;, the app has permission to call the create-user operation. But the signed-in user still needs the Entra authority to create users. The Graph permission authorizes the client. The Entra role authorizes the human. Delegated Microsoft Graph access needs both.&lt;/p&gt;

&lt;p&gt;Flip it around and the same lesson holds. If the signed-in user is a User Administrator, but the app has not been granted the right delegated Microsoft Graph permission, the user's admin role does not magically donate authority to the client. A user can perform an operation in the Entra admin center and still fail through a custom app because the app itself is not authorized for the Graph call.&lt;/p&gt;

&lt;p&gt;This is the first correction:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;In a delegated scenario, admin role assignment is not a substitute for Graph consent, and Graph consent is not a substitute for admin role assignment.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  The second plane: Entra controls who can control the thing doing the calling
&lt;/h2&gt;

&lt;p&gt;Now comes the part that gets missed.&lt;/p&gt;

&lt;p&gt;Microsoft Entra applications are represented by two related objects: the application object and the service principal object. The application object is the global template for the app. The service principal is the local representation of that app in a specific tenant, and it defines what the app can actually do in that tenant, who can access it, and what resources it can access (&lt;a href="https://learn.microsoft.com/entra/identity-platform/app-objects-and-service-principals" rel="noopener noreferrer"&gt;Application and service principal objects in Microsoft Entra ID&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;That distinction is not directory trivia. It is a control-plane boundary.&lt;/p&gt;

&lt;p&gt;The application object controls things like redirect URIs, credentials, &lt;em&gt;requested&lt;/em&gt; permissions, exposed APIs, and ownership. The service principal controls the enterprise application instance in the tenant: assignments, local policies, &lt;em&gt;consented&lt;/em&gt; permissions, provisioning configuration, sign-in behavior, and other tenant-specific settings (&lt;a href="https://learn.microsoft.com/entra/identity-platform/app-objects-and-service-principals#service-principal-object" rel="noopener noreferrer"&gt;Application and service principal objects&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;So when you ask, "is this Graph app least privileged?" the permission list is not enough. You also need to ask &lt;em&gt;who can mutate&lt;/em&gt; the app registration, &lt;em&gt;who can mutate the&lt;/em&gt; enterprise application, who can add credentials, who can add owners, who can change user assignment, and who can change the permissions the app requests.&lt;/p&gt;

&lt;p&gt;That is the second plane.&lt;/p&gt;

&lt;h2&gt;
  
  
  Owner is not a label, owner is control
&lt;/h2&gt;

&lt;p&gt;Application and enterprise application (service principal) ownership is one of the most underestimated privilege paths in Microsoft Entra.&lt;/p&gt;

&lt;p&gt;Microsoft documents that an owner of an enterprise application can manage the organization-specific configuration of the application, including single sign-on, provisioning, and user assignment. The owner can also add or remove other owners. More importantly, Microsoft states that owners have the same permissions as application administrators, scoped to an individual application (&lt;a href="https://learn.microsoft.com/entra/identity/enterprise-apps/overview-assign-app-owners" rel="noopener noreferrer"&gt;Enterprise application ownership&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;Read that again slowly: &lt;em&gt;application administrator, scoped to one application&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;That is not harmless metadata. &lt;strong&gt;That is delegated control over a highly sensitive object&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The default user permissions documentation is even more concrete:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Owners of application registrations can update application authentication (reply urls), &lt;strong&gt;credentials&lt;/strong&gt;, owners, permissions, policies, and basic properties. Owners of enterprise applications can update service principal &lt;strong&gt;credentials&lt;/strong&gt;, owners, permissions, &lt;strong&gt;app role assignments&lt;/strong&gt;, provisioning settings, and related policies (&lt;a href="https://learn.microsoft.com/entra/fundamentals/users-default-permissions#ownership-permissions" rel="noopener noreferrer"&gt;Default user permissions and ownership permissions&lt;/a&gt;).&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Microsoft also calls out the dangerous consequence directly: &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;... the application might have more permissions than the owner, which can become elevation of privilege because an owner can create or update objects while impersonating the application (&lt;a href="https://learn.microsoft.com/entra/identity/enterprise-apps/overview-assign-app-owners" rel="noopener noreferrer"&gt;Enterprise application ownership note&lt;/a&gt;).&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That is the part many app teams miss. The owner does not need to have the same directory role as the app. If the app has powerful application permissions, the person who can add a credential or change ownership can potentially create a path to act as the app.&lt;/p&gt;

&lt;p&gt;Static secrets get most of the attention here, but ownership is the quieter root. If I can become owner, add myself as another owner, add or rotate credentials, adjust the app configuration, or influence the service principal's local settings, I am no longer merely adjacent to the app. I am in the control plane of the app.&lt;/p&gt;

&lt;h2&gt;
  
  
  The bad pattern: broad Graph permission plus casual ownership
&lt;/h2&gt;

&lt;p&gt;Here is the pattern I still see too often.&lt;/p&gt;

&lt;p&gt;An engineering team builds a Graph automation app. They correctly avoid &lt;code&gt;Directory.ReadWrite.All&lt;/code&gt; and use a narrower Graph permission. Good. Then they leave three developers as owners of the app registration, two operations engineers as owners of the enterprise application, and a shared service account with broad rights because "someone needs to maintain it."&lt;/p&gt;

&lt;p&gt;On paper, the app permission set looks defensible. In reality, the control plane around the app is loose.&lt;/p&gt;

&lt;p&gt;That is not least privilege. That is narrow API permission sitting inside broad object control.&lt;/p&gt;

&lt;p&gt;The better question is not only "what Graph permissions does the app have?" The better question is: "who can change the identity that has those permissions?"&lt;/p&gt;

&lt;p&gt;If the answer is a long list of permanent owners, the app is overprivileged even if the Graph permission list looks elegant.&lt;/p&gt;

&lt;h2&gt;
  
  
  Scoped Entra roles are the missing design tool
&lt;/h2&gt;

&lt;p&gt;This is where Microsoft Entra role scoping becomes much more interesting than most app teams realize.&lt;/p&gt;

&lt;p&gt;Microsoft Entra roles are usually assigned at tenant scope, but Microsoft also supports assigning roles at narrower scopes, including application registrations and administrative units. Microsoft describes the resource a role assignment applies to as the scope, and supports restricted scope for built-in and custom roles (&lt;a href="https://learn.microsoft.com/entra/identity/role-based-access-control/manage-roles-portal" rel="noopener noreferrer"&gt;Assign Microsoft Entra roles&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;For app registrations, the model is direct. Instead of granting someone tenant-wide Application Administrator, you can assign a relevant built-in or even custom role scoped to a single app registration. Microsoft describes this as a way to let a principal update credentials and basic properties of a single app without granting tenant-wide application administration (&lt;a href="https://learn.microsoft.com/entra/identity/role-based-access-control/manage-roles-portal#assign-roles-with-app-registration-scope" rel="noopener noreferrer"&gt;Assign roles with app registration scope&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;The Graph shape is simple:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;POST https://graph.microsoft.com/v1.0/roleManagement/directory/roleAssignments
Content-Type: application/json

{
  "@odata.type": "#microsoft.graph.unifiedRoleAssignment",
  "principalId": "&amp;lt;principal-object-id&amp;gt;",
  "roleDefinitionId": "&amp;lt;role-definition-id&amp;gt;",
  "directoryScopeId": "/&amp;lt;application-object-id&amp;gt;"
}
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That &lt;code&gt;directoryScopeId&lt;/code&gt; value matters. For app registration scope, &lt;code&gt;/&amp;lt;id&amp;gt;&lt;/code&gt; means the principal can manage that Microsoft Entra object. Microsoft explicitly contrasts that with &lt;code&gt;/administrativeUnits/&amp;lt;id&amp;gt;&lt;/code&gt;, which means the principal can manage the members of the administrative unit based on the assigned role (&lt;a href="https://learn.microsoft.com/entra/identity/role-based-access-control/manage-roles-portal#assign-roles-with-app-registration-scope" rel="noopener noreferrer"&gt;Assign roles with app registration scope&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;That distinction is the whole architecture in miniature. Same role-assignment resource. Different scope semantics. Different blast radius.&lt;/p&gt;

&lt;h2&gt;
  
  
  Administrative units are not just for humans
&lt;/h2&gt;

&lt;p&gt;Administrative units are usually explained as a delegation feature for helpdesk and regional administration. That is true, but incomplete.&lt;/p&gt;

&lt;p&gt;Microsoft documents that Entra roles can be assigned with administrative-unit scope so the role permissions apply only when managing members of that administrative unit, not tenant-wide settings or unrelated directory objects (&lt;a href="https://learn.microsoft.com/entra/identity/role-based-access-control/manage-roles-portal#assign-roles-with-administrative-unit-scope" rel="noopener noreferrer"&gt;Assign roles with administrative unit scope&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;For example, a User Administrator scoped to the "Germany Employees" administrative unit can manage users in that administrative unit without becoming User Administrator for the whole tenant, subject to the role's supported administrative-unit behavior and restrictions (&lt;a href="https://learn.microsoft.com/entra/identity/role-based-access-control/manage-roles-portal#assign-roles-with-administrative-unit-scope" rel="noopener noreferrer"&gt;Administrative-unit role scope&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;That already matters for humans. It matters even more for automation.&lt;/p&gt;

&lt;p&gt;If you are building an onboarding app for five subsidiaries, why should the app's runtime identity be able to manage every user in the tenant? If the work is limited to five administrative units, the role assignment should be limited to those administrative units. &lt;/p&gt;

&lt;p&gt;This is the balance shift: Graph permissions are always tenant-wide. Entra role assignments can be scoped.&lt;/p&gt;

&lt;h2&gt;
  
  
  The widely unknown pattern: service principal plus administrative-unit scope
&lt;/h2&gt;

&lt;p&gt;Now for the part that is still too obscure.&lt;/p&gt;

&lt;p&gt;You can assign a Microsoft Entra role to a service principal at administrative-unit scope.&lt;/p&gt;

&lt;p&gt;This is not how most portal walkthroughs teach the feature. Most examples show a user as the principal. Some examples mention groups. The architecture pattern for app identities is still widely unknown. But the Graph contract supports the pieces.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;unifiedRoleAssignment&lt;/code&gt; resource represents a role definition assigned to a principal at a scope, and Microsoft lists supported principals as users, role-assignable groups, and service principals (&lt;a href="https://learn.microsoft.com/graph/api/resources/unifiedroleassignment?view=graph-rest-1.0" rel="noopener noreferrer"&gt;unifiedRoleAssignment resource type&lt;/a&gt;). The create-role-assignment API documents &lt;code&gt;directoryScopeId&lt;/code&gt; formats for the directory provider, including &lt;code&gt;/&lt;/code&gt; for tenant-wide scope, &lt;code&gt;/administrativeUnits/{administrativeunit-id}&lt;/code&gt; for administrative-unit scope, and &lt;code&gt;/{application-objectId}&lt;/code&gt; for application scope (&lt;a href="https://learn.microsoft.com/graph/api/rbacapplication-post-roleassignments?view=graph-rest-1.0" rel="noopener noreferrer"&gt;Create unifiedRoleAssignment&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;Put those two facts together and the payload becomes obvious:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;POST https://graph.microsoft.com/v1.0/roleManagement/directory/roleAssignments
Content-Type: application/json

{
  "@odata.type": "#microsoft.graph.unifiedRoleAssignment",
  "principalId": "&amp;lt;service-principal-object-id&amp;gt;",
  "roleDefinitionId": "&amp;lt;role-definition-id&amp;gt;",
  "directoryScopeId": "/administrativeUnits/&amp;lt;administrative-unit-id&amp;gt;"
}
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That assignment says: this service principal has this Entra role, but only for the members of this administrative unit, subject to the role's administrative-unit support.&lt;/p&gt;

&lt;p&gt;In practice, there is one more wrinkle engineers need to know. Microsoft documents that service principals and guest users do not receive directory read permissions by default, and service principals using an administrative-unit-scoped role assignment need corresponding &lt;em&gt;read&lt;/em&gt; permissions, commonly Directory Readers at tenant scope or another role that includes the necessary read permissions (&lt;a href="https://learn.microsoft.com/entra/identity/role-based-access-control/manage-roles-portal#assign-roles-with-administrative-unit-scope" rel="noopener noreferrer"&gt;Service principals and administrative-unit scoped role assignments&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;That tenant-scope read dependency annoys people at first. It should not. Read capability and write authority are different risks. A service principal might need enough tenant-level read to resolve directory objects while its actual administrative authority remains scoped to one administrative unit. That is still a materially better design than granting a tenant-wide write role because the app needs to manage one slice of the directory.&lt;/p&gt;

&lt;p&gt;The pattern is not theoretical. It is validated, battle-tested, and still underused.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why this changes Graph automation design
&lt;/h2&gt;

&lt;p&gt;Once you see the two planes, the design review changes.&lt;/p&gt;

&lt;p&gt;For a delegated app, you ask:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What Microsoft Graph delegated permission does the client need?&lt;/li&gt;
&lt;li&gt;Which Entra role must the signed-in user hold?&lt;/li&gt;
&lt;li&gt;Can that role be scoped to an administrative unit or application registration instead of the tenant?&lt;/li&gt;
&lt;li&gt;Who owns the app registration and enterprise application?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For an app-only automation identity, you ask:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What Microsoft Graph application permission does the service principal need?&lt;/li&gt;
&lt;li&gt;Can the operation be represented by an Entra role assignment instead of a broad Graph application permission?&lt;/li&gt;
&lt;li&gt;Can that Entra role be scoped to an administrative unit or specific (application) object?&lt;/li&gt;
&lt;li&gt;Does the service principal have only the directory read permissions needed to make that scoped role usable?&lt;/li&gt;
&lt;li&gt;Who can add credentials or owners to the app and service principal?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That last question is not an afterthought. It is often the difference between least privilege and theater.&lt;/p&gt;

&lt;h2&gt;
  
  
  A practical model for app teams
&lt;/h2&gt;

&lt;p&gt;Here is the model I would use with app and platform engineers.&lt;/p&gt;

&lt;p&gt;First, choose the narrowest Microsoft Graph permission that matches the API operation. Use &lt;code&gt;User.Create&lt;/code&gt; if the app creates users. Use &lt;code&gt;Application.ReadWrite.OwnedBy&lt;/code&gt; if the app only needs to manage applications it owns. Avoid directory-wide permissions unless the operation truly has no narrower resource-specific permission (&lt;a href="https://learn.microsoft.com/graph/permissions-reference" rel="noopener noreferrer"&gt;Microsoft Graph permissions reference&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;Second, decide whether the app needs delegated or app-only access. In delegated mode, remember that Graph evaluates the client permission &lt;strong&gt;and&lt;/strong&gt; the user's own authority. In app-only mode, remember that the app is the actor, so application permissions &lt;strong&gt;or role assignments&lt;/strong&gt; must be treated as production privilege (&lt;a href="https://learn.microsoft.com/graph/permissions-overview#permission-types" rel="noopener noreferrer"&gt;Microsoft Graph permission types&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;Third, scope Entra roles as aggressively as the business process allows. If the operator manages one app, use app-registration scope. If the automation manages one population, use administrative-unit scope. If the app really needs tenant-wide scope, make that an explicit exception, not the default (&lt;a href="https://learn.microsoft.com/entra/identity/role-based-access-control/manage-roles-portal" rel="noopener noreferrer"&gt;Assign Microsoft Entra roles at different scopes&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;Fourth, treat owners as privileged. Application owners and enterprise application owners should be reviewed like role assignments, because they can control the object that holds the app's permissions and credentials (&lt;a href="https://learn.microsoft.com/entra/fundamentals/users-default-permissions#ownership-permissions" rel="noopener noreferrer"&gt;Default user permissions and ownership permissions&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;Finally, keep the object model visible. The app registration and the service principal are not interchangeable. The app registration is the template. The service principal is the tenant-local instance that controls what the app can actually do in that tenant (&lt;a href="https://learn.microsoft.com/entra/identity-platform/app-objects-and-service-principals#relationship-between-application-objects-and-service-principals" rel="noopener noreferrer"&gt;Application and service principal relationship&lt;/a&gt;).&lt;/p&gt;

&lt;h2&gt;
  
  
  The sentence to bring to your next design review
&lt;/h2&gt;

&lt;p&gt;Least privilege for Microsoft Graph is not "we picked a smaller Graph permission."&lt;/p&gt;

&lt;p&gt;That is a good start. It is not the finish line.&lt;/p&gt;

&lt;p&gt;Least privilege for Microsoft Graph means the client has the narrowest Graph permission, the actor has the narrowest Entra authority, the role assignment has the narrowest scope, and the app object has the narrowest ownership model.&lt;/p&gt;

&lt;p&gt;Miss any one of those, and your beautiful permission list can still hide a control-plane problem.&lt;/p&gt;

&lt;p&gt;The industry is finally getting better Microsoft Graph permission granularity. Now app teams need to get equally serious about the Entra control plane around those permissions.&lt;/p&gt;

&lt;p&gt;Because the app that can touch your directory is only as least-privileged as the people and identities that can control the app.&lt;/p&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/graph/permissions-overview" rel="noopener noreferrer"&gt;Overview of Microsoft Graph permissions&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/graph/permissions-reference" rel="noopener noreferrer"&gt;Microsoft Graph permissions reference&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/entra/identity-platform/app-objects-and-service-principals" rel="noopener noreferrer"&gt;Application and service principal objects in Microsoft Entra ID&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/entra/identity/enterprise-apps/overview-assign-app-owners" rel="noopener noreferrer"&gt;Overview of enterprise application ownership in Microsoft Entra ID&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/entra/fundamentals/users-default-permissions#object-ownership" rel="noopener noreferrer"&gt;What are the default user permissions in Microsoft Entra ID?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/entra/identity/role-based-access-control/manage-roles-portal" rel="noopener noreferrer"&gt;Assign Microsoft Entra roles&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/graph/api/resources/unifiedroleassignment?view=graph-rest-1.0" rel="noopener noreferrer"&gt;unifiedRoleAssignment resource type&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/graph/api/rbacapplication-post-roleassignments?view=graph-rest-1.0" rel="noopener noreferrer"&gt;Create unifiedRoleAssignment&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>microsoftgraph</category>
      <category>microsoftentra</category>
      <category>leastprivilege</category>
      <category>appsecurity</category>
    </item>
    <item>
      <title>Enterprise architects: your overdue Entra decision is an agent CSA schema</title>
      <dc:creator>Anton Staykov</dc:creator>
      <pubDate>Thu, 23 Jul 2026 09:23:00 +0000</pubDate>
      <link>https://dev.to/astaykov/enterprise-architects-your-overdue-entra-decision-is-an-agent-csa-schema-2j5n</link>
      <guid>https://dev.to/astaykov/enterprise-architects-your-overdue-entra-decision-is-an-agent-csa-schema-2j5n</guid>
      <description>&lt;p&gt;If you are an enterprise architect working on Microsoft Entra and AI agents, your first overdue job is not another policy wizard, another dashboard, or another governance steering committee. It is schema design.&lt;/p&gt;

&lt;p&gt;Specifically, it is deciding how you classify non-human identities with &lt;a href="https://learn.microsoft.com/entra/fundamentals/custom-security-attributes-overview" rel="noopener noreferrer"&gt;custom security attributes in Microsoft Entra&lt;/a&gt;. Not eventually. Up front.&lt;/p&gt;

&lt;p&gt;I keep seeing the same pattern across customers of every size: teams move quickly on agent experimentation, they onboard identities, they test controls, and then they realize they have no consistent attribute language for policy scope. At that point, every policy becomes a naming convention problem in disguise.&lt;/p&gt;

&lt;p&gt;That is backwards.&lt;/p&gt;

&lt;h2&gt;
  
  
  The control plane starts with classification
&lt;/h2&gt;

&lt;p&gt;Custom security attributes are not decorative metadata. They are tenant-scoped key-value classifications you can assign to users, enterprise applications (service principals), and agent identities that are modeled as a service principal subtype, with dedicated role and permission boundaries for who can define and assign them (&lt;a href="https://learn.microsoft.com/entra/fundamentals/custom-security-attributes-overview" rel="noopener noreferrer"&gt;overview&lt;/a&gt;, &lt;a href="https://learn.microsoft.com/en-us/graph/api/resources/custom-security-attributes-overview?view=graph-rest-1.0" rel="noopener noreferrer"&gt;Graph model&lt;/a&gt;, &lt;a href="https://learn.microsoft.com/entra/agent-id/agent-service-principals" rel="noopener noreferrer"&gt;agent identity service principal model&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;That alone should change how architects think about them. This is not "nice to have taxonomy." This is policy input.&lt;/p&gt;

&lt;p&gt;Microsoft Entra Conditional Access for agents supports attribute-driven targeting with custom security attributes, and policy evaluation happens during token issuance and refresh, not just at policy authoring time (&lt;a href="https://learn.microsoft.com/en-us/entra/identity/conditional-access/agent-id#attribute-driven-conditional-access" rel="noopener noreferrer"&gt;Conditional Access for agents&lt;/a&gt;). In other words: if your classification is sloppy, your runtime decisions are sloppy.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why agents raise the stakes
&lt;/h2&gt;

&lt;p&gt;You can say "an agent identity is still a service principal" and be technically correct. Microsoft Entra Agent ID is built on service principal infrastructure (&lt;a href="https://learn.microsoft.com/entra/agent-id/agent-service-principals" rel="noopener noreferrer"&gt;agent identities, service principals, and applications&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;You can also miss the point.&lt;/p&gt;

&lt;p&gt;Agent identity introduces a blueprint-centered model where one blueprint can represent many agents, where blueprint-level policy decisions can affect all derived identities, and where operational accountability (owners and sponsors) is part of the model (&lt;a href="https://learn.microsoft.com/entra/agent-id/key-concepts" rel="noopener noreferrer"&gt;key concepts&lt;/a&gt;). This is not how most architects historically modeled "traditional" workload identities.&lt;/p&gt;

&lt;p&gt;So yes, same substrate. Different operating semantics.&lt;/p&gt;

&lt;p&gt;That difference is exactly why CSA strategy for agents should be deliberate, not inherited by accident from old workload identity templates.&lt;/p&gt;

&lt;h2&gt;
  
  
  Reuse your existing workload schema or create a dedicated agent schema?
&lt;/h2&gt;

&lt;p&gt;This is the question every architecture board eventually reaches.&lt;/p&gt;

&lt;p&gt;The honest answer is conditional, not ideological.&lt;/p&gt;

&lt;p&gt;Reusing your existing workload schema has real advantages. It accelerates rollout, avoids duplicate governance machinery, and can be enough if your workload identity estate is already cleanly governed and your agent population is still limited.&lt;/p&gt;

&lt;p&gt;But here is the trade-off many teams underestimate: semantic collision.&lt;/p&gt;

&lt;p&gt;When the same attribute family has to describe static daemon services, integration apps, and AI agents with interactive and autonomous behavior patterns, naming starts to drift. Policy intent gets fuzzy. People read "criticality" or "environment" and assume shared meaning where meaning has already diverged. Then policy scopes become misleading even when the syntax is valid.&lt;/p&gt;

&lt;p&gt;That is the failure mode I care about most. Not policy syntax errors. Policy &lt;em&gt;intent&lt;/em&gt; errors.&lt;/p&gt;

&lt;p&gt;For regulated enterprises, or any organization expecting meaningful agent scale, my default recommendation is to establish a dedicated agent CSA schema and keep crosswalk mappings to legacy workload attributes where needed. You can still align governance. You just stop pretending the populations are identical.&lt;/p&gt;

&lt;h2&gt;
  
  
  The architect's mistake: tactical granularity too early
&lt;/h2&gt;

&lt;p&gt;Most teams that finally commit to an agent schema make the same second mistake: they overengineer it.&lt;/p&gt;

&lt;p&gt;They try to encode every nuanced runtime trait into attributes on day one. They turn schema design into a cataloging exercise. They create dozens of values nobody will govern consistently.&lt;/p&gt;

&lt;p&gt;Do not do that.&lt;/p&gt;

&lt;p&gt;Strategic schema design means defining &lt;em&gt;long-lived control boundaries&lt;/em&gt;, not short-lived implementation detail. Microsoft documents clear limits and lifecycle constraints on custom security attributes, including tenant-level definition caps and immutable naming characteristics, which is exactly why restraint is a design virtue (&lt;a href="https://learn.microsoft.com/entra/fundamentals/custom-security-attributes-overview#limits-and-constraints" rel="noopener noreferrer"&gt;limits and constraints&lt;/a&gt;, &lt;a href="https://learn.microsoft.com/en-us/graph/api/resources/custom-security-attributes-overview?view=graph-rest-1.0" rel="noopener noreferrer"&gt;Graph overview&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;Start with coarse boundaries that support high-impact policy outcomes you know you will still care about two years from now.&lt;/p&gt;

&lt;h2&gt;
  
  
  Two coarse boundaries worth defining first
&lt;/h2&gt;

&lt;p&gt;First boundary: approved workload state.&lt;/p&gt;

&lt;p&gt;If your operating model says "no AI agent should sign in until explicitly approved," then encode exactly that as a first-class classification and enforce from there. This is the kind of architectural control objective that survives platform changes.&lt;/p&gt;

&lt;p&gt;Second boundary: department trust segmentation.&lt;/p&gt;

&lt;p&gt;If your operating model says "financial department agents should only authenticate within finance-defined trust boundaries," then encode that boundary at classification level before you layer finer controls. Entra policy and filtering surfaces are strongest when your classes reflect organizational trust zones, not implementation trivia (&lt;a href="https://learn.microsoft.com/en-us/entra/identity/conditional-access/agent-id#attribute-driven-conditional-access" rel="noopener noreferrer"&gt;attribute-driven Conditional Access for agents&lt;/a&gt;, &lt;a href="https://learn.microsoft.com/en-us/entra/identity/conditional-access/agent-id/policy-autonomous-agents#allow-only-specific-agents-to-access-resources" rel="noopener noreferrer"&gt;autonomous agent policy walkthrough&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;These are strategic guardrails. The fine-grained boundaries should come from additional controls and deeper policy patterns, which we can cover in follow-up pieces.&lt;/p&gt;

&lt;h2&gt;
  
  
  Governance is part of schema, not a separate phase
&lt;/h2&gt;

&lt;p&gt;Custom security attributes are security-sensitive by design, and Entra uses dedicated attribute roles for definition and assignment. Even Global Administrator is not automatically enough for these operations by default (&lt;a href="https://learn.microsoft.com/entra/fundamentals/custom-security-attributes-overview#custom-security-attribute-roles" rel="noopener noreferrer"&gt;custom security attribute roles&lt;/a&gt;, &lt;a href="https://learn.microsoft.com/entra/identity/role-based-access-control/permissions-reference#attribute-assignment-administrator" rel="noopener noreferrer"&gt;permissions reference&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;Treat that as a feature, not friction.&lt;/p&gt;

&lt;p&gt;Your schema decision should include explicit ownership for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;who defines agent attribute sets and values,&lt;/li&gt;
&lt;li&gt;who is allowed to assign and update values on agent identities,&lt;/li&gt;
&lt;li&gt;how schema changes are reviewed when agent programs evolve.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If those answers are not defined with the schema, your taxonomy will drift faster than your policies can keep up.&lt;/p&gt;

&lt;h2&gt;
  
  
  The practical position
&lt;/h2&gt;

&lt;p&gt;Could you reuse your current workload identity schema for AI agents? Yes.&lt;/p&gt;

&lt;p&gt;Should you, by default, in a large regulated enterprise? Usually no.&lt;/p&gt;

&lt;p&gt;The better default is a dedicated agent schema with a small number of coarse, policy-driving attributes. Make the boundaries obvious. Keep the vocabulary stable. Optimize for long-term policy clarity, not short-term convenience.&lt;/p&gt;

&lt;p&gt;This is architecture work in the most literal sense: defining the shape of control before control logic gets complicated.&lt;/p&gt;

&lt;p&gt;We already did this in this series when discussing dynamic consent accumulation and agent governance pressure in &lt;a href="https://dev.to/astaykov/the-overlooked-gem-in-microsoft-entra-that-gives-your-ai-agents-super-powers-3mde"&gt;The Overlooked Gem in Microsoft Entra&lt;/a&gt; and &lt;a href="https://dev.to/astaykov/your-agent-is-becoming-the-crown-jewel-soc-reviews-and-governance-for-the-dynamic-consent-era-23l3"&gt;Your Agent Is Becoming the Crown Jewel&lt;/a&gt;. This article just names the next prerequisite: the classification layer that keeps your controls honest.&lt;/p&gt;

&lt;p&gt;If you delay this decision, every downstream guardrail becomes harder to reason about.&lt;/p&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/entra/fundamentals/custom-security-attributes-overview" rel="noopener noreferrer"&gt;What are custom security attributes in Microsoft Entra ID?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/en-us/graph/api/resources/custom-security-attributes-overview?view=graph-rest-1.0" rel="noopener noreferrer"&gt;Overview of custom security attributes using Microsoft Graph API&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/entra/identity/enterprise-apps/custom-security-attributes-apps" rel="noopener noreferrer"&gt;Manage custom security attributes for an application&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/en-us/entra/identity/conditional-access/agent-id#attribute-driven-conditional-access" rel="noopener noreferrer"&gt;Conditional Access for agents (attribute-driven Conditional Access)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/en-us/entra/identity/conditional-access/agent-id/policy-autonomous-agents#allow-only-specific-agents-to-access-resources" rel="noopener noreferrer"&gt;Conditional Access for autonomous agents (allow only specific agents to access resources)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/entra/agent-id/key-concepts" rel="noopener noreferrer"&gt;Microsoft Entra Agent ID key concepts&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/entra/agent-id/agent-service-principals" rel="noopener noreferrer"&gt;Agent identities, service principals, and applications&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/entra/identity/role-based-access-control/permissions-reference#attribute-assignment-administrator" rel="noopener noreferrer"&gt;Role permissions reference: Attribute Assignment Administrator&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>entraagentid</category>
      <category>customsecurityattributes</category>
      <category>aiagents</category>
      <category>zerotrust</category>
    </item>
    <item>
      <title>How enterprise architects should decide Entra Agent ID blueprint boundaries for non-Microsoft platforms</title>
      <dc:creator>Anton Staykov</dc:creator>
      <pubDate>Tue, 21 Jul 2026 10:47:00 +0000</pubDate>
      <link>https://dev.to/astaykov/how-enterprise-architects-should-decide-entra-agent-id-blueprint-boundaries-for-non-microsoft-oaa</link>
      <guid>https://dev.to/astaykov/how-enterprise-architects-should-decide-entra-agent-id-blueprint-boundaries-for-non-microsoft-oaa</guid>
      <description>&lt;p&gt;Enterprise architects keep asking me the same question: &lt;em&gt;how many agent identity blueprints should we create&lt;/em&gt; for third-party or custom agent platforms connected to Microsoft Entra Agent ID?&lt;/p&gt;

&lt;p&gt;That question sounds operational, but it is architectural. You are deciding failure containment, ownership boundaries, and governance behavior, all at once.&lt;/p&gt;

&lt;p&gt;Before we go deeper, one important acknowledgement. If your agents are built in Microsoft-managed builder paths such as Copilot Studio and Agent 365 integration flows, identity provisioning is largely handled for you, including automatic Agent ID creation and shared blueprint behavior in that platform path (&lt;a href="https://learn.microsoft.com/en-us/microsoft-copilot-studio/admin-use-entra-agent-identities" rel="noopener noreferrer"&gt;Copilot Studio automatic Agent IDs&lt;/a&gt;, &lt;a href="https://learn.microsoft.com/en-us/microsoft-agent-365/builder/identity" rel="noopener noreferrer"&gt;Agent 365 identity integration&lt;/a&gt;). If you choose that platform, you also accept its blueprint identity model by design.&lt;/p&gt;

&lt;p&gt;This article is for the other world: non-Microsoft-built agent platforms, where your enterprise architecture team owns the blueprint boundary decisions.&lt;/p&gt;

&lt;p&gt;I am also not going to summarize the canonical guidance. You should read both &lt;a href="https://learn.microsoft.com/en-us/entra/agent-id/how-to-plan-agent-identity-architecture" rel="noopener noreferrer"&gt;Plan your agent identity architecture&lt;/a&gt; and &lt;a href="https://learn.microsoft.com/en-us/entra/agent-id/concept-agent-id-design-patterns" rel="noopener noreferrer"&gt;Microsoft Entra Agent ID design patterns&lt;/a&gt; directly, then come back. This piece is about decision framing, trade-offs, and policy posture.&lt;/p&gt;

&lt;h2&gt;
  
  
  Stop asking for a count, start defining trust boundaries
&lt;/h2&gt;

&lt;p&gt;When architects ask "how many blueprints," they usually mean one of three fears:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;"I do not want one compromised component to become tenant-wide damage."&lt;/li&gt;
&lt;li&gt;"I do not want central identity teams to become a ticket bottleneck."&lt;/li&gt;
&lt;li&gt;"I do not want unrecoverable sprawl six months from now."&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;That is a trust-boundary problem, not an object-count problem.&lt;/p&gt;

&lt;p&gt;Microsoft's own architecture guidance is explicit that blueprint count should follow trust boundaries, while many common concerns (replicas, memory partitioning, session context) are not valid reasons to split identities (&lt;a href="https://learn.microsoft.com/en-us/entra/agent-id/how-to-plan-agent-identity-architecture" rel="noopener noreferrer"&gt;planning guidance&lt;/a&gt;, &lt;a href="https://learn.microsoft.com/en-us/entra/agent-id/concept-agent-id-design-patterns" rel="noopener noreferrer"&gt;design patterns&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;If your team still starts with org chart lines like "one blueprint per project by default," you are optimizing for slide readability, not for incident containment.&lt;/p&gt;

&lt;h2&gt;
  
  
  The blueprint power model, and the part teams keep confusing
&lt;/h2&gt;

&lt;p&gt;You need one clear mental model.&lt;/p&gt;

&lt;p&gt;The blueprint is your identity factory and authentication anchor. It is what enables creation of child agent identities at scale, including runtime or ephemeral patterns when your architecture needs on-demand identities (&lt;a href="https://learn.microsoft.com/en-us/entra/agent-id/agent-id-creation-channels" rel="noopener noreferrer"&gt;creation channels&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;That is good news for delivery teams. Developers do not need to raise a central ticket for every single new identity if the right blueprint channel and controls are already in place.&lt;/p&gt;

&lt;p&gt;But do not confuse that with automatic governance.&lt;/p&gt;

&lt;p&gt;Blueprints are not where your organization magically enforces business access policy. Consent boundaries, entitlement policy, Conditional Access, and app-governance controls are separate control planes that still require explicit enterprise policy definition and rollout (&lt;a href="https://learn.microsoft.com/en-us/entra/identity/enterprise-apps/user-admin-consent-overview" rel="noopener noreferrer"&gt;user/admin consent model&lt;/a&gt;, &lt;a href="https://learn.microsoft.com/en-us/entra/id-governance/identity-governance-applications-define" rel="noopener noreferrer"&gt;define governance policy&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;Treat the blueprint as a creation authority, not as the permission-grant layer and not as your per-agent lifecycle governance switch.&lt;/p&gt;

&lt;p&gt;In plain terms, Entra Agent ID and Agent 365 capabilities do not enforce your internal architecture doctrine by themselves. Without internal policies, teams will improvise.&lt;/p&gt;

&lt;h2&gt;
  
  
  Blast radius reality: one compromised agent can become many identities
&lt;/h2&gt;

&lt;p&gt;Here is the uncomfortable part.&lt;/p&gt;

&lt;p&gt;Within a shared blueprint trust boundary, if one agent runtime is compromised and the attacker can use the same blueprint credential path, that attacker can request tokens that act as sibling identities under that blueprint boundary. This is exactly why Microsoft frames blueprint compromise as potentially affecting all child identities in that blueprint scope (&lt;a href="https://learn.microsoft.com/en-us/entra/agent-id/how-to-plan-agent-identity-architecture" rel="noopener noreferrer"&gt;planning guidance&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;That is the architectural reason you model by trust boundary first.&lt;/p&gt;

&lt;p&gt;It also explains a frequent false comfort pattern: "we will just split certificates or managed identity details but keep one shared blueprint boundary." If the effective authentication authority is still the same blueprint trust surface, your containment gain is smaller than people assume.&lt;/p&gt;

&lt;h2&gt;
  
  
  A risk-first decision framework for blueprint boundaries
&lt;/h2&gt;

&lt;p&gt;Use this sequence. Do not skip steps.&lt;/p&gt;

&lt;h3&gt;
  
  
  1) Define hard trust boundaries
&lt;/h3&gt;

&lt;p&gt;Map runtime, secret stores, network segmentation, and operational ownership. If compromise should not cross between two zones, they should not share a blueprint.&lt;/p&gt;

&lt;h3&gt;
  
  
  2) Define failure containment targets
&lt;/h3&gt;

&lt;p&gt;State explicitly what should be kill-switch scope:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;one identity,&lt;/li&gt;
&lt;li&gt;one blueprint,&lt;/li&gt;
&lt;li&gt;one environment,&lt;/li&gt;
&lt;li&gt;or tenant-wide.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If this is undefined, your incident response design is fiction.&lt;/p&gt;

&lt;h3&gt;
  
  
  3) Define permission topology
&lt;/h3&gt;

&lt;p&gt;Decide what is inheritable baseline and what is per-agent specialization. Keep inherited baseline tight and role-specific differentiation local to agent identities.&lt;/p&gt;

&lt;h3&gt;
  
  
  4) Define lifecycle behavior
&lt;/h3&gt;

&lt;p&gt;Will identities be long-lived, or created dynamically for specific task windows? If dynamic creation is a first-class requirement, blueprint channel design and guardrails become even more critical.&lt;/p&gt;

&lt;h3&gt;
  
  
  5) Define governance ownership
&lt;/h3&gt;

&lt;p&gt;Who approves what, who can create via which channel, who can override in emergencies, who attests quarterly. If there is no owner matrix, there is no governance.&lt;/p&gt;

&lt;h2&gt;
  
  
  When one blueprint is right, and when it is reckless
&lt;/h2&gt;

&lt;p&gt;One blueprint with multiple agent identities is often correct when teams share the same trust boundary, runtime envelope, and operational ownership, while still needing per-agent attribution and differentiated downstream access (&lt;a href="https://learn.microsoft.com/en-us/entra/agent-id/concept-agent-id-design-patterns" rel="noopener noreferrer"&gt;design patterns&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;Concrete example: if your agents run in the same container orchestration boundary (for example, a shared Kubernetes namespace on AKS, EKS, or GKE), that is usually a strong indicator for one shared blueprint in that boundary. In practice, namespace-level controls define who can deploy, who can mutate workloads, what network policies apply, and what mounted/shared resources are reachable.&lt;/p&gt;

&lt;p&gt;That matters because your real security perimeter is often the namespace and its attached runtime controls, not an individual pod. Most engineering organizations can consistently enforce controls at namespace scope for developers and CI/CD pipelines, while per-pod isolation is harder to guarantee as a durable operating model.&lt;/p&gt;

&lt;p&gt;It becomes reckless when you cross security domains and still keep one blueprint for convenience. Separate teams, separate secret domains, separate runtime isolation, or separate regulatory boundaries should usually trigger separate blueprints.&lt;/p&gt;

&lt;h2&gt;
  
  
  Policy starter kit for enterprise architects
&lt;/h2&gt;

&lt;p&gt;If you want consistency across teams, publish a short internal policy before the next project starts.&lt;/p&gt;

&lt;h3&gt;
  
  
  Blueprint policy baseline
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;One blueprint per trust boundary unless exception approved.&lt;/li&gt;
&lt;li&gt;Mandatory sponsor and owner assignment, with periodic attestation.&lt;/li&gt;
&lt;li&gt;Naming convention that encodes environment, domain, and business owner.&lt;/li&gt;
&lt;li&gt;Conditional Access baseline and monitoring baseline applied at blueprint and identity levels where applicable (&lt;a href="https://learn.microsoft.com/en-us/entra/agent-id/best-practices-agent-id" rel="noopener noreferrer"&gt;best practices&lt;/a&gt;, &lt;a href="https://learn.microsoft.com/en-us/entra/agent-id/manage-agent-identities-admin" rel="noopener noreferrer"&gt;manage and govern agent identities&lt;/a&gt;).&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Creation channel policy
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Define which channels are allowed in your org: admin center, Graph, IaC/CLI, product-integrated channels.&lt;/li&gt;
&lt;li&gt;Bind each channel to approved roles and permission grants.&lt;/li&gt;
&lt;li&gt;Prohibit ad-hoc consent paths for production-grade identity creation unless explicitly reviewed (&lt;a href="https://learn.microsoft.com/en-us/entra/identity/enterprise-apps/configure-user-consent" rel="noopener noreferrer"&gt;configure user consent&lt;/a&gt;).&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Split and merge rubric
&lt;/h3&gt;

&lt;p&gt;Require architecture review on:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;trust-boundary changes,&lt;/li&gt;
&lt;li&gt;new regulated data exposure,&lt;/li&gt;
&lt;li&gt;new external execution platform,&lt;/li&gt;
&lt;li&gt;incident lessons that show containment gaps.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If teams cannot explain why two workloads can safely share compromise blast radius, they should not share a blueprint.&lt;/p&gt;

&lt;h2&gt;
  
  
  The architect checklist to force before production
&lt;/h2&gt;

&lt;p&gt;Before approving blueprint design, require crisp answers to these questions:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;If one agent runtime is compromised, what exact identity scope can the attacker influence?&lt;/li&gt;
&lt;li&gt;Which trust boundaries are represented, and why are they safe to share?&lt;/li&gt;
&lt;li&gt;Which channels can create identities, and who controls them?&lt;/li&gt;
&lt;li&gt;Which policies prevent consent and permission sprawl?&lt;/li&gt;
&lt;li&gt;What is the fastest kill-switch path during incident response?&lt;/li&gt;
&lt;li&gt;What quarterly evidence proves this model is still valid?&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If your team cannot answer those six questions in one review, your blueprint model is not ready for production.&lt;/p&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/en-us/entra/agent-id/concept-agent-id-design-patterns" rel="noopener noreferrer"&gt;Microsoft Entra Agent ID design patterns&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/en-us/entra/agent-id/how-to-plan-agent-identity-architecture" rel="noopener noreferrer"&gt;Plan your agent identity architecture&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/en-us/microsoft-copilot-studio/admin-use-entra-agent-identities" rel="noopener noreferrer"&gt;Automatically create Microsoft Entra Agent IDs for Copilot Studio agents&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/en-us/microsoft-agent-365/builder/identity" rel="noopener noreferrer"&gt;Agent identity integration for Copilot Studio&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/en-us/entra/agent-id/agent-id-creation-channels" rel="noopener noreferrer"&gt;How are agent identities created?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/en-us/entra/agent-id/best-practices-agent-id" rel="noopener noreferrer"&gt;Best practices for Microsoft Entra Agent ID&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/en-us/entra/identity/enterprise-apps/user-admin-consent-overview" rel="noopener noreferrer"&gt;Overview of user and admin consent&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/en-us/entra/identity/enterprise-apps/configure-user-consent" rel="noopener noreferrer"&gt;Configure how users consent to applications&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/en-us/entra/id-governance/identity-governance-applications-define" rel="noopener noreferrer"&gt;Define organizational policies for governing access to applications in your environment&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/en-us/entra/id-governance/identity-governance-applications-deploy" rel="noopener noreferrer"&gt;Deploying organizational policies for governing access to applications integrated with Microsoft Entra ID&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>entraagentid</category>
      <category>identitygovernance</category>
      <category>aiagents</category>
      <category>zerotrust</category>
    </item>
    <item>
      <title>The industry does not have an agent identity propagation standard yet</title>
      <dc:creator>Anton Staykov</dc:creator>
      <pubDate>Tue, 14 Jul 2026 11:07:00 +0000</pubDate>
      <link>https://dev.to/astaykov/the-industry-does-not-have-an-agent-identity-propagation-standard-yet-3fmf</link>
      <guid>https://dev.to/astaykov/the-industry-does-not-have-an-agent-identity-propagation-standard-yet-3fmf</guid>
      <description>&lt;p&gt;Most architecture conversations about AI agents still get stuck on the wrong boundary.&lt;/p&gt;

&lt;p&gt;Someone draws an agent in Azure, another agent in AWS, a resource behind Ping Identity or Auth0, a Microsoft Entra-protected API, an MCP server in the middle, and a user somewhere at the beginning of the chain. Then the room asks: can Microsoft Entra Agent ID work if the agent is not hosted on Microsoft infrastructure?&lt;/p&gt;

&lt;p&gt;That is the easy question. Hosting is not the boundary.&lt;/p&gt;

&lt;p&gt;Identity provider domain is the boundary.&lt;/p&gt;

&lt;p&gt;An application can live in AWS, Google Cloud, a co-location rack, or a basement server room and still use Microsoft Entra ID for sign-in. The same principle applies to agents. Microsoft explicitly positions Entra Agent ID as working with Microsoft and non-Microsoft platforms, including integrations through the Microsoft Entra Auth SDK sidecar and workload identity federation (&lt;a href="https://learn.microsoft.com/entra/agent-id/what-is-microsoft-entra-agent-id" rel="noopener noreferrer"&gt;Microsoft Entra Agent ID&lt;/a&gt;, &lt;a href="https://learn.microsoft.com/entra/agent-id/configure-third-party-agents" rel="noopener noreferrer"&gt;third-party agent integration patterns&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;The hard part starts when the flow leaves one identity provider and must preserve the original user context in another identity provider's world.&lt;/p&gt;

&lt;p&gt;That is where the industry is still awkward. Not because there are no standards. There are standards. The problem is that the standards describe token exchange mechanics better than they describe enterprise trust governance for long, agentic, multi-hop chains.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fa8lh33gibedd8dmb6uou.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fa8lh33gibedd8dmb6uou.png" alt="A user starts in one identity provider domain, passes through agents, MCP servers, and resources, then crosses into another identity provider domain before returning to downstream APIs. The hard part is not hosting. The hard part is preserving trustworthy user context across identity provider boundaries." width="800" height="377"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The diagram above is the mental model. A user signs in, an application invokes an agent, the agent calls a tool or MCP server, another agent appears, and eventually a REST API or database needs to decide whether to trust the original user's context. Some arrows stay inside Microsoft Entra. Some arrows cross into Ping Identity or another provider. Some resources are custom APIs. Some are Microsoft resources. Some are agentic systems calling other agentic systems.&lt;/p&gt;

&lt;p&gt;The diagram looks like a flow. In practice, it is a chain of trust decisions.&lt;/p&gt;

&lt;h2&gt;
  
  
  Federation is not token exchange
&lt;/h2&gt;

&lt;p&gt;One of the most useful audience questions in the discussion was simple: "We already solve multiple identity providers with federation. Why is this different for agents?"&lt;/p&gt;

&lt;p&gt;That question matters because it separates two things teams often blend together.&lt;/p&gt;

&lt;p&gt;Federation solves an interactive authentication problem. A user starts at one identity provider, is redirected to another identity provider, proves who they are, and returns with a token or assertion the application can use. That pattern is well understood in SAML, OpenID Connect, and enterprise SSO designs (&lt;a href="https://openid.net/specs/openid-connect-core-1_0.html" rel="noopener noreferrer"&gt;OpenID Connect Core&lt;/a&gt;, &lt;a href="https://learn.microsoft.com/entra/identity-platform/v2-protocols-oidc" rel="noopener noreferrer"&gt;Microsoft identity platform and OpenID Connect&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;Token exchange solves a different problem. A component that already received one token asks an authorization server for another token intended for a different resource or context. The standard that most directly describes this is &lt;a href="https://www.rfc-editor.org/rfc/rfc8693.html" rel="noopener noreferrer"&gt;OAuth 2.0 Token Exchange, RFC 8693&lt;/a&gt;. RFC 8693 defines a Security Token Service style protocol for requesting and obtaining tokens from OAuth authorization servers, including delegation and impersonation semantics.&lt;/p&gt;

&lt;p&gt;Those are not the same motion.&lt;/p&gt;

&lt;p&gt;Federation is the front door. Token exchange is what happens in the hallway after the front door has already closed.&lt;/p&gt;

&lt;p&gt;That distinction becomes brutally important for agents because agents create more hallways. A user does not simply sign in and call one API. The user asks an agent, the agent calls a tool, the tool calls an MCP server, the MCP server calls another agent, and that agent calls a REST API. Each hop may need a different token audience, a different scope, a different policy evaluation, and a different interpretation of who is acting.&lt;/p&gt;

&lt;h2&gt;
  
  
  RFC 8693 gives us language, not the whole operating model
&lt;/h2&gt;

&lt;p&gt;RFC 8693 is important because it gives the industry precise words for what keeps showing up in agent conversations: &lt;code&gt;subject_token&lt;/code&gt;, &lt;code&gt;actor_token&lt;/code&gt;, &lt;code&gt;audience&lt;/code&gt;, &lt;code&gt;resource&lt;/code&gt;, &lt;code&gt;scope&lt;/code&gt;, &lt;code&gt;act&lt;/code&gt;, and &lt;code&gt;may_act&lt;/code&gt; (&lt;a href="https://www.rfc-editor.org/rfc/rfc8693.html" rel="noopener noreferrer"&gt;RFC 8693&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;It also draws a clean line between impersonation and delegation. In impersonation, principal A becomes indistinguishable from principal B within the token's rights context. In delegation, principal A remains visible as the actor representing principal B (&lt;a href="https://www.rfc-editor.org/rfc/rfc8693.html#section-1.1" rel="noopener noreferrer"&gt;RFC 8693, delegation vs. impersonation&lt;/a&gt;). For agents, that distinction is not academic. An enterprise should almost never want an agent to become invisible as the human. It wants the user, the agent, and the delegation relationship to remain visible.&lt;/p&gt;

&lt;p&gt;RFC 8693 also defines the &lt;code&gt;act&lt;/code&gt; claim as a way to represent the current actor and even nested actor chains in a JWT (&lt;a href="https://www.rfc-editor.org/rfc/rfc8693.html#section-4.1" rel="noopener noreferrer"&gt;RFC 8693, actor claim&lt;/a&gt;). That sounds like exactly what we need.&lt;/p&gt;

&lt;p&gt;But the standard is careful about its own boundary. It defines a protocol. It does not solve every deployment trust model. It explicitly leaves token semantics, policy decisions, and many security characteristics to implementations and deployment configuration (&lt;a href="https://www.rfc-editor.org/rfc/rfc8693.html#section-1" rel="noopener noreferrer"&gt;RFC 8693 introduction&lt;/a&gt;, &lt;a href="https://www.rfc-editor.org/rfc/rfc8693.html#section-5" rel="noopener noreferrer"&gt;RFC 8693 security considerations&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;That is the gap.&lt;/p&gt;

&lt;p&gt;The standard can describe a token exchange. It cannot, by itself, answer the enterprise question: who configured the mapping that allows an actor from identity provider A to arrive at identity provider B and claim a privileged user context?&lt;/p&gt;

&lt;p&gt;That question is not protocol syntax. That question is governance.&lt;/p&gt;

&lt;h2&gt;
  
  
  Autonomous agents are the solved part
&lt;/h2&gt;

&lt;p&gt;If there is no user context to preserve, the picture is much cleaner.&lt;/p&gt;

&lt;p&gt;An autonomous agent can authenticate as itself using client credentials, a certificate, a federated identity credential, or a managed identity, depending on the platform and operating model. Microsoft Entra Agent ID supports autonomous access and delegated access as different modes for agent identities (&lt;a href="https://learn.microsoft.com/entra/agent-id/what-are-agent-identities#what-agent-identities-enable" rel="noopener noreferrer"&gt;what agent identities enable&lt;/a&gt;). The Microsoft Entra Auth SDK sidecar is not the authentication method for the agent itself. It is a token-handling pattern that keeps credential exchange, OBO flows, token caching, and credential-source abstraction outside the agent code (&lt;a href="https://learn.microsoft.com/entra/agent-id/authentication-with-auth-sdk-sidecar" rel="noopener noreferrer"&gt;Microsoft Entra Auth SDK sidecar&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;This is also where workload identity federation fits well. Microsoft documents workload identity federation as a way for applications to access Microsoft Entra-protected resources without managing secrets, by trusting tokens from external identity providers (&lt;a href="https://learn.microsoft.com/entra/workload-id/workload-identity-federation" rel="noopener noreferrer"&gt;workload identity federation&lt;/a&gt;). Entra Agent ID's third-party integration guidance describes agents on external platforms exchanging native workload identity tokens for Microsoft Entra tokens before calling APIs (&lt;a href="https://learn.microsoft.com/entra/agent-id/configure-third-party-agents" rel="noopener noreferrer"&gt;integrate third-party agents&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;This pattern is not limited to Microsoft. AWS, Google Cloud, Kubernetes ecosystems, and SPIFFE-based systems all lean into the same broad direction: give workloads short-lived, attestable identities instead of static secrets. SPIFFE describes itself as a set of open-source standards for identifying software systems in dynamic and heterogeneous environments, with short-lived identity documents called SVIDs delivered through a workload API (&lt;a href="https://spiffe.io/docs/latest/spiffe-about/overview/" rel="noopener noreferrer"&gt;SPIFFE overview&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;For fully autonomous agents, the industry has enough pieces to build sane systems today.&lt;/p&gt;

&lt;p&gt;The agent proves what workload it is. The target resource validates the token. The authorization system checks the agent's assigned rights. No human context needs to survive the journey.&lt;/p&gt;

&lt;p&gt;That is not trivial, but it is tractable.&lt;/p&gt;

&lt;h2&gt;
  
  
  User context is where the floor drops
&lt;/h2&gt;

&lt;p&gt;The difficult case is the one the audience kept circling back to: an employee signs in, an agent acts on behalf of that employee, the flow crosses identity providers, and a downstream resource must still know which human user started the journey.&lt;/p&gt;

&lt;p&gt;Microsoft Entra Agent ID supports on-behalf-of flows for agents. The documented OBO pattern uses a user token sent to the agent identity blueprint, then performs token exchanges so the resulting resource token carries the signed-in user's delegated context together with the agent identity (&lt;a href="https://learn.microsoft.com/entra/agent-id/agent-on-behalf-of-oauth-flow" rel="noopener noreferrer"&gt;Agent OAuth flows: on behalf of flow&lt;/a&gt;). Microsoft recommends using approved SDKs such as Microsoft.Identity.Web or the Microsoft Entra Auth SDK sidecar because manual implementation is complex and error-prone (&lt;a href="https://learn.microsoft.com/entra/agent-id/agent-on-behalf-of-oauth-flow" rel="noopener noreferrer"&gt;Agent OAuth flows: on behalf of flow&lt;/a&gt;, &lt;a href="https://learn.microsoft.com/entra/agent-id/authentication-with-auth-sdk-sidecar#sidecar-sample-scenarios" rel="noopener noreferrer"&gt;sidecar sample scenarios&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;Inside one coherent identity domain, that is already a serious amount of machinery. Across identity providers, it becomes a trust governance problem.&lt;/p&gt;

&lt;p&gt;The downstream system is no longer asking only, "Is this token valid?" It is asking a longer set of questions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Who was the original user?&lt;/li&gt;
&lt;li&gt;Which agent is acting now?&lt;/li&gt;
&lt;li&gt;Which prior agents or services participated in the chain?&lt;/li&gt;
&lt;li&gt;Which identity provider translated the context?&lt;/li&gt;
&lt;li&gt;Who configured that translation?&lt;/li&gt;
&lt;li&gt;Are we allowed to trust that mapping?&lt;/li&gt;
&lt;li&gt;How do we monitor and revoke that trust when risk changes?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is where the diagram matters. Six hops are not theoretical in an agentic architecture. They are what happens when a human asks one assistant to do work that uses an MCP server, which delegates to another specialized agent, which calls a vendor API, which then calls an internal API.&lt;/p&gt;

&lt;p&gt;The standards do not stop you from creating such a chain. OAuth OBO style flows do not impose a natural business limit on how many middle tiers can appear. RFC 8693 can represent actor chains through nested &lt;code&gt;act&lt;/code&gt; claims. In theory, the chain can keep growing (&lt;a href="https://www.rfc-editor.org/rfc/rfc8693.html#section-4.1" rel="noopener noreferrer"&gt;RFC 8693 actor claim&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;Then the old boring transport layer walks into the room.&lt;/p&gt;

&lt;p&gt;Bearer tokens are commonly presented to resource servers in the HTTP &lt;code&gt;Authorization&lt;/code&gt; header field using &lt;code&gt;Authorization: Bearer ...&lt;/code&gt; (&lt;a href="https://www.rfc-editor.org/rfc/rfc6750.html#section-2.1" rel="noopener noreferrer"&gt;RFC 6750, authorization request header field&lt;/a&gt;). HTTP deliberately does not define one universal maximum length for a field line, field value, or header section. Implementations set their own limits, and a server that receives request fields larger than it is willing to process is supposed to reject the request with an appropriate 4xx response, not ignore the fields (&lt;a href="https://www.rfc-editor.org/rfc/rfc9110.html#name-field-limits" rel="noopener noreferrer"&gt;RFC 9110, field limits&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;That means a perfectly valid access token can still be operationally too large for one proxy, gateway, service mesh sidecar, WAF, CDN edge, or application server in the path.&lt;/p&gt;

&lt;p&gt;This is not only an availability problem. It is a security design smell. If every delegated hop adds nested actors, subjects, issuers, audiences, or other chain evidence, the token can grow until one component in the HTTP path handles it differently than the component that issued it or the API that validates it. Best case, the call fails cleanly. Worse case, a proxy truncates, drops, rewrites, retries, normalizes, caches, or falls back in a way the authorization model never expected. HTTP itself warns that ignoring oversized header fields increases exposure to request smuggling, and its security considerations explicitly call out attacks via protocol element length (&lt;a href="https://www.rfc-editor.org/rfc/rfc9110.html#name-field-limits" rel="noopener noreferrer"&gt;RFC 9110, field limits&lt;/a&gt;, &lt;a href="https://www.rfc-editor.org/rfc/rfc9110.html#name-attacks-via-protocol-elemen" rel="noopener noreferrer"&gt;RFC 9110, attacks via protocol element length&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;This is how a governance problem becomes an incident report. The identity system issued a token that was cryptographically valid. The architecture allowed a chain long enough to make that token huge. The application team assumed middleware would fail closed. The proxy team assumed the upstream API owned authorization. Then an agent reaches data it should not see because one hop in the path handled an oversized credential differently than another.&lt;/p&gt;

&lt;p&gt;Who owns that failure?&lt;/p&gt;

&lt;p&gt;The vendor that supported unbounded chaining? The architect who allowed policies with no practical chain-depth ceiling? The developer who never measured token size or actor-chain depth? The HTTP proxy that mishandled an oversized &lt;code&gt;Authorization&lt;/code&gt; header? The operations team that changed a WAF limit two months ago and did not know it was now part of the identity control plane?&lt;/p&gt;

&lt;p&gt;This is why "it worked two months ago" is such a dangerous sentence in agent identity. Nothing in the agent code needs to change for the chain to become too long. More tools get added. More agents call each other. More claims get preserved for audit. More tenants and issuers appear. Suddenly the same logical flow produces a larger token, crosses a different intermediary, and starts failing. Or worse, failing open.&lt;/p&gt;

&lt;p&gt;But a representable chain is not automatically a governable chain.&lt;/p&gt;

&lt;p&gt;That is the industry's current problem in one sentence: we can describe the chain better than we can govern the chain.&lt;/p&gt;

&lt;h2&gt;
  
  
  Evidence helps, but it does not create trust
&lt;/h2&gt;

&lt;p&gt;Once the conversation moves from "can I get a token?" to "can the downstream system understand the context?" teams naturally reach for more data in the token.&lt;/p&gt;

&lt;p&gt;That instinct is reasonable, but it is also incomplete. A downstream resource may need to know whether the caller is an agent, which agent is acting, which human user is present, which tenant owns the agent, whether the human is external to the resource tenant, or whether the agent is multi-tenant. That context can make downstream decisions smarter.&lt;/p&gt;

&lt;p&gt;But more evidence in a token is not the same as trust.&lt;/p&gt;

&lt;p&gt;Adding agent context, actor context, tenant context, or multi-tenant application context can make a downstream authorization decision more informed. It does not answer who approved the mapping, whether the source domain is trusted, whether the chain is still valid, or how the receiving domain should react when a risk signal changes.&lt;/p&gt;

&lt;p&gt;That is the mistake to avoid. Token evidence can describe what arrived. Governance decides whether what arrived should be trusted.&lt;/p&gt;

&lt;p&gt;The difference matters. Carrying context is easier than proving that the context should be trusted across organizational, identity provider, and agent boundaries.&lt;/p&gt;

&lt;h2&gt;
  
  
  The emerging standards conversation is really about trust evidence
&lt;/h2&gt;

&lt;p&gt;When people ask for "the standard," they often mean one protocol that makes the whole diagram safe. That is not how this will land.&lt;/p&gt;

&lt;p&gt;The industry is moving along several tracks at once.&lt;/p&gt;

&lt;p&gt;OAuth token exchange gives us the exchange grammar (&lt;a href="https://www.rfc-editor.org/rfc/rfc8693.html" rel="noopener noreferrer"&gt;RFC 8693&lt;/a&gt;). Workload identity federation gives us a workable model for autonomous workload trust without secrets (&lt;a href="https://learn.microsoft.com/entra/workload-id/workload-identity-federation" rel="noopener noreferrer"&gt;workload identity federation&lt;/a&gt;). SPIFFE gives us an identity framework for workloads in heterogeneous environments, including JWT and X.509 identity documents (&lt;a href="https://spiffe.io/docs/latest/spiffe-about/overview/" rel="noopener noreferrer"&gt;SPIFFE overview&lt;/a&gt;). Shared Signals and CAEP give the ecosystem a way to communicate security events and state changes continuously, rather than treating access decisions as one-time snapshots (&lt;a href="https://openid.net/wg/sharedsignals/" rel="noopener noreferrer"&gt;OpenID Shared Signals Working Group&lt;/a&gt;). Agent protocols such as MCP and A2A are pushing tool and agent communication into more formal shapes, and Microsoft explicitly lists OAuth 2.0, MCP, and A2A among the protocols supported by the Entra Agent identity platform (&lt;a href="https://learn.microsoft.com/entra/agent-id/what-is-microsoft-entra-agent-id" rel="noopener noreferrer"&gt;Microsoft Entra Agent ID&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;The center of gravity is shifting from "can I exchange a token?" to "what evidence travels with the token, and who is accountable for trusting it?"&lt;/p&gt;

&lt;p&gt;That is the right shift.&lt;/p&gt;

&lt;p&gt;The next generation of standards work has to answer questions the current protocol layer only exposes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;How does a receiving domain know the original user is still the same user after multiple agent and tool hops?&lt;/li&gt;
&lt;li&gt;How does the chain preserve both the human subject and the current agent actor without collapsing into impersonation?&lt;/li&gt;
&lt;li&gt;How are identity provider mappings approved, reviewed, expired, and revoked?&lt;/li&gt;
&lt;li&gt;How does a downstream resource distinguish a first-party enterprise agent from a multi-tenant external agent acting for an enterprise user?&lt;/li&gt;
&lt;li&gt;How do risk signals travel fast enough that a compromised account or agent does not keep operating on stale trust?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;None of those questions are solved by adding one more token parameter.&lt;/p&gt;

&lt;p&gt;They require standards that combine identity evidence, delegation semantics, risk signaling, authorization policy, and observability.&lt;/p&gt;

&lt;h2&gt;
  
  
  What architects should do now
&lt;/h2&gt;

&lt;p&gt;The correct response is not to wait for the perfect standard. It is to design with the current product and trust boundary in mind.&lt;/p&gt;

&lt;p&gt;First, separate platform location from identity provider domain. An agent running in AWS can still use Microsoft Entra Agent ID. An agent running in Azure can still call a Ping-protected resource. The host is an implementation detail. The issuer, audience, subject, actor, and trust mapping are the security model (&lt;a href="https://learn.microsoft.com/entra/agent-id/configure-third-party-agents" rel="noopener noreferrer"&gt;third-party agent integration patterns&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;Second, split autonomous agent flows from user-delegated flows before you draw the final architecture. Autonomous flows can be built today with client credentials, certificates, federated identity credentials, managed identities, workload identity federation, and standard token validation. A sidecar can help keep token acquisition and credential handling out of the agent code, but it is not itself the agent authentication method.&lt;/p&gt;

&lt;p&gt;The delegated case is different. With Entra Agent ID today, do not design for an OBO flow that carries Microsoft Entra user context across an identity provider boundary and expects the other domain to preserve that context as part of the same delegation chain. Entra Agent ID supports OBO patterns for agents, but cross-IDP user-context propagation is not the supported operating model (&lt;a href="https://learn.microsoft.com/entra/agent-id/agent-on-behalf-of-oauth-flow" rel="noopener noreferrer"&gt;Agent OAuth OBO flow&lt;/a&gt;). If the downstream system is governed by another identity provider, treat that as a separate trust domain. Either keep the delegated path inside one identity provider boundary, use autonomous workload-to-workload trust where user context is not required, or design an explicit application-level handoff with its own audit and policy model.&lt;/p&gt;

&lt;p&gt;Third, avoid cross-domain impersonation unless the business process genuinely requires it and the audit model can survive scrutiny. Delegation should preserve the actor. Impersonation hides the actor from the resource's practical decision point. For agents, hiding the actor is usually the wrong instinct (&lt;a href="https://www.rfc-editor.org/rfc/rfc8693.html#section-1.1" rel="noopener noreferrer"&gt;RFC 8693 delegation vs. impersonation&lt;/a&gt;).&lt;/p&gt;

&lt;h2&gt;
  
  
  The real standard we are missing
&lt;/h2&gt;

&lt;p&gt;The industry does not lack token standards. It lacks a complete, interoperable &lt;em&gt;agent identity propagation governance model&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;That model must preserve the original user where appropriate, preserve the current agent actor always, make cross-IDP mappings explicit, carry enough evidence for downstream decisions, propagate risk and revocation signals, and give enterprises a way to audit who allowed which transition.&lt;/p&gt;

&lt;p&gt;OAuth token exchange is part of that answer. Workload identity federation is part of that answer. SPIFFE is part of that answer. Shared Signals and CAEP are part of that answer. Agent-specific identity platforms such as Microsoft Entra Agent ID are part of that answer.&lt;/p&gt;

&lt;p&gt;But no single one of them is the whole answer yet.&lt;/p&gt;

&lt;p&gt;That is the uncomfortable but useful conclusion from the diagram. We can build useful agent identity systems today. We can secure autonomous agents today. We can do delegated agent flows inside well-understood trust boundaries today. We can even cross platforms without much drama when the flow is autonomous or the trust boundary is explicit.&lt;/p&gt;

&lt;p&gt;The thing we cannot hand-wave is multi-hop, user-bound, cross-IDP identity propagation.&lt;/p&gt;

&lt;p&gt;That is the work ahead. Not just for Microsoft. For the industry.&lt;/p&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/entra/agent-id/what-is-microsoft-entra-agent-id" rel="noopener noreferrer"&gt;Microsoft Entra Agent ID&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/entra/agent-id/what-are-agent-identities" rel="noopener noreferrer"&gt;What are agent identities&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/entra/agent-id/configure-third-party-agents" rel="noopener noreferrer"&gt;Integrate third-party agents with Microsoft Entra Agent ID&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/entra/agent-id/authentication-with-auth-sdk-sidecar" rel="noopener noreferrer"&gt;Authentication with Microsoft Entra Auth SDK sidecar&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/entra/agent-id/agent-on-behalf-of-oauth-flow" rel="noopener noreferrer"&gt;Agent OAuth flows: on behalf of flow&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/entra/workload-id/workload-identity-federation" rel="noopener noreferrer"&gt;Microsoft Entra workload identity federation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/entra/identity-platform/v2-protocols-oidc" rel="noopener noreferrer"&gt;Microsoft identity platform and OpenID Connect&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.rfc-editor.org/rfc/rfc8693.html" rel="noopener noreferrer"&gt;OAuth 2.0 Token Exchange, RFC 8693&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.rfc-editor.org/rfc/rfc6750.html" rel="noopener noreferrer"&gt;OAuth 2.0 Bearer Token Usage, RFC 6750&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.rfc-editor.org/rfc/rfc9110.html" rel="noopener noreferrer"&gt;HTTP Semantics, RFC 9110&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://openid.net/specs/openid-connect-core-1_0.html" rel="noopener noreferrer"&gt;OpenID Connect Core 1.0&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://spiffe.io/docs/latest/spiffe-about/overview/" rel="noopener noreferrer"&gt;SPIFFE overview&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://openid.net/wg/sharedsignals/" rel="noopener noreferrer"&gt;OpenID Shared Signals Working Group&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>entraagentid</category>
      <category>identitystandards</category>
      <category>oauth</category>
      <category>aiagents</category>
    </item>
    <item>
      <title>Access packages are the control plane for Entra Agent ID permissions</title>
      <dc:creator>Anton Staykov</dc:creator>
      <pubDate>Thu, 09 Jul 2026 10:43:00 +0000</pubDate>
      <link>https://dev.to/astaykov/access-packages-are-the-control-plane-for-entra-agent-id-permissions-4kph</link>
      <guid>https://dev.to/astaykov/access-packages-are-the-control-plane-for-entra-agent-id-permissions-4kph</guid>
      <description>&lt;p&gt;It has been a couple of months since &lt;a href="https://learn.microsoft.com/en-us/entra/agent-id/key-concepts" rel="noopener noreferrer"&gt;Microsoft Entra Agent ID became generally available&lt;/a&gt;, but the same message keeps landing in my inbox: "How do we grant permissions to an agent identity? There is no API Permissions page in Entra."&lt;/p&gt;

&lt;p&gt;That moment is always interesting. The team thinks they found a product gap. What they actually found is a mental-model gap.&lt;/p&gt;

&lt;p&gt;Most engineering organizations still design identity the old way: define a permission set early, grant it once, and treat that grant as stable infrastructure. That was already brittle for conventional apps. For agents, it is worse. An agent is useful &lt;em&gt;because&lt;/em&gt; it encounters new contexts, new tools, and new data surfaces after go-live.&lt;/p&gt;

&lt;p&gt;In &lt;a href="https://dev.to/astaykov/a-transformational-ai-agent-does-not-start-with-the-permissions-it-needs-4g8l"&gt;A transformational AI agent does not start with the permissions it needs&lt;/a&gt;, I argued that serious agents should start narrow and &lt;em&gt;earn&lt;/em&gt; scope over time. This article is the governance architecture behind that claim.&lt;/p&gt;

&lt;h2&gt;
  
  
  The portal is not missing a button, your architecture is missing a layer
&lt;/h2&gt;

&lt;p&gt;Entra Agent ID does not erase consent. It does not replace consent. It does not invent a magical fourth mode of consent.&lt;/p&gt;

&lt;p&gt;Consent fundamentals remain exactly what Microsoft documents for enterprise applications: &lt;a href="https://learn.microsoft.com/en-us/entra/identity/enterprise-apps/user-admin-consent-overview" rel="noopener noreferrer"&gt;user consent and admin consent are still the core model&lt;/a&gt;. If delegated access is allowed by policy, a user can authorize it. If risk or scope requires stronger control, admin consent is required.&lt;/p&gt;

&lt;p&gt;That is the first important correction: the "no API permissions blade" observation should not lead to panic. It should lead to design. Agent identities are still bound to Entra authorization rules, including the &lt;a href="https://learn.microsoft.com/en-us/entra/agent-id/authorization-agent-id" rel="noopener noreferrer"&gt;Agent ID authorization model for delegated and application permissions&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The missing thing is not a tab. The missing thing is an &lt;em&gt;operating path&lt;/em&gt; for controlled permission growth.&lt;/p&gt;

&lt;h2&gt;
  
  
  Consent semantics are unchanged, enterprise consent posture is still strict
&lt;/h2&gt;

&lt;p&gt;Some architects respond with: "Fine, let users consent as the agent discovers new needs."&lt;/p&gt;

&lt;p&gt;Technically, that is an option in specific policy configurations. Operationally, most enterprises do not live there.&lt;/p&gt;

&lt;p&gt;Real-world tenants often clamp user consent tightly with explicit &lt;a href="https://learn.microsoft.com/en-us/entra/identity/enterprise-apps/configure-user-consent?pivots=portal" rel="noopener noreferrer"&gt;tenant-level consent settings&lt;/a&gt;, because delegated access can expose sensitive workloads very quickly when control boundaries are weak. Security teams are not being stubborn here. They are pattern matching from years of experience.&lt;/p&gt;

&lt;p&gt;So we end up with a familiar tension:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;AI platform teams need agents that adapt at runtime.&lt;/li&gt;
&lt;li&gt;Governance teams need approvals, accountability, and least-standing-privilege.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you force one side to "win," you get predictable failure modes. Either the agent is over-permissioned up front, or it is blocked by manual ticketing loops every time reality changes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Access packages for agent identities are the bridge
&lt;/h2&gt;

&lt;p&gt;This is where &lt;a href="https://learn.microsoft.com/en-us/entra/agent-id/agent-access-packages" rel="noopener noreferrer"&gt;access packages for agent identities&lt;/a&gt; become much more than a governance checkbox.&lt;/p&gt;

&lt;p&gt;Access packages give you a permission supply chain. The agent does not invent access. It requests from a pre-governed catalog. Policy decides if, when, and how access is granted.&lt;/p&gt;

&lt;p&gt;That sounds abstract until you map it to day-to-day architecture decisions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Identity engineers define packages that correspond to meaningful data capabilities.&lt;/li&gt;
&lt;li&gt;Security and compliance teams define who can approve under which conditions.&lt;/li&gt;
&lt;li&gt;Agent runtime requests package assignment only when task context justifies it.&lt;/li&gt;
&lt;li&gt;Lifecycle policy removes or expires access when the need window closes.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is exactly the governance direction reflected in &lt;a href="https://learn.microsoft.com/en-us/entra/id-governance/agent-id-governance-overview" rel="noopener noreferrer"&gt;Entra guidance for governing agent identities&lt;/a&gt;: agent access is not just about granting once, it is about ongoing controlled assignment.&lt;/p&gt;

&lt;h2&gt;
  
  
  The underrated power move: put consent inside workflow
&lt;/h2&gt;

&lt;p&gt;Most teams still treat admin consent like a one-off privileged ceremony. Someone with rights clicks approve, everyone moves on, and the architecture diagram quietly lies about "least privilege."&lt;/p&gt;

&lt;p&gt;Access packages let you treat that approval as a workflow stage instead of an exception path.&lt;/p&gt;

&lt;p&gt;With entitlement management policies, you can require &lt;a href="https://learn.microsoft.com/en-us/entra/id-governance/entitlement-management-access-package-approval-policy" rel="noopener noreferrer"&gt;multi-stage approvals&lt;/a&gt;, use &lt;a href="https://learn.microsoft.com/en-us/entra/id-governance/entitlement-management-dynamic-approval" rel="noopener noreferrer"&gt;dynamic approval selection&lt;/a&gt;, enforce &lt;a href="https://learn.microsoft.com/en-us/entra/id-governance/entitlement-management-access-package-incompatible" rel="noopener noreferrer"&gt;separation-of-duties with incompatible packages&lt;/a&gt;, and enforce &lt;a href="https://learn.microsoft.com/en-us/entra/id-governance/entitlement-management-access-package-lifecycle-policy" rel="noopener noreferrer"&gt;expiry and lifecycle controls&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If that still feels too generic, add custom process hooks through &lt;a href="https://learn.microsoft.com/en-us/entra/id-governance/entitlement-management-logic-apps-integration" rel="noopener noreferrer"&gt;Logic Apps integration for entitlement management&lt;/a&gt;. That is where you inject business-specific gates: risk score checks, change-ticket correlation, regulatory checks, or post-assignment notifications into your SOC workflow.&lt;/p&gt;

&lt;p&gt;This is why I keep calling access packages an underestimated powerhouse. They are not "just governance UI." They are programmable policy choreography.&lt;/p&gt;

&lt;h2&gt;
  
  
  How this looks in a real request loop
&lt;/h2&gt;

&lt;p&gt;A transformational agent pattern becomes concrete when the request loop is concrete.&lt;/p&gt;

&lt;p&gt;Start with the assumption that the agent is under-permissioned for future unknown tasks. When it hits a boundary, it enters a governed escalation loop:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Determine the missing capability from the failed call and policy context.&lt;/li&gt;
&lt;li&gt;Discover candidate packages from &lt;a href="https://learn.microsoft.com/en-us/graph/api/entitlementmanagement-list-accesspackages?view=graph-rest-1.0" rel="noopener noreferrer"&gt;GET access packages&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Inspect assignment rules through &lt;a href="https://learn.microsoft.com/en-us/graph/api/entitlementmanagement-list-assignmentpolicies?view=graph-rest-1.0" rel="noopener noreferrer"&gt;GET assignment policies&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;If policy allows, submit &lt;a href="https://learn.microsoft.com/en-us/graph/api/entitlementmanagement-post-assignmentrequests?view=graph-rest-1.0" rel="noopener noreferrer"&gt;POST assignment request&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Let approval workflow, incompatibility checks, and lifecycle policy decide the final outcome.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If you need to inspect package role mappings more deeply, you can also query &lt;a href="https://learn.microsoft.com/en-us/graph/api/accesspackage-list-resourcerolescopes?view=graph-rest-1.0" rel="noopener noreferrer"&gt;resource role scopes in an access package&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Notice the key architectural boundary: the agent can &lt;em&gt;request&lt;/em&gt; escalation, but it cannot bypass governance. That is the whole point.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why this matters more than one Entra feature
&lt;/h2&gt;

&lt;p&gt;If we are honest, most early agent pilots still hide one uncomfortable truth: broad standing access was pre-granted so the demo would not fail in front of stakeholders.&lt;/p&gt;

&lt;p&gt;That shortcut does not scale. It becomes technical debt with legal exposure.&lt;/p&gt;

&lt;p&gt;The better model is plain:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;capability discovery can be dynamic,&lt;/li&gt;
&lt;li&gt;approval can be policy-bound,&lt;/li&gt;
&lt;li&gt;access can be time-bound,&lt;/li&gt;
&lt;li&gt;accountability can be explicit.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That combination is exactly what transformational agents need. They can evolve without becoming permanent over-privileged identities.&lt;/p&gt;

&lt;h2&gt;
  
  
  The decision point for architecture teams
&lt;/h2&gt;

&lt;p&gt;If your operating model is "we will manually update permissions whenever the agent needs something new," you do not have an agent permission model. You have a backlog with production blast radius.&lt;/p&gt;

&lt;p&gt;If your operating model is "new access must flow through access packages with policy-owned approvals and expiry," then you have something that can survive beyond a pilot and withstand audit pressure.&lt;/p&gt;

&lt;p&gt;That is the shift in one sentence: agent autonomy should grow through governed workflow, not through standing privilege.&lt;/p&gt;

&lt;p&gt;This is why the "missing API Permissions tab" conversation is useful. It forces the right question. Not "where do I click to grant more?" but "what control plane governs permission growth in my tenant?"&lt;/p&gt;

&lt;p&gt;For Entra Agent ID in enterprise reality, the best answer today is access packages.&lt;/p&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/en-us/entra/agent-id/key-concepts" rel="noopener noreferrer"&gt;Microsoft Entra Agent ID key concepts&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/en-us/entra/agent-id/authorization-agent-id" rel="noopener noreferrer"&gt;Authorization in Microsoft Entra Agent ID&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/en-us/entra/identity/enterprise-apps/user-admin-consent-overview" rel="noopener noreferrer"&gt;Overview of user and admin consent&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/en-us/entra/identity/enterprise-apps/configure-user-consent?pivots=portal" rel="noopener noreferrer"&gt;Configure how users consent to applications&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/en-us/entra/agent-id/agent-access-packages" rel="noopener noreferrer"&gt;Access packages for agent identities&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/en-us/entra/id-governance/agent-id-governance-overview" rel="noopener noreferrer"&gt;Governing agent identities&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/en-us/entra/id-governance/entitlement-management-access-package-approval-policy" rel="noopener noreferrer"&gt;Configure approval policy for an access package&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/en-us/entra/id-governance/entitlement-management-dynamic-approval" rel="noopener noreferrer"&gt;Configure dynamic approvals in entitlement management&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/en-us/entra/id-governance/entitlement-management-access-package-incompatible" rel="noopener noreferrer"&gt;Configure separation of duties checks for access packages&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/en-us/entra/id-governance/entitlement-management-access-package-lifecycle-policy" rel="noopener noreferrer"&gt;Change lifecycle settings for an access package&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/en-us/entra/id-governance/entitlement-management-logic-apps-integration" rel="noopener noreferrer"&gt;Trigger Logic Apps with custom extensions in entitlement management&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/en-us/graph/api/entitlementmanagement-list-accesspackages?view=graph-rest-1.0" rel="noopener noreferrer"&gt;List access packages&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/en-us/graph/api/entitlementmanagement-list-assignmentpolicies?view=graph-rest-1.0" rel="noopener noreferrer"&gt;List assignment policies&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/en-us/graph/api/entitlementmanagement-post-assignmentrequests?view=graph-rest-1.0" rel="noopener noreferrer"&gt;Create access package assignment request&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/en-us/graph/api/accesspackage-list-resourcerolescopes?view=graph-rest-1.0" rel="noopener noreferrer"&gt;List resource role scopes in an access package&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>entraagentid</category>
      <category>identitygovernance</category>
      <category>microsoftentra</category>
      <category>aiagents</category>
    </item>
    <item>
      <title>Every change to an Entra extension is a Control Plane event: the monitoring contract</title>
      <dc:creator>Anton Staykov</dc:creator>
      <pubDate>Tue, 07 Jul 2026 09:47:00 +0000</pubDate>
      <link>https://dev.to/astaykov/every-change-to-an-entra-extension-is-a-control-plane-event-the-monitoring-contract-5621</link>
      <guid>https://dev.to/astaykov/every-change-to-an-entra-extension-is-a-control-plane-event-the-monitoring-contract-5621</guid>
      <description>&lt;p&gt;Parts 1 and 2 of this series (&lt;a href="https://dev.to/astaykov/microsoft-entra-extensibility-is-a-gift-it-is-also-control-plane-4egn"&gt;Microsoft Entra extensibility is a gift. It is also Control Plane.&lt;/a&gt; and &lt;a href="https://dev.to/astaykov/securing-the-code-that-decides-who-entra-trusts-a-function-app-and-a-logic-app-end-to-end-1bi0"&gt;Securing the code that decides who Entra trusts&lt;/a&gt;) made two static decisions. &lt;em&gt;Where&lt;/em&gt; the code lives: a dedicated Control Plane subscription, directly under the root management group or under a dedicated Control Plane management group, never in the platform identity subscription or an application landing zone. &lt;em&gt;What credential&lt;/em&gt; it uses to call out: a managed identity by default, &lt;a href="https://learn.microsoft.com/en-us/entra/workload-id/workload-identity-federation" rel="noopener noreferrer"&gt;federated identity credentials&lt;/a&gt; when the call must leave Azure, certificates as a tolerated middle step, and static symmetric keys never.&lt;/p&gt;

&lt;p&gt;Both decisions are one-time. You make them, you walk away, you do not touch them for months.&lt;/p&gt;

&lt;p&gt;The third decision is not like that. It is continuous, and it is the one most teams quietly skip: how do you know the deployed code on that Function App is still the code your reviewers approved? How do you know the Logic App workflow definition has not been rewritten since last Tuesday? How do you know nobody added a federated identity credential to the managed identity at 3 a.m. on a Saturday?&lt;/p&gt;

&lt;p&gt;The answer is monitoring. Not "we have Log Analytics turned on." Monitoring with a specific operating contract attached.&lt;/p&gt;

&lt;h2&gt;
  
  
  The posture inversion
&lt;/h2&gt;

&lt;p&gt;For most Azure workloads, the default operating posture is reasonable trust. Engineers deploy. Pipelines run. Configuration drifts a little. The team reviews changes weekly. Anomalies are caught eventually.&lt;/p&gt;

&lt;p&gt;For a Microsoft Entra extension, that posture is wrong. The default has to be inverted.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Once an Entra extension lands in production, every change to it is suspicious by default.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Not "needs review." Not "let's check first." Suspicious. The default state of an alert firing on a Function App that hosts a &lt;a href="https://learn.microsoft.com/en-us/entra/identity-platform/custom-extension-overview" rel="noopener noreferrer"&gt;custom claims provider&lt;/a&gt; is "the SOC is investigating, prove this was approved." If you cannot prove the change was approved within the team's response SLA, the change is treated as an incident and rolled back. That posture is harsh on purpose. The same RBAC inheritance chain that lets a Contributor four management groups above your Function App quietly replace its code (the central argument of Part 1) is what forces the inverted default. You cannot rely on the deployment surface to be small. You have to rely on the alerting surface to be loud.&lt;/p&gt;

&lt;p&gt;That inversion is the monitoring contract this article is about.&lt;/p&gt;

&lt;h2&gt;
  
  
  The three change surfaces
&lt;/h2&gt;

&lt;p&gt;A production Entra extension has three change surfaces, and each one needs its own forced-diagnostics control, its own alert, and its own row on the operating contract.&lt;/p&gt;

&lt;h3&gt;
  
  
  Surface 1: the runtime itself
&lt;/h3&gt;

&lt;p&gt;This is the surface most teams think of first, and the one most teams under-instrument. The deployed code on the Function App. The workflow definition on the Logic App. The App Settings. The environment variables. The runtime version. The configured custom domain. Each of these is a knob that, if turned, changes the behavior of the extension the next time Entra calls it.&lt;/p&gt;

&lt;p&gt;For the custom claims provider Function App, the relevant Azure Resource Manager operations to alert on are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;Microsoft.Web/sites/extensions/write&lt;/code&gt; (a new deployment was pushed).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Microsoft.Web/sites/config/appsettings/write&lt;/code&gt; (App Settings were changed, including any reference to a Key Vault secret).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Microsoft.Web/sites/config/web/write&lt;/code&gt; (runtime version, startup command, or general configuration was changed).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Microsoft.Web/sites/publishxml/action&lt;/code&gt; (publish profile credentials were generated, which is one of the easiest ways to deploy code outside the normal pipeline).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For the dynamic approval Logic App, the analogous operations are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;Microsoft.Logic/workflows/write&lt;/code&gt; (the workflow definition itself was modified).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Microsoft.Logic/workflows/connections/write&lt;/code&gt; and &lt;code&gt;Microsoft.Web/connections/write&lt;/code&gt; (an API connection was created or modified, including the authentication target it points at).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Each of these should be backed by an &lt;a href="https://learn.microsoft.com/en-us/azure/azure-monitor/alerts/activity-log-alerts" rel="noopener noreferrer"&gt;Activity Log alert&lt;/a&gt; scoped to the Control Plane subscription, with an action group that routes to the SOC's primary intake. Not to the team's chat channel. Not to an email list. The SOC's intake.&lt;/p&gt;

&lt;h3&gt;
  
  
  Surface 2: identity and RBAC
&lt;/h3&gt;

&lt;p&gt;This is the surface that does not look like the extension at all and is therefore the easiest to ignore. The extension is unchanged. The blast radius around it is what changed.&lt;/p&gt;

&lt;p&gt;Specific things to alert on, across the Control Plane subscription:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;Microsoft.Authorization/roleAssignments/write&lt;/code&gt; anywhere on the inheritance path, from the resource up to the subscription. A Contributor (or User Access Administrator, or Owner) just appeared on a tier in the chain. Walk the inheritance (&lt;a href="https://learn.microsoft.com/en-us/azure/role-based-access-control/overview" rel="noopener noreferrer"&gt;Azure RBAC overview&lt;/a&gt;) and ask: does this new principal now have effective Contributor on the Function App? If yes, the SOC needs to know.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Microsoft.ManagedIdentity/userAssignedIdentities/federatedIdentityCredentials/write&lt;/code&gt; on any user-assigned managed identity in scope. A new &lt;a href="https://learn.microsoft.com/en-us/graph/api/resources/federatedidentitycredentials-overview?view=graph-rest-1.0" rel="noopener noreferrer"&gt;federated identity credential&lt;/a&gt; is, by construction, a new way to act as that identity from outside Azure. Treat it as a credential issuance event, because that is what it is.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Microsoft.KeyVault/vaults/accessPolicies/write&lt;/code&gt; and &lt;code&gt;Microsoft.Authorization/roleAssignments/write&lt;/code&gt; scoped to the Key Vault. Same logic as the RBAC inheritance, sharper consequence: Owner on the Key Vault is operationally Owner of every certificate in it.&lt;/li&gt;
&lt;li&gt;App registration credential events in the Entra audit log. The Function App and the Logic App that back the extension are both fronted by app registrations or managed identities in Entra. A new client secret, certificate, or federated credential added to that app registration is an alert-worthy event, full stop. The &lt;a href="https://learn.microsoft.com/en-us/entra/identity/monitoring-health/concept-audit-logs" rel="noopener noreferrer"&gt;Entra audit logs&lt;/a&gt; capture these as &lt;code&gt;Update application – Certificates and secrets management&lt;/code&gt; events; pipe them into the same SOC intake.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Surface 3: the Entra-side binding
&lt;/h3&gt;

&lt;p&gt;This surface lives entirely in Entra, not in Azure. The custom claims provider is &lt;em&gt;bound&lt;/em&gt; to the application through a custom authentication extension configuration. The dynamic approval Logic App is &lt;em&gt;bound&lt;/em&gt; to an access package through an entitlement management custom extension. Those bindings are what make Entra actually call your code. Change the binding, and Entra now calls somebody else's code instead.&lt;/p&gt;

&lt;p&gt;Two specific events worth their own alerts:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Changes to custom authentication extensions in the Entra audit log: the endpoint URL was repointed, the API authentication configuration was changed, the extension was disabled.&lt;/li&gt;
&lt;li&gt;Changes to entitlement management custom extensions: a new callback URL, a new authentication method, a change to the access package policy that consumes the extension.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The same Entra audit log feed that the IGA team already monitors covers most of this. The extra step is making sure the SOC sees it alongside the Azure Activity Log events, because the correlation between "the Entra binding moved" and "a deployment happened on the new target one hour earlier" is exactly the story you want analysts to be able to tell.&lt;/p&gt;

&lt;h2&gt;
  
  
  Forced diagnostics: making the signal undeniable
&lt;/h2&gt;

&lt;p&gt;Alerts only fire if the signal reaches the alert engine. Three controls turn diagnostics from "we enabled it on most resources" into a property of the subscription itself.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Azure Policy with &lt;code&gt;deployIfNotExists&lt;/code&gt; for diagnostic settings.&lt;/strong&gt; Apply &lt;a href="https://learn.microsoft.com/en-us/azure/governance/policy/concepts/effect-deploy-if-not-exists" rel="noopener noreferrer"&gt;built-in or custom policies&lt;/a&gt; at the Control Plane management group that force every Function App, every Logic App, every Key Vault, every Storage Account, and every Managed Identity created underneath it to ship diagnostic logs to a dedicated tenant-level &lt;a href="https://learn.microsoft.com/en-us/azure/azure-monitor/essentials/diagnostic-settings" rel="noopener noreferrer"&gt;Log Analytics workspace&lt;/a&gt;. The policy is &lt;code&gt;deployIfNotExists&lt;/code&gt;, not &lt;code&gt;audit&lt;/code&gt;, so a missing diagnostic setting is repaired by the platform, not just reported on. There are existing built-ins covering Logic Apps and many resource types; custom definitions cover the rest. The principle is: it is not optional, and it is not possible to deploy a resource into this management group without diagnostic settings.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Azure Policy &lt;code&gt;deny&lt;/code&gt; for tampering with diagnostic settings.&lt;/strong&gt; A &lt;code&gt;deployIfNotExists&lt;/code&gt; policy creates the setting. A complementary policy denies its modification or removal by anyone except a tightly-scoped platform identity. If an engineer cannot turn the diagnostic setting off, an attacker who compromises that engineer cannot either.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Resource Locks on the resource groups that host the extensions.&lt;/strong&gt; &lt;a href="https://learn.microsoft.com/en-us/azure/azure-resource-manager/management/lock-resources" rel="noopener noreferrer"&gt;Resource Locks&lt;/a&gt; at &lt;code&gt;CanNotDelete&lt;/code&gt; for everything, &lt;code&gt;ReadOnly&lt;/code&gt; for the resource groups that hold the runtime resources, applied at the resource-group level. ReadOnly does not prevent code deployments through the Function App's own publish endpoint, so it is not a substitute for the alert on &lt;code&gt;Microsoft.Web/sites/extensions/write&lt;/code&gt;, but it does block the entire class of "an attacker recreates the resource with the same name and a different definition" attack.&lt;/p&gt;

&lt;p&gt;Layered: policy forces diagnostics on, policy prevents diagnostics from being turned off, resource locks prevent the resources from being deleted out from under the diagnostics, alerts on the resulting log stream surface every change. Each control compensates for the failure modes of the others.&lt;/p&gt;

&lt;h2&gt;
  
  
  Correlation: the story the SIEM has to be able to tell
&lt;/h2&gt;

&lt;p&gt;Each individual alert is signal. The value lives in the correlation across feeds. The SOC should be able to ask, and answer, questions like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A token issued by Entra at 14:07 carried a claim asserting "department=Finance." The custom claims provider for that application is hosted on Function App &lt;code&gt;fa-claims-prod&lt;/code&gt;. Did the deployed code on &lt;code&gt;fa-claims-prod&lt;/code&gt; change in the last 24 hours? Did its App Settings? Did any role assignment land on it, its resource group, its subscription, or any management group above it?&lt;/li&gt;
&lt;li&gt;An access package request was auto-approved at 09:21 for a privileged group. The dynamic approval Logic App that decided is &lt;code&gt;la-dynapproval-prod&lt;/code&gt;. Did the workflow definition change in the last 7 days? Did any of its API connections get repointed? Did the Logic App's managed identity get a new federated credential?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Both questions are answerable with &lt;a href="https://learn.microsoft.com/en-us/azure/azure-monitor/essentials/platform-logs-overview" rel="noopener noreferrer"&gt;Azure Activity Log&lt;/a&gt;, Entra audit log, Function App and Logic App resource logs, and the extension's own application logs, all sitting in one Log Analytics workspace, with &lt;a href="https://learn.microsoft.com/en-us/azure/sentinel/data-connectors/microsoft-entra-id" rel="noopener noreferrer"&gt;Microsoft Sentinel&lt;/a&gt; on top stitching them together. The Sentinel queries are the easy part. The hard part is making sure the three feeds are guaranteed to be there, which is exactly what the forced diagnostics controls deliver.&lt;/p&gt;

&lt;h2&gt;
  
  
  The operating contract: three states, not four
&lt;/h2&gt;

&lt;p&gt;Every alert from any of the three change surfaces lands in one of exactly three buckets. There is no fourth.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Approved change.&lt;/strong&gt; The deployment was pre-declared in the change management system. The change record carries the artifact hash, the pipeline run ID, the requesting engineer, and a declared start and end window. The alert correlates to the change record automatically (on pipeline run ID, on deployment principal, on time window) and is closed within minutes. The audit trail shows the alert, the correlation, and the closure.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Informational platform change.&lt;/strong&gt; Microsoft platform-driven activity that you do not control: automatic certificate rotations on platform-managed components, Microsoft-side runtime version refreshes, Azure-managed identity key rotations, and similar. These still ship to the same workspace and still fire as Activity Log entries, but they route to a low-severity "platform" queue, not to the SOC's primary intake. Two rules govern this bucket: the events must be attributable to a known Microsoft service principal or platform operation (the actor identity on the Activity Log entry is non-human and matches an allow-listed Microsoft pattern), and the bucket must be reviewed at least weekly so it does not become a quiet hiding place. Anything that does not cleanly match a platform pattern falls through to the third bucket by default.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Incident.&lt;/strong&gt; Anything that is neither pre-declared by a change record nor cleanly attributable to a Microsoft platform operation. SOC intake, paged on-call, automatic rollback if the runtime change can be reverted, manual investigation either way. The default reaction is to revert first and investigate after. The cost of a wrongly-reverted legitimate change is a Slack apology. The cost of a wrongly-allowed malicious change is a forged claim in a token your applications already trusted.&lt;/p&gt;

&lt;p&gt;That is the entire operating model. Approved, informational-platform, or incident. No "we will look at it later." No "probably the platform team." If it does not fit one of the first two buckets within a tight automatic correlation window, it is bucket three.&lt;/p&gt;

&lt;h2&gt;
  
  
  The three-legged stool
&lt;/h2&gt;

&lt;p&gt;Part 1 placed the resources correctly. Part 2 made sure they authenticate without leaking secrets. Part 3 makes sure that, once they are running, every change to them is either justified or visibly investigated. None of the three legs stands alone. A correctly placed resource without monitoring is one quiet deployment away from being someone else's resource. A perfectly monitored resource using a static API key is monitoring around an already-compromised credential. A managed identity in the wrong subscription is a credential model wasted on an over-permissive blast radius.&lt;/p&gt;

&lt;p&gt;Built together, they are how Entra extensibility stays the thing that makes Entra a great product, rather than the thing that quietly hollowed it out. Use the extensibility. Put the code where it belongs. Authenticate it with the right credentials. And treat every change to it, from the first day it is in production, as a Control Plane event worth proving.&lt;/p&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/en-us/entra/identity-platform/custom-extension-overview" rel="noopener noreferrer"&gt;Custom authentication extensions overview&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/en-us/azure/azure-monitor/essentials/diagnostic-settings" rel="noopener noreferrer"&gt;Diagnostic settings in Azure Monitor&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/en-us/azure/azure-monitor/essentials/platform-logs-overview" rel="noopener noreferrer"&gt;Azure Monitor platform logs overview&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/en-us/azure/governance/policy/concepts/effect-deploy-if-not-exists" rel="noopener noreferrer"&gt;Azure Policy &lt;code&gt;deployIfNotExists&lt;/code&gt; effect&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/en-us/azure/azure-monitor/alerts/activity-log-alerts" rel="noopener noreferrer"&gt;Activity log alerts in Azure Monitor&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/en-us/azure/azure-monitor/alerts/alerts-types" rel="noopener noreferrer"&gt;Azure Monitor alert types&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/en-us/azure/azure-resource-manager/management/lock-resources" rel="noopener noreferrer"&gt;Lock your resources to protect your infrastructure (Resource Locks)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/en-us/entra/identity/monitoring-health/concept-audit-logs" rel="noopener noreferrer"&gt;Microsoft Entra audit logs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/en-us/azure/sentinel/data-connectors/microsoft-entra-id" rel="noopener noreferrer"&gt;Microsoft Entra ID data connector for Microsoft Sentinel&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/en-us/graph/api/resources/federatedidentitycredentials-overview?view=graph-rest-1.0" rel="noopener noreferrer"&gt;Federated Identity Credentials overview (Microsoft Graph)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/en-us/azure/role-based-access-control/overview" rel="noopener noreferrer"&gt;Azure RBAC overview&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/en-us/entra/workload-id/workload-identity-federation" rel="noopener noreferrer"&gt;Workload Identity Federation&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>microsoftentra</category>
      <category>azuremonitor</category>
      <category>zerotrust</category>
      <category>secops</category>
    </item>
  </channel>
</rss>
