← Security research

Access Control in AI Agent Workflows: The SaaS Security Gap Behind Prompt Injection

Most teams shipping AI features right now are testing the wrong thing.

AI agent access control: many users flow through one agent identity to every tenant

They run their chatbot through a list of jailbreak prompts, confirm it refuses to write malware or insult a customer, and call the AI feature secure. That work matters, but it answers a question about what the model says. The question that actually decides whether you leak customer data is what the agent is allowed to do.

Prompt injection is real. It is also the entry point, not the impact. An injected instruction only becomes a security incident when the agent on the other side of it can reach data or actions the user never should have touched. That reach is an access control problem, and access control is the oldest, most reliable source of high severity findings in SaaS. AI agents did not remove it. They moved it somewhere most teams are not looking.

This article is about that gap: where AI agent access control breaks, why your existing API security controls do not automatically cover it, and how to test it by hand instead of trusting that the model will behave.

Why AI agents break the access control model

A normal SaaS request has a clean identity story. A user authenticates, every request carries their session or token, and your authorization layer checks that identity against the object being requested. When it works, user A asking for user B’s record gets a 403. When it fails, you have broken object level authorization, which is BOLA, the single most common serious API finding in modern SaaS.

An AI agent breaks that story in three specific ways.

First, the agent sits between the user and your backend. The user talks to the agent in natural language. The agent decides which tools or functions to call and with what parameters. By the time a real API call happens, it is the agent making it, not the user. If the user’s identity is not carried all the way through to that call, the authorization check at the end has nothing real to check against.

Second, agents usually run with their own identity. To be useful, an agent often holds a service account, an API key, or a broad token so it can read documents, query databases, call internal services, and take actions. That identity is almost always more privileged than any single user. The moment the agent acts on a user’s request using its own privileged identity without re-checking what that specific user is allowed to do, you have a confused deputy. The agent is the deputy, and it has been confused into using its power on behalf of someone who lacks it.

Third, the authorization logic that protects your UI often lives in the UI. The frontend hides the admin button, omits the other tenant’s records, greys out the action the user cannot take. Agents do not use your UI. They call the functions underneath it. Every control that was enforced by what the interface chose to render is simply gone.

None of this is exotic. It is the same class of bug you find in any API that trusts the client, except the client is now a language model that a user can talk into making requests on their behalf.

Failure class 1: Identity collapse and the confused deputy

This is the core one, and almost every other AI access control bug is a variation of it.

Identity collapse is what happens when many users’ requests all flow through one agent identity. The agent holds a token that can read any customer’s records. User A asks a question that requires reading a record. The agent reads it with its own token. The token works, so the data comes back. The agent never asked whether user A, specifically, was allowed to see that record.

In classic API terms, you have just reproduced BOLA and its sibling BFLA, broken function level authorization, at the tool layer. The difference is that the vulnerable call is not something the user crafted in Burp. It is something the agent was persuaded to do through conversation.

Here is what it looks like in practice. Say an agent exposes a tool that fetches account records, and the tool is implemented as a call to an internal endpoint:

POST /internal/agent/tools/getRecord HTTP/1.1
Host: api.target.com
Authorization: Bearer AGENT_SERVICE_TOKEN
Content-Type: application/json

{
  "record_id": "acct_8842_rec_001",
  "on_behalf_of": "user_1021"
}

The agent token authorizes the call. The question is whether anything checks that user_1021 actually owns acct_8842_rec_001 before the record is returned. If the ownership check lives in the user facing API but not in this internal tool endpoint, the agent is now a clean bypass around your entire authorization layer. A user who convinces the agent to fetch a record ID belonging to another tenant gets it back, and your logs show the agent did it, not the attacker.

The reason this slips through review is that each piece looks correct in isolation. The agent token is valid. The internal endpoint is internal. The user facing API does enforce ownership. The gap only appears when you trace a single user request all the way from the chat box to the final data access and ask, at the last hop, whose identity is being checked.

Failure class 2: Tenant isolation loss in memory and retrieval

The second failure class is the one that turns a single bug into a cross customer breach, which is exactly the outcome that ends contracts.

Most useful AI features in SaaS are retrieval augmented. The agent pulls relevant context from a store of documents, past conversations, support tickets, or embeddings, and uses it to answer. In a multi tenant product, that store holds every tenant’s data. Isolation between tenants now depends entirely on the retrieval query being correctly scoped to the asking tenant, every single time.

That scoping is easy to get wrong, and the failures are quiet.

