Classic painting used as the article cover
← Back to blog

Authorization

Agent Scope ∩ User Permission: The Intersection Rule

An agent's effective capability is the overlap of what the agent may do and what the acting user may do. Widening one side alone has to change nothing, and three cloud platforms already say so.

•Oct 12, 2026•15 min
DelegationIdentitySecurity

TL;DR

An agent's effective capability is the intersection of what the agent is allowed and what the acting user is allowed. Widening one side alone must change nothing.

This is not a novel idea, it is how temporary credentials have worked for years. AWS states it flatly for session policies: "The resulting session's permissions are the intersection of the role's identity-based policy and the session policies", and "You cannot use session policies to grant more permissions than those allowed by the identity-based policy of the role that is being assumed."

The common agent architecture breaks the rule by accident. One service account holds the integration credentials, every user's request flows through it, and the agent's reach becomes the union of everybody's access rather than the overlap with anyone's.

We are not claiming intersection is a complete authorization story. It says nothing about whether an action is reversible, or whether a human should see it first. It is the floor, not the ceiling.

The test is cheap and worth running this week: grant the agent a scope the user does not have and confirm the call still fails. Then grant the user a scope the agent does not have and confirm the same. If either direction leaks, you are computing a union.

Overview

Take a sales operations agent at a 2,000-person software company. It reads opportunity records, drafts follow-up email from each rep's own mailbox, and updates close dates. Reps see their own territory, managers see their team, and the VP sees all of it. The agent is one deployment with one set of tools.

Now ask the only question that matters about its permissions: when a rep asks the agent to summarise pipeline, whose view of the pipeline does it read? If the answer is "the integration's view", the rep has just been handed the VP's data through a chat box, and no one will notice until an org chart question gets answered too accurately.

Which permissions an agent should hold is covered in agent permissions best practices and permission models for autonomous systems, which separates reach from initiative. This post is narrower and more mechanical. It is about one rule, how three major platforms already encode it, what goes wrong when a system computes a union instead, and where to put the check so it cannot be skipped.

The invariant: effective capability equals agent capability intersected with acting-user capability, and no change to one input alone can grow the result. A system that cannot demonstrate that property is not enforcing an intersection, whatever its documentation says.

The Rule, Stated Precisely

Write the two inputs as sets of allowed actions, one per principal, and the rule is a one-liner: effective = agent ∩ user. The part people skip is the second half, which is where the testable property lives.

Monotonicity in one variable buys you nothing. If effective can grow when you widen only the agent's grant, then the agent is a privilege escalation route for every user attached to it. If effective can grow when you widen only the user's grant, then your users can reach tools the agent was never approved to use, which is the same failure with the blame reversed.

So the test has two directions and both have to fail closed:

  1. Grant the agent calendar.write while the acting user has no calendar access. The call must be denied.
  2. Grant the user finance.export while the agent has no finance tool. The call must be denied, and ideally the tool should not appear in the agent's list at all.

OAuth's own framing already pushes in this direction. OAuth 2.1 section 1.4.1 says "Access tokens are intended to be issued to clients with less privileges than the user granting the access has. This is known as a limited 'scope' access token." Less, not equal. The spec treats the client's authority as a subset of the user's by design, and an agent is just a client that asks for a lot of scopes at once. Why scopes alone do not get you there is the subject of why OAuth alone isn't enough for AI agents.

yes

no

no

yes

no

yes

Tool call

explicit deny
on either side?

Deny

agent grant
allows action?

acting user
allows action?

Allow

Record the denial
with both principals

Record the call
with both principals

Figure 1 — The two-sided check, with deny evaluated first. Three of the four branches end in a denial, which is the correct shape: an intersection is mostly a machine for saying no.

Deny goes first for a reason the cloud platforms learned the hard way, and it is the subject of the next section.

Where This Rule Already Exists

The agent world keeps reinventing this. It has been shipping in cloud IAM for years, and reading the existing wording is faster than arguing about it.

AWS session policies. The IAM documentation: "The permissions for a session are the intersection of the identity-based policies for the IAM entity (user or role) used to create the session and the session policies." It then adds the constraint that makes the property hold, twice over. Session policies "limit permissions for a created session, but do not grant permissions", and in the AssumeRole API reference: "You cannot use session policies to grant more permissions than those allowed by the identity-based policy of the role that is being assumed." Permissions boundaries work the same way one level up: with a boundary set, "the entity can perform only the actions that are allowed by both its identity-based policies and its permissions boundaries."

That is the whole design pattern. One side describes the ceiling, the other describes the request, and widening the request cannot raise the ceiling. For an agent, the agent's own grant is the ceiling and the acting user's permissions are the request, or the reverse, and it does not matter which way you name them because intersection is commutative.

