Authorization Patterns for Agentic Workflows: Delegation, Constraints, and Just-in-Time Permissions
Updated 2026-05-15 · 11 min read · By @guptadeepak
Key takeaways
- Agent authorization is RBAC plus delegation plus attenuation. Off-the-shelf RBAC alone underspecifies agent scenarios.
- Just-in-time permission elevation — the agent asks for a specific permission for a specific action, gets it for that call — is the production-quality pattern in 2026.
- ReBAC (Zanzibar-style) is the cleanest authorization model for delegated agent workflows because the parent-of and acts-for relationships map directly.
- Capability-based tokens (Biscuit, macaroons) let an agent's permissions narrow downstream without contacting the issuer; the right pick when delegation chains are deep.
- Human-in-the-loop for high-impact actions is not a workaround, it is the authorization model. The pattern is approval requests, not pre-authorization.
Why agent authorization is its own problem
The mental model: there are four authorization questions for any agent action.
- Standing permissions: what is this agent role allowed to do at all? (RBAC)
- Delegation: which user authorized this agent to act for them? (ReBAC:
acts_for(agent, user)) - Context: is this specific action within the scope of what was delegated? (ReBAC:
referenced_in(resource, conversation)) - Approval: does this action need human confirmation before execution? (Policy: thresholds, action class, sensitivity)
Each question has a different answer and a different mechanism. Conflating them — using RBAC alone, for instance — collapses all four into "is this role allowed to do this action," which loses delegation, context, and approval.
Standing permissions: RBAC for the agent itself
The first authorization question is the easiest: what is this agent's role allowed to do, intrinsically?
- A calendar agent has scopes
calendar:read,calendar:write. - A research agent has scopes
documents:read,web:search. - A finance agent has scopes
transactions:read,reports:generate.
This is plain RBAC, the same model that works for human roles. Production CIAM products handle this without modification: assign the agent to a role, the role has permissions, the OAuth scopes reflect the role.
Standing permissions are necessary but not sufficient. The role determines what the agent can do; it doesn't determine what the agent should do in a specific context.
Delegation: ReBAC for "acts for"
The second authorization question is about user delegation: when the agent calls an API, on which user's behalf?
The clean model is ReBAC with an acts_for relationship:
user:alice --acts_for_relation-> agent:calendar_assistant
A query like "does agent:calendar_assistant have access to calendar:work" resolves through the relationship graph: yes, if acts_for(calendar_assistant, alice) AND owner(calendar:work, alice).
The benefit over flattening delegation into roles: the agent's permissions inherit from the specific user it is acting for, dynamically. Alice's calendar agent has access to Alice's calendars; Bob's same-named-type agent has access to Bob's. No proliferation of per-user roles, no static permission baking.
OpenFGA, SpiceDB, Keto, and Permify (the open-source Zanzibar implementations) all handle this pattern. Authress, Ory Keto, and Auth0 FGA ship it commercially. The detail on the model is in Google Zanzibar Explained and RBAC vs ABAC vs ReBAC.
Context: ReBAC for "referenced in this conversation"
The third question is harder: the agent has standing permission, and it is acting for an authorized user, but is the specific action within the scope of what was delegated?
A research agent acting for Alice has documents:read and acts for Alice. Alice has access to thousands of documents. The agent is currently summarizing the document Alice attached to the conversation. The question: is the agent allowed to read other documents Alice has access to, even though only one was attached?
The conservative answer is no — bound the agent to the resources referenced in the active context. The ReBAC representation:
document:proposal_v3 --referenced_in-> conversation:current_session
The agent can read a document if acts_for(agent, user) AND owner(document, user) AND referenced_in(document, conversation). The agent cannot wander Alice's document store; it can only act on what is in scope.
This pattern is what saves you from "the agent decided to also read these other related documents" surprises. Make context-membership an explicit relationship; check it in the authorization decision.
Attenuation: capability tokens for chains
The fourth question shows up when agents delegate to sub-agents or tools: how does each step in the chain receive a narrower set of permissions than the step that called it?
The mechanisms:
OAuth token exchange + scope reduction. The agent has a token scoped [A, B, C]. To call a sub-agent, it requests a token scoped [A] only via RFC 8693 token exchange. The sub-agent gets A; theft from the sub-agent does not expose B or C. Works well for chains of 2-3 steps; gets chatty for deeper chains because every step requires an issuer roundtrip.
Capability tokens with attenuation (Biscuit, macaroons). The agent holds a token that authorizes a set of actions. It derives a new token from the parent, adding constraints, without contacting the issuer. The new token is independently verifiable, carries the agent's authentication, and has the narrower scope. The chain can go arbitrarily deep without the issuer being involved at each step.
The Biscuit specification is the modern entrant; macaroons are the older Google design. Both are appropriate when delegation chains are deep and the issuer-roundtrip cost of OAuth token exchange would be prohibitive. PASETO and JWT do not support attenuation natively.
The decision: if your agent architecture has 1-3 levels of delegation, OAuth token exchange is enough. If it has 5+ levels (an agent that delegates to sub-agents that delegate to tools that delegate to micro-tools), capability tokens are worth the implementation cost.
Just-in-time permission elevation
The pattern that bounds blast radius best: the agent does not hold broad standing permissions; instead, it requests narrow permissions per action.
The flow:
- Agent decides to take action X on resource Y.
- Agent requests permission
X:Yfrom the auth server via a JIT-elevation API. - Auth server evaluates against policy:
- Grant: issue a narrow short-lived token (audience: the API; scope:
X:Y; lifetime: seconds to a few minutes). - Deny: return a denial reason. Agent backs off or asks for human help.
- Approval required: queue an approval request; pause the agent.
- Grant: issue a narrow short-lived token (audience: the API; scope:
- Agent uses the narrow token for the single call.
- Token expires.
Properties:
- Standing token has minimal scope (audit, introspection only).
- Each impactful action has its own token, scoped precisely to that action.
- Compromise of the standing token is low impact; compromise during a specific JIT window is bounded to that action's scope.
- Audit log shows every elevation request, granted or not — the agent's intent is visible even when actions don't happen.
The friction: a JIT roundtrip per action adds latency. For a chatty agent (50 tool calls per task), this is meaningful. Mitigations: batch elevations for related actions, cache elevations for the duration of the conversation but never across users, use long-lived sender-constrained tokens for the standing surface that doesn't change.
JIT elevation requires an auth server that exposes the elevation API and a policy engine that can evaluate per-call. Open Policy Agent (OPA), Cedar (AWS), and Permit.io support this; commercial CIAM is starting to ship the pattern (Authress, Auth0 FGA via custom policies).
Human-in-the-loop approval
The fourth question: which actions require explicit human confirmation before execution?
The answer depends on impact and reversibility:
- Financial transactions above a threshold: human approval required.
- Sending external communications (email to non-customers, social posts, public announcements): human approval, usually.
- Changing security-critical configuration (revoking access, modifying IAM, rotating credentials): human approval.
- Deleting data: human approval, with confirmation that the user understands what is being deleted.
- Aggregating data across users for export: human approval if the result is going outside the company.
The pattern, mechanically:
- Agent prepares the action: a typed object describing what will happen.
- System inserts the action into a "pending approvals" queue with a short TTL.
- Approval UI surfaces the action to the right human (the delegating user, an admin, an on-call security person — depends on the action class).
- Human approves or rejects with optional rationale.
- System executes (or discards) the action.
The system must enforce that an action with requires_approval: true cannot be executed by any code path that bypasses the queue. The architectural lever: every impactful API takes a typed action object; the dispatcher checks approval status; calls that bypass the dispatcher are blocked at the policy layer.
Approval is not a backstop for missing authorization — it is part of the authorization model. The set of actions that require approval is part of the policy, not the agent's logic. Agents should be unable to opt out of approval; the auth server enforces.
Audit and explainability
Every authorization decision should produce an audit entry containing:
- Principal: the agent, the user it acts for, the parent chain.
- Action: what was requested.
- Decision: grant / deny / approval_required.
- Reason: the policy rule or relationship that produced the decision.
- Token: the audience-restricted token issued (if granted), with thumbprint.
Explainability matters more for agent decisions than for human ones because the principal is non-human and the action chains are deeper. When something goes wrong, the audit trail must support "who authorized what, on whose behalf, for what reason."
Implementation guidance
- Layer the model: RBAC for standing permissions, ReBAC for delegation and context, capabilities for chains, JIT for blast radius, human approval for high impact. Do not pick one and stretch it.
- Start with ReBAC if you are building agents from scratch. The delegation and context graphs are the differentiator vs human users.
- JIT permission elevation for impactful actions. The latency cost is worth the blast-radius reduction.
- Define the human-approval policy up front, in the same document as RBAC. Retrofit is painful.
- Make the policy live outside the agent code. OPA, Cedar, the auth server's policy engine — the agent should not be the place that decides what is allowed.
- Audit every decision, granted or not. Intent and outcome.
- For sub-agent chains, prefer capability tokens with explicit attenuation. OAuth token exchange works for shallow chains; capabilities scale to deep ones.
- Test authorization at every layer. Standing permissions, delegation, context, attenuation, approval bypass attempts. The bugs accumulate in the gaps between layers.
Related vendors
Auth0
Auth0 remains the safest mid-market default for B2C plus B2B Enterprise SSO when developer velocity matters more than long-run TCO. Below 50k MAU it is hard to beat. Above 500k MAU, cost and Actions-driven lock-in make alternatives like FusionAuth (self-host), Cognito (AWS-native), or Stytch plus Corbado (passkey-first) increasingly attractive.
Authress
Authress is the authorization-first developer CIAM in 2026, native ReBAC and Zanzibar-style FGA at a price point materially below Auth0 FGA or WorkOS FGA. For B2B SaaS designing fine-grained per-resource permissions where authorization is the binding constraint rather than authentication, Authress removes the two-vendor split (full CIAM plus separate authz service) most teams end up running. For teams whose binding constraint is auth methods or B2C scale, look elsewhere.
Ory
Ory is the most architecturally modern open-source CIAM in 2026, Go-based, Kubernetes-native, composable components, strict Apache 2.0, with native Zanzibar-style FGA via Keto that no other full-platform vendor in this index ships natively. The trade-off is operational scope: running four composable services rather than one binary suits Kubernetes-native teams and frustrates everyone else. For teams that want OSS plus FGA from one vendor, Ory is the singular pick.
WorkOS
WorkOS is the strongest B2B-first CIAM in 2026 by deliberate scope choice, every product surface assumes the buyer is selling to enterprise IT, not to consumers. AuthKit's 1M MAU free tier makes it a credible Auth0 alternative for B2B SaaS that doesn't need adaptive risk or B2C consumer flows. For pure B2B SSO, SCIM, and audit logs, WorkOS is hard to beat at any price point.
FAQ
- Can I just use RBAC for AI agents?
- RBAC handles the static permissions of an agent (this agent can read documents). It does not handle the delegation question (this agent can read documents that user X has access to) or attenuation (this agent can read only the specific document referenced in this conversation). RBAC plus ReBAC (for delegation) plus capabilities (for attenuation) is the production combination. Off-the-shelf RBAC alone underspecifies almost every interesting agent scenario.
- What is just-in-time permission elevation?
- Instead of pre-authorizing the agent for everything it might need, the agent asks for a specific permission for a specific action at the moment it needs it. The auth server evaluates the request against policy and either grants a narrow short-lived token, denies, or escalates to human approval. The agent does not hold broad standing permissions; it gets exactly what each step requires. Reduces blast radius materially for both bugs and compromises.
- When should I require human-in-the-loop approval?
- When the action is high-impact (financial transactions above a threshold, sending external communications, changing security-critical configuration, deleting data) and reversibility is low. The pattern: the agent prepares the action, the system holds it in a pending state, a human approves or rejects via a UI, the system executes only on approval. Make it part of the authorization model from the start, not an afterthought, because retrofit costs much more than initial design.
- What's ReBAC and why does it fit agents?
- ReBAC (Relationship-Based Access Control, the Zanzibar model) defines authorization as graph queries: 'does user U have relationship R to resource X?' For agents, the relationships acts_for(agent, user) and parent_of(action, conversation) map directly. An agent can read a document if user(agent) has access to document AND the document is referenced in the current conversation. The relationship graph captures both delegation and context, which RBAC alone cannot.
- What are capability-based tokens and when do they help?
- A capability token bundles authorization with the token itself: holding the token IS the proof of authorization. Biscuit and macaroons add attenuation — the holder can derive a more-restrictive token without contacting the issuer. For deep agent delegation chains (agent → sub-agent → tool → API), capabilities let each step pass a narrower-scoped token to the next without issuer roundtrips. The right fit when chain depth is high and centralized auth would be the bottleneck.
Sources
- Google Zanzibar paper (Pang et al., 2019)
- Biscuit specification (biscuitsec.org)
- Macaroons paper (Birgisson et al., 2014)
- OWASP Authorization Cheat Sheet