A vector search that filters by tenant in application code, but not in the query itself, can return a near match from another tenant whenever the application filter is skipped on one code path. A shared conversation memory that keys on user ID but not tenant ID can surface a previous answer that contained another customer’s data. A caching layer in front of the model can return a generated answer built from tenant A’s context to a tenant B user whose prompt hashed to the same key.

The thing that makes this dangerous is that it does not look like an attack. There is no malformed request. A user asks a normal question, and the agent, drawing on an improperly scoped store, includes a sentence that belongs to someone else. You will not find it with a scanner, because nothing is technically broken at the protocol level. You find it by seeding two tenants with distinct, identifiable data and then asking one tenant’s agent questions designed to pull the other tenant’s content into context.

If your product uses RAG, shared memory, or any cross request caching around the model, tenant isolation testing of that layer is not optional. It is the highest impact test you can run on an AI feature.

Failure class 3: Over scoped tool permissions

The third class is simple to state and widespread: the agent can do more than the user it serves.

When teams wire up tools for an agent, the scoping conversation is usually about what the agent needs to be useful, not about what each user is allowed to trigger. So the agent gets a tool that can issue refunds, a tool that can change account settings, a tool that can export data, a tool that can send email. These are granted at the agent level. The per user question, should this particular user be able to cause this action, is frequently never asked, because the mental model is that the agent is a helpful assistant rather than a new, highly privileged actor that any user can instruct.

The test here is to enumerate every tool the agent can call, then for each one ask a low privilege user to make the agent do it. A read only user who can make the agent trigger a write. A standard tenant member who can make the agent reach an admin only function. A trial account that can make the agent call an action reserved for paying customers. Each of these is a privilege escalation, and because it runs through the agent, it will not show up in a test that only exercises the user’s own direct permissions.

Over scoping also compounds the first two classes. A broadly scoped agent identity is what makes identity collapse high impact. The more the agent can do with its own privileges, the worse every confused deputy bug becomes.

Failure class 4: Business logic abuse by chaining tools

The fourth class is the one scanners will never find and the one that best demonstrates why manual testing still matters.

Individually, each tool an agent exposes might be properly authorized. The user is allowed to read their own data. The user is allowed to generate a report. The user is allowed to export. The vulnerability appears when a user, through the agent, chains those legitimate capabilities into an outcome the product never intended to allow.

A user might not have a direct feature to bulk export every record in a workflow. But if they can ask the agent to read a set of records, then summarize them, then send the summary to an external address, they have assembled an exfiltration path out of three permitted actions. No single step is a violation. The chain is.

This is business logic testing, applied to a system that now takes instructions in plain language. The natural language interface actually widens the attack surface, because the user does not need to know your API shape or your workflow constraints. They describe an outcome, and the agent figures out which permitted tools get there. Testing it means thinking in outcomes, not endpoints: what is the most damaging thing a user could talk this agent into assembling, and does any control stop the assembled chain even though each link is allowed.

How to test AI agent access control by hand

A scanner cannot do this work, because every request involved is well formed and individually authorized. The vulnerability lives in the relationships between identity, tools, tenants, and chained actions. Here is the manual approach that actually surfaces it.

Start by mapping the real architecture, not the chat interface. You need four things written down: every tool or function the agent can call, the identity each tool call runs as, where the user’s identity is or is not propagated to each call, and every data store the agent can read from with its tenant scoping model. Most of the findings become obvious the moment this map exists, because the gaps are visible on paper.

Then test identity propagation directly. For each tool that touches user or tenant data, set up two accounts in different tenants and drive the agent, as the low privilege user, toward an object owned by the other account. Watch the actual backend call if you can observe it. The question is always the same: at the final data access, is the asking user’s identity checked against the object, or only the agent’s. If only the agent’s, you have a tool layer BOLA.

Test function level authorization the same way. Enumerate the privileged tools and, as a user who should not be able to trigger them, try to make the agent do it anyway. A refund, an export, a settings change, an admin query. Any success is BFLA through the agent.

Test tenant isolation in the retrieval and memory layer deliberately. Seed tenant A with unique, searchable canary content. As a tenant B user, ask the agent questions crafted to pull that content into context: topics that overlap, phrasings that would match embeddings, references to shared concepts. Anything from tenant A that appears in tenant B’s answers is a cross tenant leak, and it is almost always critical.

