Your copilot needs identity, permissions, and limits; not just a prompt

The prompt does not control access: a SaaS copilot needs its own identity, OAuth, tenant isolation, tools closed by default, and a human only when the risk requires it.

a close up of a computer screen with a menu on it

An agent inside a SaaS needs its own identity, tenant-scoped permissions, narrow scopes, and a limit you can revoke. The prompt does not control access: if it acts on customer data, treat it like a service (who it is, on whose behalf, on which tenant, and how far it can go).

The copilot no longer just drafts: it calls your API, reads the ticket, and proposes a refund. At that point it stops being a chat and becomes a system actor, and an actor needs an identity, permissions, and limits.

When the pilot touches customer data, someone asks what permissions it has. If the answer is "it is instructed not to do harm", you do not have security, you have a wish. An agent in a SaaS shares the database, the tools, and the memory with the product. If it acts, treat it like a service: who it is, on whose behalf, on which tenant, and how far it can go.

The 2026 OWASP Top 10 for LLM applications names the main security risks of shipping AI agents. Among the most important is, first, prompt injection (instructions smuggled into context). Third is excessive agency (the agent has more power than the task needs). Neither one is fixed by a line in the system prompt: they are fixed in the architecture.

Why calling the agent "secure" is not enough

"Secure" is an adjective, and a committee cannot audit an adjective: it can audit a list of controls you can show, test, and revoke.

The usual mistake is treating the system prompt as a firewall, with instructions such as "do not touch other customers", "do not delete anything", or "ask if you are unsure". The model does not sign that promise. A ticket, a PDF, or a tool that returns text can drop new instructions into the context: that is prompt injection, and the model reads them as yours.

The second mistake is mixing up "the user is authenticated" with "the agent is authorized". The user logged in, and the agent often reuses that session or an API key with more power than the user. Authentication says who called; authorization says what that call may do. They are different layers, and mixing them is how you get a black box that "worked in the demo".

You should be able to answer four questions: which identity it uses, which scopes it has, which tenant it is in, and how you stop it. If any answer is "the model already knows", the design is not ready. Same jump we already covered from POC to production: a demo is not an agent.

The agent's identity is not the user's

In a SaaS with agents there is not one "who", but three, and you have to be able to name them.

The user is the person (or role) who opened the thread (support, admin, customer success), the product is the app that orchestrates, and the agent is the runtime that decides which tool to call. If all three ride the same session, the log cannot tell "Maria asked for a summary" from "the agent exported the CRM". The day you have an incident, you will not know who to revoke.

There are two patterns: impersonation, when the agent uses the user's full identity (easy to ship, and it breaks least privilege), and delegation (act on behalf of): the agent has its own identity and a narrower token, marked as "for Maria". AWS describes this in its multi-tenant agents guide and in the on-behalf-of pattern (RFC 8693): the token carries the user and the actor, and downstream can require both.

For example, Maria can read tickets in her tenant and cannot issue refunds. If the agent uses a "support admin" API key, it can refund. If it uses Maria's session uncut, it cannot refund, but you also cannot revoke the agent without kicking Maria out. If the agent has its own identity, with tickets:read and without refunds:write, the refund does not exist as an action, no matter what the prompt "asks" for.

The rule is that the agent is not the user: it is an actor with its own identity, bound to a user and a tenant. If you cannot revoke it without removing the human, you do not have identities yet, you have a costume.

OAuth, scopes, and short-lived credentials

