In April, a Cursor agent working on a routine task in PocketOS's staging environment hit a credential mismatch. It decided to fix it by deleting a Railway volume. To do that it needed a token, so it went looking and found one in an unrelated file. The token had been created to manage custom domains through Railway's CLI. One GraphQL volumeDelete call later, about nine seconds in, the production database was gone.
The detail that matters here: Railway's own write-up says the token was provisioned with account-scoped access, "the maximum access possible", even though narrower workspace and project tokens existed. "The right scope existed," they wrote, "but the flow didn't make it obvious which one to pick." Railway did recover the database, and the customer got all their data back.
So, the direct answer. Least privilege for AI agents means each agent has its own identity, holds only short-lived tokens issued for one API and one operation, and, when it acts for a person, gets its token through OAuth 2.0 Token Exchange (RFC 8693), so the token names both the person and the agent and can never carry more rights than that person holds. Below: how that works, what Microsoft, Okta and Google now ship, and a small demo I ran locally.
Key takeaways
- Most agents today run on a borrowed credential: a developer's personal token, a CLI token from a dotfile, or a shared service account. That is the real blast radius, whatever the system prompt says.
- Give every agent its own identity. Then give it tokens that expire in minutes, work against one audience, and carry one scope.
- When an agent acts for a person, exchange the person's token for a narrower one with an
actclaim. Rights become the intersection of what the user holds and what the agent is ever allowed. - Entra Agent ID, Okta for AI Agents and Google Cloud Agent Identity are all generally available now. The MCP spec requires audience-bound OAuth tokens. None of them fixes a token sitting in a file.
Why does your agent have your admin token?
Because it was the easiest thing to give it. The same three setups come up again and again:
- The agent runs inside a developer's terminal or IDE and inherits everything that developer can do: cloud CLI sessions,
.envfiles, a GitHub token withreposcope. - The agent runs under a shared service account created years ago for an integration, which has quietly collected permissions ever since.
- An API key is pasted into its configuration, with whatever the default scope was.
In all three, "what can this agent do?" has the same answer as "what can the most privileged thing near it do?". The system prompt says "never run destructive commands". The credential says otherwise, and the credential wins. (More on that in why system prompts are not security controls.)

The PocketOS token could manage domains. It could also delete volumes. That gap between what a credential was created for and what it can do is exactly what an agent will find, because agents are good at finding a path to their goal.
What does a least-privilege identity model for agents look like?
Strip away vendor names and it comes down to five rules. We use the same rules in our builds, and they line up with the deny-by-default approach on our AI governance page.
Each agent is its own principal. A named identity with an owner, so you can see it in logs, disable it in one place, and answer "which agent did this?" without reading prompts.
Tokens are short-lived. Minutes, not months. A token that leaks from a log, a trace or a prompt should be useless by the time anyone finds it.
Tokens are audience-restricted. A token minted for the orders API is rejected by the billing API. This is the aud claim, the cheapest control here and the one most teams skip.
Scopes map to operations, not to systems. orders:read and orders:refund are different permissions. "Access to the ERP" is not a permission; it is a wish.
Credentials are bound to the environment. A staging agent gets staging tokens from a staging issuer. Where the platform supports it, tokens are bound to a key or certificate the agent holds, so a copied token alone is not enough.
The fifth rule alone would probably have stopped PocketOS. Each of the others would have made it smaller.
What is OAuth token exchange, and why do agents need it?
Most useful agents act for somebody. An operations lead asks the assistant which orders are stuck. The agent should see what she can see, and nothing more. It also should not simply hold her token, because then the agent is her, and every log line says she did it.
RFC 8693, OAuth 2.0 Token Exchange, solves this. The agent sends the authorisation server two things: the user's token (the subject_token) and proof of its own identity (the actor_token). The server checks both and issues a new token where sub is the user and the act claim names the agent. RFC 8693 calls this delegation, as opposed to impersonation, and the difference is the whole point.