Test the chains last, because they require understanding the product. List the agent’s capabilities and look for combinations that assemble into an outcome the product restricts as a direct feature. Bulk read plus external send. Read plus transform plus export. Then try to talk the agent through the full chain in one conversation and see whether any control interrupts it.

Throughout, treat prompt injection as a delivery mechanism, not the finding. If a tool ingests external content, a web page, a document, an email, a support ticket, then an attacker can plant instructions in that content. The real test is not whether the injection lands. It is what the agent is able to do once it does. An injection that can only make the agent say something is low impact. An injection that can make the agent call a tool that reads another tenant’s data is a breach. The access control testing above is what tells you which one you have.

What good looks like

The fixes are not AI specific. They are the same authorization discipline SaaS has always needed, applied to a new actor.

Propagate the user’s identity to every tool call, and enforce authorization at the tool layer against that identity, not against the agent’s. The agent’s own privileges should never be what authorizes a user initiated action. If a tool reads a record, it must check that the asking user, carried through from the original request, is allowed to read that specific record. The ownership and function level checks you already built for your user facing API have to exist on the paths the agent uses too.

Scope every data store the agent reads by tenant at the query level, not only in application code that a single code path can skip. Isolate conversation memory and any model output cache by tenant as strictly as you isolate the primary database. Treat the retrieval layer as a place where a missing filter equals a cross tenant breach, because it does.

Give agents the least privilege that keeps them useful, and scope tools per user where the action matters. An agent that can issue refunds should only be able to issue a refund when the specific user driving it is authorized to. Where that per user check is hard to push into the tool, gate the capability so low privilege users cannot reach it through the agent at all.

Then verify the chains, log every tool call with the real initiating user, and retest after changes. Agent capabilities grow fast as teams add tools, and every new tool is a new path that needs the same identity and authorization checks as the last one.

Where this leaves you

AI features did not invent a new vulnerability class. They took the one that already produces the most serious SaaS findings, broken access control, and relocated it to a layer that most security testing has not caught up to. The agent is a new, privileged actor that any user can instruct in plain language, and it sits upstream of the authorization logic your product spent years getting right.

If you are shipping AI features into a multi tenant product, AI agent access control is the thing to test before launch, and the questions worth answering are concrete. Whose identity is checked at the final data access when the agent acts. Whether your retrieval and memory layers isolate tenants as strictly as your database. What the most damaging chain of individually permitted tool calls a user could assemble. Whether a trial or read only user can make the agent do something they cannot do directly.

That is the work, and it is manual. It is also exactly where the real findings are.

If you are building AI into your SaaS product and want these paths tested properly before a customer or an auditor finds them first, that is the kind of review The Hidden Finds runs. Request a security review.

FAQ

Is prompt injection the main AI security risk?

Prompt injection is the delivery mechanism, not the impact. An injected instruction only becomes a security incident when the agent it reaches can access data or trigger actions the user should not have. The impact is decided by access control: what the agent is authorized to do. Testing that is what tells you whether a given injection is a minor annoyance or a cross tenant breach.

Can an AI agent cause a BOLA or IDOR?

Yes, and it is one of the most common AI findings. If the agent fetches objects using its own privileged identity without checking that the specific user who prompted it owns the requested object, you have broken object level authorization at the tool layer. The user never crafts a malicious request. They talk the agent into making it.

How is AI agent access control different from normal API authorization?

In a normal API, every request carries the user’s identity and your authorization layer checks it. With an agent, the user’s identity can be lost before the real backend call happens, and the agent often acts with a broad service identity. Authorization has to be enforced at the tool layer against the propagated user identity, not against the agent’s own privileges.

How do you test access control in an AI agent workflow?

Manually. Map every tool, the identity each runs as, and every data store the agent reads. Then drive the agent as a low privilege user toward other tenants’ objects, toward privileged functions, and toward retrieval of other tenants’ seeded data, and look for combinations of permitted tools that assemble into a restricted outcome. Scanners cannot find this because every individual request is well formed and authorized.

Why does tenant isolation break in AI features specifically?

Most AI features are retrieval augmented and draw context from a store holding every tenant’s data. Isolation depends on that retrieval being correctly scoped to the asking tenant on every path. A missing filter in one code path, a shared memory keyed only by user, or a response cache that ignores tenant can all surface one customer’s data to another, with no malformed request to flag it.

SECURITY REVIEW

Need help testing your SaaS application?

I help SaaS and API teams find exploitable vulnerabilities, access control gaps and attack paths before they become incidents.

SaaS SecurityAPI SecurityAccess ControlManual Validation
Request a Security Review