OAuth is how you delegate access without sharing the password. The user (or your product's identity provider) issues a token. The token is not "full access": it carries scopes, concrete verbs (tickets:read, orders:refund). The tool checks the scope and, if it is missing, returns 403. The model does not get a vote.

A short-lived credential (15 minutes, an hour, whatever your flow can stand) limits the blast radius if it leaks. A forever API key in the environment, or worse, pasted into the system prompt, is a lifetime pass. If the context leaks (a log, a trace, another tenant in the index), the key travels with it.

MCP (Model Context Protocol, the contract for connecting agents to tools) makes this normative: the MCP server is an OAuth 2.1 resource server. The token has to be issued for that server (audience binding, RFC 8707). A token for server A is not reused on server B. Without an audience, every tool on the network accepts the same Bearer.

Three rules that prevent most of the 3 a.m. pages:

  1. The secret does not live in the prompt: it lives in a vault, is exchanged for a short token, and is not logged.

  2. The scope is narrower than the user's role. If Maria cannot refund, neither can the agent. If Maria can refund up to $50, that cap lives in the tool, not in the prose.

  3. The token dies (rotation, expiry, revocation). If the agent is orphaned (the user signs out, changes role, the tenant is shut off), the token stops working.

OAuth does not make the agent "secure": it stops authorization from being a conversation with the model.

Isolate by tenant, not by prompt

A tenant is one customer's account in your SaaS. Isolation means the agent for tenant A does not read, write, or remember tenant B: not in the API, not in memory, not in the search index, and not in the log someone pastes into Slack.

The classic failure is not a hacker, but a tool that searches "invoices" with no tenant_id, or a shared vector store where an embedding of B's contract shows up in A's answer. The model did not "cross the tenant": you gave it an unpartitioned index.

The AWS SaaS Lens does not trust the handler to isolate. In a pooled model (one function, many customers), the runtime asks for credentials scoped to that tenant. Even if the code is wrong, the credential cannot reach the neighbor. The multi-tenant agents guide adds the agentic part: tenant context travels in the JWT on every hop, including agent to agent.

A short leak list, not a slide:

  • Every tool execution must be tied to a tenant taken from trusted authentication context (session, token, JWT, backend). The agent should not freely choose which tenant it operates on from conversation text.

  • Memory and RAG (document search) are partitioned. One namespace per tenant. No "search global just in case".

  • Logs and traces do not mix two customers' payloads in one exportable document.

  • The agent has no "list every tenant" tool unless that is a platform role, outside the product.

If isolation lives only in the prompt, there is no isolation, only hope, and in a SaaS hope is not a control.

Default-deny on tools

A tool is a door, and default-deny means that if it is not allowed, it is closed, not the other way around. The model does not pick from your entire backend menu: it picks from a short list you opened for that flow, that role, and that tenant.

Excessive agency, the risk OWASP ranks third, is almost never a "bad" model. It is an agent with run_sql, send_email, refund, and admin_api because the demo looked good. Each extra tool is surface, and each free-form argument (query, url, path) is a lateral-movement tool.

A tool contract you can actually audit has four fields: name, what it does, which resource, which arguments. "Search help articles in the current tenant, max 5, no raw HTML" is a contract. "Query the database" is not.

Default-deny is enforced before the model:

  • An allowlist of tools per use case. The summary agent does not have refund.

  • An allowlist of arguments. A ticket_id from that tenant, not free SQL.

  • Quantity or amount limits: money, rows, recipients, and number of calls. A tool loop with no cap is not only a security risk, it is an invoice. We covered that in the token price is not the cost of your agent.

  • Fail closed: if the policy does not answer, deny, and do not "let this one through".

Least privilege is the list of tools the case needs and nothing else. If you had to open admin for the demo, the demo is not ready for a real tenant.

When do you actually need a human in the loop?

Human in the loop (HITL: a person approves or stops the action) is not a way to look serious; it is a risk control. You put it where failure is expensive to undo, not on every turn to seem careful.

You need HITL when the action is irreversible or high impact: money (refund, charge, plan change), writes into a customer system, export of personal data, outbound communication (email to the end user), or anything that crosses a legal or contractual line. Also when the tenant is new and you still have no evaluation: the human is the brake while you measure.

You do not need HITL to classify a ticket, draft an internal note, or pull three help articles. Extra humans there do not add security, they add a queue. Permanent HITL turns the agent into an expensive intern and kills the savings that justified the case.

Two limits are worth stating clearly. HITL does not guarantee security: a person who rubber-stamps does not read the injected context, they click approve and move on. It also does not remove prompt injection. The model may propose a tool call with incorrect arguments, and if the UI only shows the human a simplified summary instead of the action and the real arguments they are about to approve, that validation may be insufficient. HITL does not fix a bad permissions architecture: if the tool should not have been on the allowlist, you fix that upstream, not with a confirmation screen.

Design HITL by risk: a threshold (amount, data, destinations), sampling once you have evaluation, escalation if nobody answers, an owner. If everything goes through "the team Slack channel", you do not have HITL, you have a bottleneck with no trail.

Audit and revocation

If you cannot say who did what, on whose behalf, in which tenant, and with which tool, you have a black box, no matter how good the model is.

A useful trace, the one a CISO asks for and the one that saves you at 3 a.m., carries: user identity, agent identity, tenant, tool, arguments (no secrets), decision (allow / deny / HITL), result, prompt and model version. Without that you do not have an incident, you have a conversation.

Revocation is the test that the identity exists. You can revoke the agent's token without kicking the user, shut off one tenant's agent without taking down the product, and rotate a tool credential without redeploying the model. A kill switch is not a platform extra: it is proof the agent is not an orphan process.

Audit without revocation is a museum, and revocation without audit is a blind switch: you need both, from the first pilot tenant, not "when we scale".

A permissions checklist

Before you give an agent write permissions in a production environment, the company should be able to answer these questions.

Does the agent have its own identity, distinct from the user and from the service?

Does every token carry tenant, scopes, and expiry, and does the tool check them (not the prompt)?

Does tenant isolation cover API, memory, index, and logs, not only the happy-path handler?

Are tools on default-deny, with an argument allowlist and a call limit?

Is HITL placed by risk, with an owner, and is nobody selling it as a guarantee?

Can you revoke the agent for one tenant today, and leave a trail of who did it?

If two or more are missing, you do not have a governed copilot: you have a process with a key. The prompt can be good, but it is not an access control.

Once those lines exist, the rest is engineering. At Devic that chain (identity, policy, tool, trace) runs in the harness: permissions per run, not a "secure" stamp. We do not promise prompt injection goes away; we promise the tool can be denied, audited, and shut off. The plans are how you measure that on one tenant before you open it on every tenant.

The copilot that wins the demo talks well; the one that survives in a SaaS has identity, permissions, and limits.

Alberto Iglesias

CEO

Turn your SaaS AI-Native

Get a free trial just by signing up


Ready to turn your platform AI-Native?

Centralize agents, tools, and flows in one platform and start scaling with less friction.

Build and run agents without friction

Connect your current infrastructure

  • Devic AI

  • Devic AI

  • Devic AI

Ready to turn your platform AI-Native?

Centralize agents, tools, and flows in one platform and start scaling with less friction.

Build and run agents without friction

Connect your current infrastructure

  • Devic AI

  • Devic AI

  • Devic AI