A support agent proposes a refund for order 482. The request sounds reasonable, and the model has selected the right tool. Neither fact establishes permission. One authenticated support user may inspect that order but cannot refund it; another may do both. Production systems must make that distinction in trusted code before the proposed action executes.
A request is not a permission check
The support user’s message tells the agent what to attempt. Authentication tells the application who sent it. Authorization determines whether that person may perform this operation on this order, in this tenant, for this amount. Approval, when required, records a separate decision about the proposed action. These are different checks, with different sources of truth.
Suppose both support users ask, “Refund order 482.” The model might produce the same refund_order(order_id=482, amount=…) call for each. The first user should be able to inspect the order and perhaps draft a refund, but execution must be denied. The second user may be allowed to execute, subject to the order’s current state and any approval rule. Changing the prompt to make the model more cautious does not change either user’s authority.
That is the practical meaning of OWASP’s excessive-agency guidance: functionality, permissions and autonomy all matter, and downstream actions should be authorized in the user’s security context rather than by the LLM’s judgment. A refusal can prevent a bad action; a willingness to act cannot authorize one. Likewise, a risk classifier may flag a suspicious request, but it cannot establish a business user’s rights to a particular record.
Where an agent’s authority actually comes from
The model proposes a tool name and parameters. The application surrounding it decides whether to run the tool. That makes the tool adapter an enforcement point, not a passive pipe. Open Policy Agent’s agent-tool-calling pattern describes a harness asking a policy decision point about a proposed call and executing it only when permitted.
Before building the refund tool, define the permission matrix. Which authenticated identities can read an order, draft a refund, approve one and execute one? Does the rule cover every tenant or only assigned tenants? Which records and amounts require additional review? A role label alone is not enough: OWASP recommends checking the specific object on every request and denying by default.
For order 482, trusted application code should obtain the actor from the authenticated session, not from a chat message or a model-produced user_id. It should treat the proposed order ID and amount as untrusted inputs, validate them, and resolve the order’s authoritative tenant and current state. Policy can then evaluate the actor, operation, order, tenant, amount and required approval. If that evaluation is unavailable or inconclusive, execution stops.
The agent service has an identity too, but it is not the support user’s identity. A valid service credential proves which application is calling; it does not prove that the person behind this request may issue this refund. OWASP’s identity-propagation guidance makes that distinction explicit: a receiving service must validate trustworthy identity context and still authorize the requested action and resource.
Put a check on every path to the action
A useful execution path is: authenticated request → model proposes refund_order(order_id, amount) → trusted adapter validates the call and resolves the order → policy checks actor, action, resource, amount and approval → integration calls the business API → business API rechecks access and records the result. The model supplies a proposal, not the verdict.
Why check twice? The adapter knows which user initiated this conversation and can reject a bad proposal before it reaches a write API. The business service knows its own order state, tenant rules and other callers. If it trusts every call carrying the agent’s service credential, a direct integration call can bypass the adapter. If it has no trustworthy user context, a service-only check may be unable to distinguish the two support users. Design those boundaries together.
OWASP recommends authorization near the protected service, where the action and object can be checked. A gateway can reject requests early, but it cannot replace business-specific checks at the service. Hiding refund_order from some users, adding a system-prompt rule, or filtering tools in a client can improve the experience; none should be the only barrier to execution.
The same principle applies when the agent spans a CRM, order system and payment service. Each hop needs enough trustworthy context to apply its own policy, rather than accepting a conversational assertion that the user is authorized. Our guide to agents connecting disconnected SaaS tools covers that wider integration problem; the critical design decision here is which service makes each authorization decision.
MCP does not change the boundary. Its HTTP authorization specification describes token validation and scopes where authorization is used, while the tools specification requires servers to validate inputs and implement access controls. A visible tool or valid transport token is not permission to refund order 482. The tool server still needs a record-level business check.
Choose credentials that cannot outrun the policy
Prefer a validated user-delegated token when the downstream API supports it. Restrict its audience and scope to the intended service and workflow; have that service authorize the order-level action. OAuth 2.0 token exchange provides a mechanism for obtaining a downstream token and distinguishes delegation from impersonation. It does not, by itself, define your trust model or guarantee immediate revocation of a token issued through exchange. Plan how long issued credentials remain usable and where revocation is checked.
A delegated token is not always available. A restricted service identity can execute an approved background workflow, provided the application defines who may initiate it, what it can do and how each action is attributed. In that design, authenticate the agent service separately, perform the on-behalf-of user check in trusted server-side code, and keep the integration credential limited to the required operations. Never pass arbitrary chat text as identity context.
The dangerous alternative is one broadly privileged connector acting for every support user. If its credential can refund any order and the downstream API trusts that credential alone, the agent adapter becomes the sole protection against mistakes, bypasses and compromised callers. Narrow tools such as draft_refund and execute_approved_refund make the permitted workflow easier to enforce than a generic HTTP, SQL or administrative tool. Restrict write scopes and credentials even when the policy check is correct; a failure in one layer should not grant every possible action.
Require exact approval for consequential actions
Approval is an additional condition, not a way to give an otherwise unauthorized user refund rights. Let the agent draft the proposed refund, show the exact target and amount to the appropriate reviewer, and execute only after trusted code verifies the resulting approval. Use review selectively: prompting a human for every routine read or draft invites approval fatigue without improving the underlying permission model.
An agent-generated user_confirmed=true flag proves nothing. OWASP’s AI Agent Security Cheat Sheet calls for execution code to verify that approval belongs to the current actor and exact tool call, remains valid, and has not already been consumed. Bind it to the actor, tool, target, normalized parameters and expiry. If the agent changes the refund amount after approval, that is a new proposal requiring a new decision. Consume approval once at execution so a retry cannot turn one authorization into repeated refunds.
Asynchronous work needs the same discipline. A job that resumes later must recheck relevant permissions and current order state, not assume the earlier conversation still confers authority. Record the actor, acting service, tool, target identifier, policy version, authorization outcome, approval outcome and execution result in infrastructure the agent cannot edit. Redact sensitive values. Those records support investigation and detection; they do not prevent an unauthorized call.
Prove the boundary works before granting write access
Start with read and draft tools. Before enabling execution, run denial tests against the same route that will handle real tool calls:
Wrong tenant: submit another tenant’s order ID, even when the user can refund orders in their own tenant.
Lost authority: revoke a support user’s refund role after the agent drafts an action, then attempt execution.
Changed proposal: approve one normalized amount or target, then change it before calling the execution tool.
Approval replay: reuse an approval after a successful execution or after it expires.
Bypass: call the business integration directly, without the agent adapter, using the credentials it accepts.
Prompt injection: place an instruction claiming permission in a retrieved support ticket; verify that it changes neither identity nor policy.
Unavailable policy: make the policy decision unavailable and verify that no refund is issued.
Run successful cases too: the authorized support user should be able to perform the permitted action on the right order with valid approval, if approval is required. If a denial succeeds only because the model declines to make a tool call, the boundary has not been tested. Submit the proposed call directly to the adapter and the integration.
For teams building an agent against existing APIs and auth systems, this is the release gate: expand write authority only when both the agent path and direct service path enforce the permission matrix, and negative tests remain part of the deployment suite.