DEV Community

Cover image for Zero Standing Access for AI Agents: Let Users Lend Access, One Session at a Time
Akarshan Gandotra
Akarshan Gandotra

Posted on

Zero Standing Access for AI Agents: Let Users Lend Access, One Session at a Time

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 sub and the agent as act.
  • 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

  1. Zero standing access. An agent, and every new session of it, starts with nothing.
  2. 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.
  3. Only the user can consent. Approval needs the user's own credential. The agent can ask, but it can't approve.
  4. Session-scoped. Grants end when the session ends or is revoked.
  5. Inherit down, never up. A subagent gets at most its parent's access, and what a subagent is granted stays with it.
  6. Remember the consent, not the access. Saved rules let a user skip the prompt next time. Each session still starts at zero.
  7. 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.

Data model for agents in IAM: templates, agents, sessions, grants, requests, consent rules and the 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.

Wireframe: building an agent template, with IAM's permission catalog in the right pane

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.

How a template gets built, reviewed, published and turned on

Journey 1: building and publishing a template

  1. A builder describes the job ("prepare renewal briefs for an account manager") and asks the permission catalog what fits.
  2. 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.
  3. 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?
  4. The template is published as v3.
  5. 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.

Wireframe 1: starting a session, with the permission mode menu open

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.

Wireframe 2: the agent's request becomes a permission 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.

Wireframe 3: tool calls in the chat, and grants and the subagent in the right pane

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.

Wireframe 4: a sensitive request that only offers Allow once, and access removed after the user lost it

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.

A session from start to finish

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.

Wireframe 5: saved rules skip the prompt, and the rule for lost access is refused

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.

How each requested item is decided

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:

Where each kind of permission is enforced

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

Top comments (4)

Collapse
 
kami_butt_69b922ade8de012 profile image
Agentic Affairs •

'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?

Collapse
 
akarshan profile image
Akarshan Gandotra •

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.

  1. Every token names its whole chain. A subagent's token carries a nested actor claim (RFC 8693's act): subagent → parent agent → user, plus the id of the grant it was minted from. Any single call in the log explains itself, even if nothing else survives.
  2. Grants are records, not just tokens. At mint time, the IAM writes an append-only grant record: who consented, which rule or prompt approved it, the scope, the session and a parent_grant_id. Ending the session revokes the access but doesn't delete the history. Following parent_grant_id back gets you to the user's original consent.
  3. "Never up" can be checked afterwards. Because both the parent's and the child's scopes are recorded, an auditor can confirm the child's scope stayed within the parent's without taking the agent runtime's word for it.

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.

Collapse
 
kami_butt_69b922ade8de012 profile image
Agentic Affairs •

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?

Collapse
 
robertpaschal profile image
Odinaka Robert Nnamani •

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.