Google Cloud deny policies. Google adds the ordering rule. Its documentation states that "IAM always checks relevant deny policies before checking relevant allow policies", and the consequence: "When a principal is denied a permission, they can't do anything that requires that permission, regardless of the IAM roles they've been granted." AWS says the same thing in different words, that "an explicit deny in any of these policies overrides the allow."

This matters for agents more than it looks. An intersection built only from allow sets is fragile, because the two sides are usually maintained by different teams on different clocks. A deny evaluated first is the mechanism that lets a security team remove capability from an agent without auditing every user's grant.

Entra's delegated permissions. Microsoft's on-behalf-of documentation states the intersection as a product behaviour rather than a policy rule. The flow "only uses delegated scopes and not application roles", because roles "remain attached to the principal (the user) and never to the application operating on the user's behalf. This occurs to prevent the user gaining permission to resources they shouldn't have access to." Read the last clause again. The reason application roles are excluded from the OBO flow is precisely that they would let the middle tier contribute authority the user does not have. How the two identities ride on one request is covered in what on-behalf-of authorization actually proves.

If you are designing this from scratch, you are late. Three of the largest access control systems in production converged on the same two rules: intersect the two sides, and evaluate denials before allows. Copy them rather than inventing a third vocabulary.

What a Union Looks Like From the Inside

The failure is rarely a bug in an authorization check. It is an architecture that never had two sides to intersect.

A single service account holding the Salesforce credential is the canonical version. The integration authenticates once, as itself, with whatever scopes someone requested during setup, which is usually everything the vendor's consent screen offered. Every user request then runs through that one identity. The authorization check inside the agent, if there is one, compares the request against the agent's configuration. Nothing in the path has the acting user's permissions available, so nothing can narrow anything. The agent's reach is the union of every user's access, and often more, because service accounts tend to be granted admin scopes to make setup work.

Kubernetes RBAC is a useful contrast because it is explicit about being a union: "Permissions are purely additive (there are no 'deny' rules)." That is a reasonable choice for a cluster where every subject's grants are written by the same platform team. It is a bad fit for an agent sitting between thousands of users and a SaaS API, because the second principal never appears in the model.

CRM APIShared service accountAgentRep (territory West)CRM APIShared service accountAgentRep (territory West)acting user's territory never consulted"summarise pipeline this quarter"check: agent allowed to read opportunitiesuse integration credentialGET /opportunitiesall 14,000 recordsall 14,000 recordssummary of the whole company

Figure 2 — The union failure. There is no denied request to find afterwards, because the only check that ran passed. This is why reviewing denial logs will never surface it.

What makes this one hard to catch is that it produces no error. Every call returns 200. The audit log shows a successful read by a legitimate integration. The only evidence is in the response body, and nobody diffs response bodies against an entitlement model. We think this is the single most common serious permission defect in deployed agents, and it is structural rather than careless: the architecture that makes a connector easy to ship is the same architecture that erases the second principal.

The Confused Deputy Shape

Intersection is also the structural answer to a named attack class. A confused deputy is a component with more authority than its caller that can be induced to use that authority on the caller's behalf. An agent holding broad integration credentials is exactly that component, and the mechanism is the same whether the inducement arrives as a crafted prompt or a crafted OAuth request.

MCP's security guidance documents the OAuth-flavoured version in detail. The attack targets an MCP proxy server that "connects MCP clients to third-party APIs, offering MCP features while delegating operations and acting as a single OAuth client to the third-party API server." The vulnerable combination is a static client ID at the third-party authorization server, dynamic client registration for MCP clients, a consent cookie set after the first authorization, and a proxy that "does not implement proper per-client consent before forwarding to third-party authorization". The attacker registers a client with their own redirect URI, sends the user a link, the third-party server sees the consent cookie and skips the consent screen, and the authorization code lands on the attacker's host.

The required mitigation is stated as an obligation on the proxy: "MCP proxy servers using static client IDs MUST obtain user consent for each dynamically registered client before forwarding to third-party authorization servers". Per-client consent is intersection expressed as a user interface. The proxy's own authority is not sufficient; it has to establish the caller's authority separately, for that caller, before spending its own.

The prompt-injection version has the same shape without the OAuth machinery, and hallucinated permissions covers what happens when the agent reasons about what it is allowed to do rather than being told. The defence is identical in both cases. If the effective capability of a call is bounded by the acting user, then an attacker who can steer the agent still only reaches what that user could have reached directly, and the deputy has nothing extra to be confused out of.

Getting the Intersection Right

Three practical points, because the rule is easy to state and easy to implement wrongly.

Put the check where the credential is attached, not where the tool is chosen. A model that decides which tool to call is choosing from a list, and a list is advice. The decision has to be re-made at the moment the outbound request is built, by code the model cannot address. MCP's 2026-07-28 specification makes room for this on the discovery side. A server's tool set "MAY vary by the authorization presented on the request", for example by "returning only the tools the caller's granted scopes permit", because "credentials are per-request input, not connection state." A narrowed tools/list is good design because it keeps the model from proposing calls that will fail. It is not the enforcement point.