The authorisation server is where least privilege actually gets enforced. The rule I like is simple: the exchanged token's scopes are the intersection of what was requested, what the user holds, and the ceiling configured for that agent. If the operations lead can issue refunds but the order assistant may never issue refunds, the assistant cannot get a refund token, even for her.
Does it actually work? A local token-exchange demo
I wanted to see this end to end, so I wrote a small authorisation server and a resource server in Node.js with the jose library (v6.2.14), and ran them on my machine. No cloud, no vendor, about 220 lines. Alice holds orders:read and orders:refund. The agent, agent:order-assistant, has a ceiling of orders:read.
The heart of the exchange endpoint is this check, copied from the code I ran:
// 3. Scope = requested ∩ what the user holds ∩ the agent's ceiling.
const requested = (f.get('scope') || '').split(' ').filter(Boolean);
const userScopes = subj.scope.split(' ');
for (const s of requested) {
if (!userScopes.includes(s)) return deny(res, 'invalid_scope', `${subj.sub} does not hold ${s}`);
if (!client.ceiling.includes(s))
return deny(res, 'invalid_scope', `${client.id} may never hold ${s}`);
}
Before that, it verifies the user's token was issued to this agent and that the actor token belongs to the authenticated client. After it, it signs a token with sub, act, one aud and a five-minute expiry. Here is the agent side running:

Read the two refusals. Asking for orders:refund fails because the agent's ceiling forbids it, even though Alice holds it. Asking for orders:delete fails because Alice herself does not hold it. The agent cannot climb above either limit.
Then the resource server. It checks signature, issuer, audience and expiry, and then the scope each route needs. I called it with curl:

The refund gets a proper insufficient_scope challenge, the same response the MCP spec uses for step-up. Then I waited five minutes and tried the read again. Expired. That is the line I would show a sceptical CFO: a leaked token that dies on its own.

