Access control for agents defines which users, agents, tools, data sources, and actions are allowed in a given context. It has to apply before the agent sees data and before it takes action, which is what makes it different from the access control an application already has. A traditional endpoint checks one permission for one operation. An agent decides at runtime which data to read and which operations to call, so the check belongs at every one of those points rather than at the front door.
The idea that organizes the rest of this page: an agent acting on a user’s behalf needs that user’s permissions, not its own. Most agent access control incidents come from ignoring that sentence. The agent is given a service credential that can read everything the product can read, every user’s request flows through that one credential, and the only thing separating one customer’s data from another is the model’s willingness to stay on task.
Key takeaways
- An agent should carry the requesting user’s authority, not a superset of every user’s authority. Delegated identity is the default; a broad service account is the failure.
- An agent holding more authority than the person asking can be steered into using it. The request looks legitimate to every downstream system, because the credential is legitimate.
- Filter retrieval before the query runs. Content that reaches the context window has effectively been disclosed, whatever the response says.
- Enforce per action, not per session, and test with two real accounts. Correctness is a claim about what the second user cannot reach.
Three identities and why it matters which one acts
Every agent action happens on behalf of someone. There are usually three candidates for whose authority is in play:
- The end user. The person or system that made the request.
- The agent’s own identity. A service account or workload identity representing the software.
- The credential the tool actually uses. Frequently a shared API key with wide scope, chosen during a prototype and never revisited.
The safe default is delegation: the agent obtains a token scoped to the requesting user and every downstream call is authorized as that user. Existing permission checks in the database, the document store, and the internal APIs keep working without change, and a request that should return nothing returns nothing.
The common alternative is the broad service account, and it fails in a specific way. The agent holds more authority than the person talking to it, so the boundary between users now depends entirely on the agent behaving correctly. Anything influencing that behavior can spend the authority: an ambiguous request, a retrieved document containing instructions, a tool result with an injected line, or a bug in a filter. Downstream systems see a legitimate credential doing a permitted thing, so nothing alerts. Text that steers a privileged component into acting for someone else is one of the failure patterns that shows up repeatedly in production agents, and it is invisible at the point of use.
Agents that run without a user, such as a scheduled job, do need their own identity. Give it the narrowest scope the task requires and keep it separate from the account the interactive path uses.
Where the checks go
Retrieval. Entitlements belong in the query, not in a pass over the results. The index carries access metadata alongside each chunk and the filter applies at search time. Two details cause most of the bugs: chunks inherit permissions from their source document and need reindexing when those change, and a document restricted after indexing stays retrievable until something reconciles the index.
Tool calls. Every call carries the caller’s identity, and the system on the other side does its own check. An allowlist of tools the agent may use is a useful constraint but not an authorization decision, since the same tool is fine for one user and wrong for another. Watch for the specific mistake of a tool whose user_id or account_id argument is supplied by the model. A parameter the model controls is not a boundary. Identity travels in the call context, not in the arguments.
Memory and state. Anything the agent remembers across turns or sessions needs the same scoping as the primary data. Shared caches and cross-session memory are a quiet path for one user’s content to appear in another user’s answer.
Output. Redaction on the way out is a backstop, not a control to rely on. By then the sensitive text has been in the prompt, in the trace, and in whatever the model chose to say about it. Looking at how context and tool results are assembled for the model makes it obvious why the earlier checks carry the weight.
How access control for agents fails
Permissions checked once at session start. A long-running agent outlives the access it was granted. Revocation has to take effect on the next action, which means checking per call rather than caching a decision for the session.
Sub-agents inheriting the orchestrator’s authority. In a multi-agent system, a delegated task often runs with whatever credential the caller had. Scope should narrow as work is delegated, never widen. Pass the user’s token down, not the orchestrator’s.
Graceful degradation nobody wrote. When a filter correctly returns nothing, the agent needs to say it cannot access that rather than fill the gap with a plausible guess.
Tests that only cover one user. Correctness here is a claim about what a second user cannot reach, so it needs cases written to try: asking for another account directly, asking indirectly, planting an instruction in a retrieved document, setting an identity argument by hand, and repeating a request after access was revoked. These belong in the automated set, since they are exactly the cases conventional test suites are not written to catch and the same request can succeed once and be blocked the next time.
FAQ
Should an agent have its own permissions or the user’s?
The user’s, whenever a user initiated the work. Obtain a token scoped to that user and let existing downstream checks apply. Reserve a dedicated agent identity for work with no requesting user, scoped to what that task needs. An identity holding the union of every user’s access is the arrangement that turns one prompt injection into a cross-account disclosure.
How do I do access control for retrieval and RAG?
Store permission metadata with each chunk at index time and apply it as a filter inside the search request. Keep that metadata in sync with the source system, including deletions and permission changes, and treat the index as sensitive data in its own right. If your vector store cannot filter on an entitlement attribute, partition by tenant or user rather than filtering results afterward.
Why is a system prompt not enough to keep an agent out of the wrong data?
Because an instruction is a preference the model usually honors, and the credential is a capability it always has. Any input that reaches the model competes with your instruction, including text the agent retrieved or received from a tool. Enforcement has to live where the data is fetched or the action is taken, in code that does not read prompts.
How does this work in a multi-agent system?
Treat every agent as a principal and every delegation as a scope reduction. The task passed to a sub-agent carries the user’s identity and a narrower set of tools than the parent had. Record which agent took which action under which identity, because a multi-agent run without that record cannot be reconstructed after an incident.