Decide what a scope you do not recognise means. Two sides maintained separately will drift: an identity provider adds a group, a vendor renames a scope, a tool gains an argument that crosses a boundary the mapping did not anticipate. An intersection computed over sets of strings has to answer what happens when a string appears on one side and not the other, and the only safe answer is that an unmatched grant contributes nothing. That is the failure behavior dimension, and it is the subject of fail-closed agent authorization.

Log both principals on both outcomes. An intersection produces a new category of denial that neither side's own logs explain: the agent was allowed, the user was allowed, and the call still failed because the overlap was empty for that specific action. If the log line says "access denied" with one subject, whoever investigates will go and widen the wrong grant. Recording the agent, the acting user, the action and which side was missing turns a support ticket into a one-line answer.

This is the authorization model dimension of the Agent Access Control Taxonomy. Its term aact:authorization-model/intersection-of-agent-and-user is defined as a model where "an action is allowed only if both the agent's own policy and the delegating user's permissions allow it", with the note that it "prevents an agent from becoming a privilege-escalation path for a low-privilege user, and vice versa". The two terms it is worth contrasting with are aact:authorization-model/scope-at-grant, where "permissions are fixed when a credential is issued; individual actions within that scope are not checked again", and aact:authorization-model/role-based, where "the agent is assigned roles and actions are permitted if any role allows them". That last phrase, any role allows them, is the union, named.

Where Fabriq Fits

Agentic Fabriq computes an agent's effective tool list as the intersection of the user's scopes and the agent's, with default-deny RBAC across organization, team and member, scope sets and per-user overrides. Every request carries two identities, the agent and the acting user, and action-level permissions are enforced in the backend rather than in the tool description the model reads. Credentials are held per user in a vault and attached server-side at call time, which is what makes the second principal available to the check in the first place. We do not have a general-purpose rules language, and the intersection is not one; it is a structural property of how the request is built.

Frequently Asked Questions

What is the intersection of agent scope and user permission? The set of actions allowed by both the agent's own grant and the permissions of the human the agent is acting for. A call is permitted only if it is in both sets, which means widening either side on its own cannot add a capability.

Why should an agent's permissions be the intersection rather than the union? Because a union makes the agent a privilege escalation path. If any attached user's grants can contribute capability, the agent's reach is the widest user's reach, and a low-privilege user gets a high-privilege result through a chat interface. AWS encodes the opposite rule for session policies, which "do not grant permissions" and cannot exceed the role's identity-based policy.

Does AWS actually use the word intersection? Yes, in two places. The IAM user guide says "The permissions for a session are the intersection of the identity-based policies for the IAM entity (user or role) used to create the session and the session policies", and the AssumeRole API reference repeats it for the Policy and PolicyArns parameters.

How do deny rules fit into an intersection? They are evaluated first. Google's documentation states that "IAM always checks relevant deny policies before checking relevant allow policies", and that a denied principal "can't do anything that requires that permission, regardless of the IAM roles they've been granted". Practically, it lets one team subtract capability from an agent without touching anyone's grants.

Is Kubernetes RBAC an intersection model? No. Its documentation is explicit: "Permissions are purely additive (there are no 'deny' rules)." That is a reasonable design for a cluster with one administrative team and a bad fit for an agent mediating between many users and an external API.

How do I test whether my agent enforces the intersection? Two calls. Give the agent a capability the acting user lacks and confirm denial; give the user a capability the agent lacks and confirm denial. Run both, because systems commonly enforce one direction and not the other, and the direction they miss is usually the user side.

Does narrowing the tool list enforce the intersection? No, it improves the experience. A model chooses from a list, and the list is an input to a probabilistic process. The intersection has to be re-checked where the outbound request is assembled, in code the model cannot reach.

Conclusion

The reason to insist on this rule rather than a more sophisticated one is that it is checkable. "Least privilege" is a value. "No change to one input alone can grow the effective set" is a property you can write two tests for, and the tests do not need a threat model to justify them.

It also fails in a useful direction. An intersection that is wrong is usually wrong by denying something it should have allowed, which produces a complaint, a log line and a fix. A union that is wrong produces a correct-looking answer containing data the asker should never have seen, and produces it quietly for as long as nobody checks.

The platforms have done the specification work. AWS gives you the wording and the constraint that makes it hold, Google gives you the evaluation order, OAuth 2.1 gives you the principle that a client's authority is a subset of the user's, and MCP gives you the attack that happens when a mediating component spends its own authority on someone else's request. None of that is agent-specific. The agents just make it urgent, because they generate the calls faster than anyone reviews them.

Two tests, one afternoon. Widen the agent, confirm denial. Widen the user, confirm denial. Everything above is explanation for why those two results matter more than any diagram of your authorization layer.

Sources

All URLs read 2026-10-12.