SPIFFE with Microsoft Entra Agent ID sounds almost too tidy.
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.
The architecture is viable. Microsoft explicitly lists SPIFFE and SPIRE among its supported workload identity federation scenarios, and Entra Agent ID supports third-party agents through direct workload identity federation.
But the clean diagram hides a trust gap.
SPIFFE proves which workload is running. Microsoft Entra decides which agent identity that workload may act as. 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.
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.
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 |
+---------------------------------------->|
|
3. Returns T1 |
v
+--------------+--------------+
| Child agent identity |
| |
| T1 exchanged for a |
| resource-scoped token |
+--------------+--------------+
|
4. Entra access token |
<-----------------------------------------+
The SPIFFE workload authenticates the blueprint.
The child agent identity is the principal authorized on downstream APIs.
SPIFFE and Entra answer different questions
SPIFFE defines a platform-neutral identity framework for software workloads. Its central identifier is the SPIFFE ID, a URI such as spiffe://example.com/agent/invoice-reconciler. A SPIFFE implementation such as SPIRE attests a workload using runtime evidence, then makes short-lived identity documents available through the SPIFFE Workload API.
Microsoft Entra Agent ID starts one layer higher. An agent identity is a specialized service principal 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 agent identity blueprint holds credentials and acquires tokens on behalf of child agent identities.
That division is useful:
- SPIRE answers: is this the workload that my runtime policy registered?
- Entra answers: which enterprise agent identity may this authenticated workload use, and what may that identity access?
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 agent identity token acquisition.
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.
Only one of the two SVID forms fits the exchange
SPIFFE defines two common forms of SPIFFE Verifiable Identity Document, and they are not interchangeable.
An X.509-SVID 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.
A JWT-SVID is a signed bearer JWT. Its sub claim must be the workload's SPIFFE ID, and it must contain both aud and exp. That shape fits Microsoft Entra workload identity federation because Entra's client credentials flow accepts a JWT from an external identity provider as the client_assertion (federated client credential flow).
This is the first catch: an X.509-SVID is not a federated identity assertion for this flow.
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 (workload identity federation mechanics).
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.
The bridge is an exact three-claim contract
A federated identity credential, or FIC, tells Entra which external JWT may authenticate a workload identity. Three values form the contract:
-
issuermust match the incoming JWT'sissclaim. -
subjectmust match the incoming JWT'ssubclaim. -
audiencesmust contain the audience Entra expects in the incoming JWT, normallyapi://AzureADTokenExchange.
Microsoft performs this match exactly and case-sensitively. Wildcards are not supported in standard FIC property values, and an application or agent identity blueprint can have at most 20 FICs (FIC considerations).
For one production agent workload, the incoming JWT-SVID could look like this:
{
"iss": "https://oidc.example.com",
"sub": "spiffe://example.com/agent/invoice-reconciler",
"aud": ["api://AzureADTokenExchange"],
"exp": 1784304000
}
The corresponding FIC on the agent identity blueprint's application object is:
{
"name": "spiffe-production-invoice-agent",
"issuer": "https://oidc.example.com",
"subject": "spiffe://example.com/agent/invoice-reconciler",
"audiences": [
"api://AzureADTokenExchange"
]
}
Incoming JWT-SVID Federated identity credential
iss: https://oidc.example.com ===> issuer: https://oidc.example.com
sub: spiffe://example.com/ ===> subject: spiffe://example.com/
agent/invoice-reconciler agent/invoice-reconciler
aud: api://AzureADTokenExchange ===> 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.
The subject is where SPIFFE fits naturally. The JWT-SVID specification requires sub to equal the workload's SPIFFE ID, so the FIC binds one precise SPIFFE workload identity to one blueprint credential path.
The audience is where architecture becomes policy. SPIFFE allows site-specific audiences and recommends scoping them to the intended recipient (JWT-SVID audience requirements). For this token, the intended recipient is Entra's token exchange. The workload should therefore request a JWT-SVID with the single audience api://AzureADTokenExchange, matching the FIC.
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 strongly recommends single-audience JWT-SVIDs.
The token flow, without the hand-waving
The autonomous flow has seven steps:
- SPIRE attests the agent workload according to its registration and runtime selectors.
- The workload requests a JWT-SVID from the local Workload API with
api://AzureADTokenExchangeas its sole audience. - SPIRE issues a short-lived JWT-SVID whose
subis the workload's SPIFFE ID. - The workload sends that JWT to the Microsoft identity platform token endpoint as
client_assertion, requestsapi://AzureADTokenExchange/.default, and supplies the child agent identity's client ID infmi_path. - Entra discovers the external issuer's signing keys, validates the JWT, compares
iss,sub, andaudagainst the FIC on the blueprint, and returns the blueprint exchange tokenT1. - The workload sends
T1as theclient_assertionin a second request whoseclient_idis the child agent identity and whose scope names the downstream resource. - 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.
The first request uses the JWT-SVID to authenticate the blueprint. The fmi_path parameter selects the child agent identity for which the blueprint is requesting an exchange token, exactly as documented in the autonomous agent authentication flow:
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}
&scope=api%3A%2F%2FAzureADTokenExchange%2F.default
&fmi_path={agent-identity-client-id}
&client_assertion_type=urn%3Aietf%3Aparams%3Aoauth%3Aclient-assertion-type%3Ajwt-bearer
&client_assertion={jwt-svid}
&grant_type=client_credentials
That request returns T1, not the Microsoft Graph token. The second request switches client_id from the blueprint to the child agent identity and uses T1 as its assertion (request an agent identity token):
POST https://login.microsoftonline.com/{tenant-id}/oauth2/v2.0/token
Content-Type: application/x-www-form-urlencoded
client_id={agent-identity-client-id}
&scope=https%3A%2F%2Fgraph.microsoft.com%2F.default
&client_assertion_type=urn%3Aietf%3Aparams%3Aoauth%3Aclient-assertion-type%3Ajwt-bearer
&client_assertion={agent-blueprint-token-T1}
&grant_type=client_credentials
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.
A SPIFFE bundle endpoint is not OIDC discovery
This is the catch most diagrams omit.
SPIFFE defines how trust bundles carry X.509 authorities and JWT signing keys. Its federation specification 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 use value is jwt-svid (JWT-SVID key publication).
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 (external issuer configuration).
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.
The production architecture therefore needs an issuer service that:
- exposes
/.well-known/openid-configurationfor the configured HTTPS issuer; - exposes a JWKS endpoint referenced by that discovery document;
- publishes the active SPIRE JWT signing keys in an Entra-compatible JWKS;
- keeps the JWT-SVID
issclaim exactly aligned with the configured issuer URL; - rotates published keys without creating a window in which valid JWT-SVIDs cannot be verified.
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.
Microsoft also documents that the identity platform stores only the first 100 signing keys downloaded from an external OIDC endpoint (workload identity federation mechanics). That limit should be easy to respect, but it makes stale-key accumulation an operational concern rather than harmless clutter.
Short-lived bearer tokens can still be replayed
JWT-SVIDs reduce credential lifetime. They do not eliminate bearer-token behavior.
The SPIFFE specification is direct: JWT-SVIDs are susceptible to replay. A short exp narrows the window. A jti can support replay detection, but JWT-SVID validators are not required to track jti uniqueness. Confidential transport remains mandatory because possession of the token is sufficient to present it during its valid lifetime (JWT-SVID transport security).
For this exchange, that produces a clear baseline:
- request the JWT-SVID only when an Entra token is needed;
- use one audience,
api://AzureADTokenExchange; - keep JWT-SVID lifetime as short as the exchange path tolerates;
- never log the assertion or place it in agent prompts, tool output, or distributed traces;
- send it directly to Entra over TLS;
- cache the resulting resource token according to its lifetime, not the source assertion.
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.
Rotation now crosses an organizational boundary
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.
That is four operational responsibilities, even if one platform team implements all of them.
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 (supported signing algorithms). 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.
FIC changes also take time to propagate. Microsoft warns that token requests made shortly after a FIC change can fail with AADSTS70021 until regional caches update, and recommends retry logic (FIC propagation). 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.
The 20-credential limit is an architecture signal
Standard FICs bind exact issuer and subject values, and each application or blueprint is limited to 20 credentials (FIC design considerations).
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.
Microsoft has introduced flexible federated identity credentials 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.
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.
Workload federation authenticates the client in both autonomous and OBO flows
The source assertion proves the workload's identity. That makes the JWT-SVID a client credential, not a statement about whether the resulting flow is autonomous or delegated.
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 (Microsoft identity platform OBO flow, Agent ID OBO flow).
For Agent ID, this remains a two-stage exchange. First, the blueprint presents the JWT-SVID as its client_assertion, includes the child agent identity in fmi_path, and receives T1. The child agent identity then performs the OBO exchange by presenting both T1 as its client credential and the incoming user access token as the user assertion:
POST https://login.microsoftonline.com/{tenant-id}/oauth2/v2.0/token
Content-Type: application/x-www-form-urlencoded
client_id={agent-identity-client-id}
&scope=https%3A%2F%2Fresource.example.com%2Fscope1
&client_assertion_type=urn%3Aietf%3Aparams%3Aoauth%3Aclient-assertion-type%3Ajwt-bearer
&client_assertion={agent-blueprint-token-T1}
&grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Ajwt-bearer
&assertion={incoming-user-access-token}
&requested_token_use=on_behalf_of
The distinction between client_assertion and assertion is the architecture. client_assertion proves which client is making the exchange. assertion 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.
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.
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.
The production decision
Use this pattern when all of the following are true:
- the organization already operates SPIRE as a workload identity authority;
- the agent runs outside Azure or across heterogeneous infrastructure;
- the agent needs a governed Microsoft Entra identity for autonomous or OBO access;
- eliminating blueprint secrets is worth operating an OIDC discovery and JWKS publication surface;
- SPIFFE registration policy and Entra blueprint ownership have accountable operators;
- exact workload-to-blueprint mappings fit the intended blast-radius model.
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 assertion 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.
The final architecture is not "SPIFFE authenticates to Entra." That phrase hides too much.
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.
Two control planes. One narrow trust contract. No shared secret.
That is a strong architecture, provided you operate all three parts of the sentence.
References
- SPIFFE overview
- Working with SVIDs
- JWT-SVID specification
- X.509-SVID specification
- SPIFFE federation specification
- Microsoft Entra workload identity federation
- Important considerations for federated identity credentials
- Configure an app to trust an external identity provider
- Integrate third-party agents with Microsoft Entra Agent ID
- Agent identities in Microsoft Entra Agent ID
- Authenticate and acquire tokens for autonomous agents
- Microsoft identity platform and OAuth 2.0 On-Behalf-Of flow
- Agent OAuth flows: On-behalf-of flow
- Federated identity credentials overview
- OAuth 2.0 client credentials flow with a federated credential
- Flexible federated identity credentials
- RFC 7523: JWT profile for OAuth 2.0 client authentication and authorization grants
Top comments (0)