Agents are doing real work in our products now. They read records, draft emails, file tickets and reconcile invoices. To do any of that they need access, and when it's time to give it to them, most teams reach for one of two tools. Both are wrong.
Option one: a service account. The agent gets its own credentials with a fixed set of permissions. It works, but it's standing access that no user ever agreed to, it isn't bound to any user, and every action shows up in the audit log as "the robot did it".
Option two: let the agent act as the user. Pass the user's token through, or mint one that impersonates them. Now the agent can do everything the user can do. A task that needed one key gets the whole keyring.
I think agents deserve to be a first-class principal in IAM, next to users and service accounts, built on one rule:
An agent owns no access. It borrows access from the person it works for, one request at a time, with that person's consent, for the current session only, and never more than that person has.
The rest of this post is what that rule looks like in practice: the entities IAM needs, agent templates, a permission catalog that IAM hosts as a tool, three user journeys with wireframes, and the parts I'm still arguing with myself about.
TL;DR
- Every agent session starts with zero access.
- The agent asks IAM for what it needs when it needs it, through tools, and gives a reason.
- The user approves in the chat, the way AI coding tools ask before running a command: Allow once, Always allow, Deny.
- Grants last for the session only. "Always allow" saves a rule that skips the prompt next time. It doesn't preload access.
- An agent can never hold more than its user has right now, and some permissions are never available to agents at all.
- Agent templates define what a kind of agent may ever ask for, plus the context it needs to ask well.
- IAM hosts a permission catalog tool, so builders and agents can find out what exists, what's risky and what's off-limits.
Where the industry is today
As of October 2026 there's no finished standard for agent access. There is a lot of convergence, though.
Standards and guidance
- The Model Context Protocol authorization spec builds agent tool access on OAuth 2.1. Each MCP server publishes which authorization server it trusts, and each token is bound to one server.
- The IETF OAuth working group has a draft for Transaction Tokens: short-lived signed tokens that carry the user and the context along a chain of service calls. Several individual drafts propose agent-specific profiles (agent grants, agent instances, identity assertion grants), and none has been adopted by a working group yet.
- The OpenID Foundation whitepaper Identity Management for Agentic AI says existing standards cover simple agents inside one organization, and recommends delegation through OAuth Token Exchange (RFC 8693).
- NIST's NCCoE started a project on software and AI agent identity. Most of its 600+ public comments asked to build on existing standards, and agents that start other agents are flagged as an open problem.
- The OWASP Top 10 for Agentic Applications lists identity and privilege abuse as a top risk. It recommends short-lived, narrowly scoped credentials per task and per agent, a fresh check before each privileged step, and human approval before an escalation.
What vendors ship
- Microsoft Entra Agent ID gives each agent its own identity, created from a reusable blueprint and owned by a named human sponsor. High-risk permissions are blocked for agents even when someone consents.
-
Okta Cross App Access lets the company's identity provider decide which agent can reach which app. Its tokens carry the user as
suband the agent asact. - Auth0 for AI Agents adds a vault for third-party tokens, approval pushed to the user's phone (CIBA) for actions that need consent, and fine-grained checks on every action.
- AWS Bedrock AgentCore Identity swaps the user's token for a narrower one per downstream service, carrying both identities.
The convergence is real: agents get their own identity, delegation goes through token exchange with the agent recorded in act, credentials are short-lived, and some permissions are blocked outright. What's still mostly missing is the part in the middle. What happens between "the agent asked" and "the agent got it"? Who consents, to what, for how long, and what do subagents inherit? That's the part this post is about.
Seven principles
- Zero standing access. An agent, and every new session of it, starts with nothing.
- Never more than the user. Every grant is checked against the user's current access, both when it's granted and at every token refresh.
- Only the user can consent. Approval needs the user's own credential. The agent can ask, but it can't approve.
- Session-scoped. Grants end when the session ends or is revoked.
- Inherit down, never up. A subagent gets at most its parent's access, and what a subagent is granted stays with it.
- Remember the consent, not the access. Saved rules let a user skip the prompt next time. Each session still starts at zero.
- Everything is traceable. Every request, decision, grant and call records the agent, the user and the session.
A new entity in IAM: the agent
Here's how an agent compares with the two options we have today.
| Service account | Acting as the user | Agent | |
|---|---|---|---|
| Acts for | Itself | A user | A user |
| Access when it starts | Whatever an admin granted | All of the user's access | None |
| Who decides its access | An admin, up front | Nobody | The user, per request |
| Bounded by the user | No | Equal to the user | Always at or below the user |
| How long access lasts | Until someone changes it | The token's lifetime | The session |
| Audit shows | The service account | The user | The agent, the user and the session |
| Stop one run | No | No | Yes |
To support this, IAM and user management need a few new things: an agent template (what a kind of agent may ever ask for), an agent (a deployed agent with its own credential), an agent session (one run for one user), grants, access requests, consent rules and an activity log.
One design choice matters more than it looks: none of the existing grant tables (role assignments, group memberships, user permissions) ever point at an agent. Agent grants live in their own table and die with their session, so standing access for an agent can't exist even by accident.
What an agent can borrow
Most systems have more than one kind of permission, and an agent should be able to borrow each of them, with the same rules applied to all.
| Kind | Controls | Example |
|---|---|---|
| API permissions | Which endpoints and actions it can call | Accounts API · Read |
| Data permissions | Which records and fields it can see | Accounts in my region |
| Asset permissions | Which objects it can open | Q3 pipeline dashboard · View |
| Tool and outbound permissions | Which tools it can run and where data can go | Send email to customers |
| Never delegable | Nothing an agent can ever get | IAM admin rights, emergency access, managing agents or consent |
Your system may have more kinds, such as feature flags, billing actions or workflow approvals. The rules don't change. Each kind is a type of grant with its own enforcement point.
Agent templates: permissions plus context
An agent template is a reusable definition of one kind of job. It holds:
- Purpose: what the agent is for.
- Ceiling: the most it may ever ask for, by kind of permission. A request outside it is refused, whatever the user has.
- Context: how the agent should reason about access.
- Modes allowed, limits (session lifetime, subagent depth, request rate) and child templates it may start as subagents.
- Sensitive items, which always prompt.
- A sponsor, the human accountable for it, and a version.
The ceiling stops an agent from overreaching. It doesn't make the agent ask well. Left alone, an agent will often request its whole ceiling up front, because that's the easiest way to never be blocked. The context is there so the agent does the analysis every time: what's the smallest set of access this task needs, when is sensitive data actually justified, and when should it give access back.
Ask only for accounts in the renewal window, never the whole book. Prefer read access. Ask for contract pricing only when the user asks about price. Release access as soon as a brief is done.
Organizations can turn a template on and narrow it, but never widen it. A new version that widens the ceiling asks users for consent again. A version that only narrows it doesn't.
The permission catalog: an IAM tool for builders and agents
Here's a problem I didn't expect. People building agents don't know the permission model, and agents guess. IAM is the only system that knows what permissions exist, what each one unlocks, how risky it is, which data is sensitive and what's never delegable.
So IAM should publish that knowledge as a tool. iam.describe_permissions(task) searches the catalog for a task. iam.explain(permission) says what a permission unlocks, how risky it is, where it's enforced and whether an agent can have it. Builders use it to write ceilings. Agents use it at run time to plan the smallest request. Security reviewers use it to see what a new template version adds.
The same IAM tools give the agent its access during a session:
| Tool | What it does |
|---|---|
iam.describe_permissions(task) |
Finds the permissions that fit a task, with risk and sensitivity |
iam.request_access(items, reason) |
Asks for access for this session. Each item is granted, refused or sent to the user |
iam.my_access() |
Lists what this session holds right now |
iam.start_subagent(template, read_only) |
Starts a subagent from an allowed child template |
iam.release_access(items) |
Gives access back early |
iam.end_session() |
Ends the session and every subagent, and revokes all grants |
The app makes these calls for the agent and holds the tokens. The model sees each result, never a token.
Journey 1: building and publishing a template
- A builder describes the job ("prepare renewal briefs for an account manager") and asks the permission catalog what fits.
- They draft the template: the ceiling, the context and the limits. The catalog flags contract pricing as sensitive, outbound email as high risk, and billing admin as never available to agents.
- Security reviews it. Because every item comes from the catalog, the review is a diff: what does this version add, and how risky is it?
- The template is published as v3.
- An organization admin turns it on and may narrow it, for example by removing contract pricing or turning off bypass mode for their users.
Journey 2: a user's session, end to end
The user opens a three-pane app: sessions on the left, the chat in the middle, and the session's access on the right.
Step 1: the session starts with nothing. The user types the task and picks a mode from the message box. The app exchanges the user's token, its own credential and the template for a session token that carries no permissions at all.
Step 2: the agent asks. To find this quarter's renewals, the agent calls iam.request_access for three items: an API permission, a data permission and an asset permission, with a reason. IAM checks each item: it's inside the template's ceiling, the user has it right now, and it isn't on the never-delegable list. No saved rule covers these, and the mode is Ask, so the call comes back as a prompt in the chat.
Step 3: the user approves. They choose Always allow. IAM records three grants on the session and three rules, then issues a fresh token with exactly those permissions. The decision only counts because it arrives with the user's own token. An agent can't approve its own request.
Step 4: the agent works, and starts a subagent. It calls the accounts API with its own token. Then it starts an email drafter subagent, read-only. The drafter inherits the three grants as read-only, asks for its own "Email templates · Write", and gets it once. That grant stays with the drafter and doesn't flow up to the parent.
Step 5: a sensitive request, and a change of access. The drafter asks for contract pricing. Sensitive items prompt in every mode and can never be saved as Always allow, and the user denies it. That afternoon the user loses access to the pipeline dashboard. At the next token refresh, 15 minutes at most, the agent loses it too. Nobody had to touch the agent.
Step 6: the session ends. The agent calls iam.end_session, or the user clicks End session, or it expires. The session and its subagents are revoked. The saved rules stay.
Journey 3: the next day, and the audit
The user starts a new session, and it holds nothing. The agent asks for the same three items. Two match saved rules and are granted with no prompt. The dashboard has a saved rule too, but the user no longer has access to it, so it's refused. A rule never grants more than the user has.
Later, someone in compliance asks what the agent accessed for this user last week. The activity log answers per session: what was asked, who decided and how (prompt or rule), what was granted and what was called. When the template ships a version that widens its ceiling, users are asked again once.
Modes and rules, like your AI coding tool
If you've used an AI coding tool, you already know the shape of this.
- Session modes, picked when the session starts. Ask prompts for anything no rule covers. Auto also approves low-risk items, such as read-only API access, without a prompt. Bypass approves anything the user has except sensitive items, and an organization can turn it off.
- Consent rules, saved from a prompt: allow, deny or ask, per template and per permission.
The order matters. The template's ceiling, the user's current access and the never-delegable list always win. Then deny, ask and allow rules apply, and only then the mode. The mode is stored on the server's session record. If the agent could send it with the request, a prompt-injected agent would simply claim bypass.
Under the hood
The mechanics are mostly standard. The app does an OAuth 2.0 Token Exchange (RFC 8693) when the session starts, and the agent's identity rides in the act claim, with one more nested act for each subagent level. Tokens live 15 minutes at most and are re-issued after every grant. The user's current access is re-checked at every issue, so losing a role removes it from the agent within one refresh. Revoking a session bumps a version number that every token carries.
Each kind of permission is enforced where it already lives:
Two lessons that weren't obvious until I worked through them:
- Data restrictions come from the user, always. If an agent's data access were just the data permissions it asked for, it would skip the user's restrictions, such as masked fields or a block on certain sensitive records. The agent would see things the user can't. Grants come from the session, restrictions come from the user, and restrictions can't be requested at all.
- Object access needs a live dual check. API permissions get recomputed every time a token is issued. Object permissions are often stored as relationships written once, at grant time. The only thing that notices the user lost access to a dashboard is a check that the session has it and the user still has it.
What can go wrong
| Risk | Guardrail |
|---|---|
| Prompt injection makes the agent ask for broad access | Template ceiling, deny rules, the never-delegable list, the reason shown in every prompt, and a rate limit on requests |
| The agent approves its own request | Decisions only count with the user's own token |
| The agent claims a more permissive mode | The mode lives on the server |
| Escalation through admin tools | IAM admin rights are never delegable, so an agent can't raise its own user's access |
| Subagents combine into something no template allows | Review a template together with its child templates, and keep tool and outbound permissions in the ceiling |
| Data leaves through a tool IAM doesn't govern | Tool and outbound permissions are a kind of grant, enforced by a tool gateway |
| Background agents with nobody at the screen | Push approvals to the user's phone (CIBA) instead of letting saved rules quietly become standing access |
| Consent fatigue | Modes, saved rules, and templates whose context keeps requests small |
The part I'm still arguing about: whose name goes in sub?
The convention, in RFC 8693 and in what Okta and AWS ship, is sub = the user and act = the agent. It reads naturally: the agent acts on behalf of the user.
I lean the other way: sub = the agent session, with the user as on_behalf_of. The reason is failure mode. In a large system, some service somewhere will look up permissions by sub. With the user in sub, that service hands the agent the user's full access. With the session in sub, it finds nothing and fails closed.
The cost is that standard libraries and MCP clients expect the convention. If you follow it, you need confidence that every service checks the token's own permissions and never re-derives them from sub. I'd love to hear which way you went and why.
Closing
Agents will soon outnumber the people in most identity systems. Least privilege has always been more of an aspiration than a default, because people's tasks are hard to bound. Agents are different: a session gives us a natural boundary, and a chat gives us a natural place to ask. Let's use both. Start every agent at zero, let the user lend what the task needs, and take it back when the session ends.
Sources
- MCP authorization specification
- IETF: Transaction Tokens
- IETF: OAuth profile for delegated AI agent authorization
- OpenID Foundation: Identity Management for Agentic AI
- NIST NCCoE: Software and AI Agent Identity and Authorization
- OWASP Top 10 for Agentic Applications
- Microsoft Entra Agent ID: authorization
- Okta: AI agent token exchange
- Auth0 for AI Agents
- AWS Bedrock AgentCore: outbound auth
- RFC 8693: OAuth 2.0 Token Exchange











Top comments (4)
'Remember the consent, not the access' is the sharpest distinction in the whole piece — 'Always allow' saving a rule rather than preloading access keeps every session honest. The open question for me is subagent inheritance in practice: 'inherit down, never up' is clean on paper, but how do you audit a grant chain once the parent session is gone and only the subagent's calls remain in the log?
Thanks, that's the line I'd keep if I had to cut everything else.
Short answer: the chain shouldn't live in the session. It should travel with the token, and it should be written down when the grant is minted.
The weak spot in practice is downstream. Many APIs log only sub and drop the rest of the claims. If the service you're calling throws away act and the grant id, you're back to guessing. So the logging contract has to be part of the design, not an afterthought.
The token-carried chain plus the append-only grant record closes the audit gap cleanly — a single log line can explain itself without the session surviving. The downstream weak spot is the honest limit though: if the called API drops act and the grant id, the chain is only as good as the worst logger in the path. Is the fix a gateway that refuses to forward calls whose claims it can't preserve, or does that just move the trust problem one hop upstream?
Putting the session in sub instead of the user changes the failure mode in a useful way. A service that only looks at sub finding nothing and failing closed is a much safer default than handing over full access.