Every log line names both the person and the agent. That is the answer you want when someone asks "who did this?", and you only get it if the token carries both.
It is a teaching rig, not production code: sign-in is a stub (really authorisation code with PKCE), the agent uses a client secret rather than a workload identity, and there is no token binding. The shape is what transfers.
What do Microsoft, Okta and Google ship for agent identity?
This changed most in 2026. All three big identity platforms now ship something generally available.
Microsoft Entra Agent ID is generally available. An agent identity is a special service principal created from an "agent identity blueprint", and every agent gets a sponsor (business accountability) and an owner (technical). Agents acting for a user use Microsoft's On-Behalf-Of flow for agents: the blueprint presents its own credential plus the user's token, names the specific agent identity with an fmi_path parameter, and Entra issues a token for the downstream resource. It is Microsoft's own protocol rather than RFC 8693 by name, but it does the same job: both identities are checked, and the agent can only do what the user can delegate. Conditional Access has agent templates too. One caveat: the admin-centre wizard for creating blueprints and agent identities is still marked preview.
Okta made Okta for AI Agents generally available on 30 April 2026 (discovery, owners, lifecycle and governance for agents), and on 24 August made Agent SSO generally available as part of core Okta SSO. Agent SSO is built on Cross App Access (XAA), which uses the IETF draft Identity Assertion JWT Authorization Grant (ID-JAG). The agent exchanges the user's ID or refresh token at the identity provider for an ID-JAG, then presents that to the target app's authorisation server for a short-lived, scoped access token. Okta's own docs are clear that XAA is for flows started by a signed-in human, not for autonomous background agents.
Google Cloud Agent Identity went GA on 22 April 2026, with the auth manager and Agent Identity APIs following on 22 August. It gives each agent a SPIFFE-based identity tied to the resource that hosts it, with an X.509 certificate valid for 24 hours and rotated automatically. Unlike service accounts, Google says, agent identities are not shared by default, cannot be impersonated and do not allow long-lived keys, and their Google Cloud tokens are bound to the agent's certificate. When the agent acts for a user, logs show both identities. One trap in its docs: deleting an agent does not remove its IAM bindings.
Underneath Google's approach sits SPIFFE and SPIRE, the open-source workload identity standard and its reference implementation. If your agents run on your own Kubernetes or VMs, SPIRE is how you get the same "identity from attestation, not from a secret in a file" model without a cloud vendor.
Two standards are worth tracking. The MCP authorization spec (current revision 2026-07-28) builds on OAuth 2.1 and requires clients to send RFC 8707 resource indicators, and servers to accept only tokens issued for them and never pass tokens through to other services. That is audience restriction, written into the protocol most agents now use for tools. And the IETF WIMSE working group is writing the standards for workload identity across platforms: an architecture draft, workload proof tokens, and a charter item for token exchange at trust boundaries, possibly based on RFC 8693. These are Internet-Drafts, not standards. Read them for direction; do not build a compliance claim on them.
| Option | Status (Oct 2026) | Acting for a user | Best fit |
|---|---|---|---|
| Microsoft Entra Agent ID | GA (creation wizard in preview) | Agent On-Behalf-Of flow | Microsoft 365 and Azure estates |
| Okta for AI Agents / XAA | GA (30 Apr; Agent SSO 24 Aug) | ID-JAG via the enterprise IdP | SaaS-to-SaaS agents with a signed-in user |
| Google Cloud Agent Identity | GA (identity 22 Apr; APIs 22 Aug) | Auth manager, 3-legged OAuth | Agents hosted on Google Cloud |
| SPIFFE / SPIRE | Open source, CNCF | Combine with your OAuth server | Self-hosted agents, mixed environments |
| MCP authorization | Spec revision 2026-07-28 | OAuth 2.1 with resource indicators | Any agent calling MCP tools |
| IETF WIMSE | Internet-Drafts | Token exchange planned | Direction of travel, not yet a standard |
Where does least-privilege identity stop helping?
Identity tells you who may do what. It does not tell you whether this action, right now, is a good idea. Zenity's Chris Hughes made the same point about PocketOS: even perfectly scoped tokens leave the agent free to do damage inside its scope.
We learned a version of this on our own demo. In October we ran 12 tests against the governance in Ware, our simulated warehouse with a real AI recovery agent. One finding: under delegated authority, the agent could take a packer off a bench that was packing another customer's order. Every permission check passed. The delegation was simply broader than anyone meant it to be. We found it and fixed it, but a token exchange would not have caught it on its own.
So token exchange is the floor, not the ceiling. Above it you still want policy checks per action, approvals bound to the exact arguments for anything irreversible, and an audit log nobody can quietly edit. Our governance page describes how we let an agent earn more autonomy one operation at a time, and the human-in-the-loop post covers the approval side.
What should you do this week?
Start with an inventory, not a platform purchase. For each agent, write down which credential it actually uses and what that credential can do. You will probably find a borrowed personal token. Replace it with an identity the agent owns, cut its tokens to minutes and one audience, and put an exchange step in front of anything it does for a user.
If you want help, this is the core of the Access Layer package: per-operation permissions, a reviewed write queue and an audit log in front of the systems your agents touch. For a wider look at where your agents stand, start with an AI readiness audit.
Frequently asked questions
What is least-privilege access for AI agents?
Each agent has its own identity and only short-lived tokens for the specific APIs and operations its job needs. It never runs on a person's or a shared account's full credentials, and anything it does for a user is capped at that user's rights.
How does OAuth token exchange work for an AI agent?
The agent sends the authorisation server the user's token and its own identity token. The server checks both and issues a new, narrower token where sub is the user and the act claim names the agent, as defined in RFC 8693. The resource server then sees, and can log, both.
Is Microsoft Entra Agent ID generally available?
Yes. Microsoft lists Entra Agent ID as generally available, with agent identities, blueprints, sponsors, an agent On-Behalf-Of flow and Conditional Access templates for agents. The admin-centre wizard for creating blueprints and agent identities is still marked preview.
Does the MCP specification require scoped tokens?
It requires audience-bound ones. Clients must send RFC 8707 resource indicators, and MCP servers must only accept tokens issued for them and must not pass tokens through. Scope design per tool is left to you, and the spec encourages minimal scopes with step-up when more is needed.
Is scoping tokens enough to make an agent safe?
No. Scoping limits what an agent can reach, not what it chooses to do inside that limit. You still need per-action policy checks, human approval for irreversible writes, and a tamper-evident audit log.
