Agent Workload Identity
Overview
Section titled “Overview”An agent’s name is a selector; its identity is a token an identity
provider signed. When an agent profile names an identity provider, every
data-plane call from that agent must additionally present X-Agent-Token,
verified against that exact provider’s own key set. Without a provider the
agent is trusted on the strength of its Virtual Key alone, which is appropriate
for local development and not much else.
Configure providers under Agentic → Identities → Identity providers, then bind one to an agent from Assets → Agents → Identity & credentials.
Supported provider types
Section titled “Supported provider types”| Type | Use it for |
|---|---|
entra_agent_id | Microsoft Entra Agent ID. Constrained to approved Microsoft authority hosts. |
generic_oidc | Any OIDC issuer with a public HTTPS discovery endpoint. |
zeroid | ZeroID-issued agent identities. |
spiffe | JWT-SVIDs from a SPIFFE issuer, typically an in-cluster SPIRE OIDC discovery provider. |
Endpoints are validated before the gateway dials them: no cloud metadata address is ever reachable, and the hostname is resolved by the same client that connects, so a DNS answer cannot change between validation and the connection.
Admit a class of identities from one provider
Section titled “Admit a class of identities from one provider”Binding a provider to exactly one client id means one provider row per agent —
the same issuer, authority, and audience re-entered for every agent in the
estate. agent_identity_match_mode generalises that from “this one agent” to
“the agents this issuer vouches for”:
| Mode | Admits |
|---|---|
exact (default) | Only the configured agent identity client id. |
list | Any value in agent_identity_match. |
prefix | Any value carrying one of the listed prefixes. |
any | Any identity the issuer signs for. Correct only when the issuer is dedicated to this workspace’s agents. |
Per-agent binding is not weakened by this. Whatever the predicate admits, the
agent subject is still pinned to the exact identity the token carries — the
predicate decides which identities a provider may speak for, never which agent
a given token is. An unrecognised mode collapses to exact, so a typo can
never widen a provider.
Bound accepted token lifetime
Section titled “Bound accepted token lifetime”exp alone leaves the issuer deciding how long a stolen credential stays
useful: an issuer minting 24-hour agent tokens has every one of them honoured
for its full life, so a token lifted from a log or a crash dump is valid for
the rest of the day.
accepted_token_max_ttl_sec on the identity provider is the relying party’s
own tolerance:
| Value | Effect |
|---|---|
0 (default) | A bounded default of one hour. Accommodates the common one-hour token while removing the unbounded case. |
| n > 0 | Refuse a token from this provider whose total lifetime exceeds n seconds. |
| n < 0 | Disable the ceiling. An operator has to choose this deliberately. |
This is separate from token_max_ttl_sec, which is mint-side and only shortens
the expiry on tokens the gateway itself hands out.
Replay protection
Section titled “Replay protection”A workload token carrying a jti is refused if it is presented twice. Entries
are remembered only until the token would expire anyway, since a token past its
exp is already refused by claim validation. Tokens without a jti are not
tracked — the claim is optional in JWT and requiring it would break issuers
that omit it.
Replay protection stops a credential recovered from a log, a proxy trace, or a crash dump and reused afterwards. It cannot help against an attacker who intercepts a token before its first use, because nothing yet ties the token to the sender. That is what proof-of-possession adds.
Proof-of-possession (DPoP)
Section titled “Proof-of-possession (DPoP)”Set require_proof_of_possession on an identity provider to refuse a plain
bearer token from it. The client then holds a private key, the issuer binds
that key’s thumbprint into the token as cnf.jkt, and every request carries a
fresh proof signed by the key over the request method and URI
(RFC 9449). A stolen token
without the key is useless.
- It is off by default, and should stay off until the issuer actually binds
tokens. A token with no
cnf.jkthas nothing to prove possession of, so enabling this first denies everything. - A token that carries no
cnf.jktbehaves exactly as before, so enabling binding at the issuer does not break existing callers ahead of the switch. - The Python SDK generates an ephemeral per-process key and needs the optional
dependency:
pip install "deepintshield[dpop]==2.8.3". See Agents and Agentic governance.
Protecting an approved binding
Section titled “Protecting an approved binding”Once an agent’s registration is approved with an identity provider, clearing
that provider is an attestation downgrade: the agent would keep every grant
it earned while proving less about who it is. PUT /agents/{id}/governance
therefore refuses to empty identity_provider_id on an approved agent —
changing it requires an explicit re-review.
Expected identities: the resolution layer
Section titled “Expected identities: the resolution layer”A workload often announces itself by a friendly name (agent:gisec-demo-agent)
while the identity a provider can prove for it is a client id
(agent:79330596-…). An expected identity declares, per friendly agent key
and workspace, which provider-issued identity that name is expected to prove.
It is modelled on a SPIRE registration entry: it names the identity a workload
should attest to, and it never attests anything itself.
What a declaration does:
- Resolves the name before validation and verification. At first sighting,
a token that verifies against the declared provider and derives to exactly
the expected subject attests the registration under the friendly name. At
runtime, a bound agent’s token is verified as the expected identity while the
agent keeps its friendly subject - so every
allowed_callertuple, resource grant and delegation stays keyed toagent:<key>and survives the upgrade from VK-trusted to provider-attested. - Unlocks an explicit VK-trusted approval. With an expected identity declared, the review form offers Approve as VK-trusted: the agent is approved with no provider, recorded as unattested, and carries its own exit - the declaration says what proof will complete it.
- Proposes, never promotes. When an approved VK-trusted agent later presents a verified token for exactly its expected identity, the gateway records an attestation upgrade proposal. The call proceeds VK-trusted and nothing on the agent changes until an operator confirms in the review dialog. Confirmation binds the provider, records which provider attested the agent and when, and marks the declaration bound - in one transaction, under row locks, re-checking that the proposal still matches the declaration and that the provider is enabled and tested.
Invariants the layer enforces:
- A declaration can be satisfied only by a verified token for exactly the expected subject from exactly the expected provider. Selecting the expected provider in a review for a registration that never presented such a token is refused - declaring is not vouching.
- One name, one identity; one identity, one name per workspace, enforced by the database. Two names can never race for the same credential.
- A bound declaration is immutable: re-pointing a name at another identity is an identity change and goes through registration review again. Deleting it is refused for the same reason.
- Zero cost on the hot path: the resolved identity is denormalised onto the agent profile the request already loads, so the runtime never consults the declaration table. A provider-less call that carries no token does no resolution work at all.
Manage declarations from the registration review dialog (Expected identity) or the API:
GET /api/agentic-new/registry/identity-aliasesPUT /api/agentic-new/registry/identity-aliases {agent_key, expected_provider_id, expected_subject}DELETE /api/agentic-new/registry/identity-aliases/{id}POST /api/agentic-new/registry/registrations/{id}/attest confirm a verified proposalPOST /api/agentic-new/registry/registrations/{id}/attest/dismissNext steps
Section titled “Next steps”- Agent Enrolment — enrol a class of verified agents from one rule.
- Agentic — the full control-plane workflow.
- Setting up Entra — connect Microsoft Entra.