
During a recent discussion about an AI assistant for an internal platform, we walked through a fairly straightforward workflow: a user describes a problem, the agent retrieves the relevant context, calls platform APIs, and returns a proposed solution or configuration draft.
Most of the early questions were about functionality. Could the agent understand dependencies across modules? Could it identify why a workflow failed? Could it help users avoid asking an engineer to investigate every configuration issue?
Then we reached a question that changed how I thought about the design:
If the bot calls those APIs using its maintainer's permissions, what prevents another user from asking it to perform an action they could not perform themselves?
The agent might understand the request perfectly and execute every API call correctly. The problem would still exist: the system had allowed a conversational interface to become a shortcut around its access controls.
That question led me to look more closely at delegated authorization, human approval, and audit trails. It also made me reconsider what a useful agent should be allowed to decide.
1. A User Request Contains a Goal, but Not a Complete Authorization
Consider a simple request:
“This workflow is filtering out too many events. Help me fix it.”
From a product perspective, the intent seems clear. From an execution perspective, several important questions remain unanswered.
Which workflow and version does the user mean? Are they asking for an explanation, a proposed change, or an update to the live configuration? Can they edit every dependency involved? If several fixes are possible, which trade-off are they willing to accept?
For example, an agent might identify three possible approaches:
Adjust the filtering condition.
Change how missing feature values are handled.
Repair an upstream data issue.
Each approach could address part of the symptom, but their impact would be different. One might increase downstream traffic. Another might change business behavior. The third might require support from another team.
The original request does not settle those decisions.
I found it useful to separate three questions:
- Capability: Can the system perform this action?
- Permission: Is this user allowed to perform it on this resource?
- Authorization for this task: Has the user agreed to this particular action?
An available API answers the first question. The platform's access controls answer the second. A sufficiently specific user instruction or approval answers the third.
A reliable agent needs all three.
2. The Bot Should Not Become a Shortcut Around Access Controls

The permission issue resembles a classic security problem known as the confused deputy: a more privileged service is induced to use its authority on behalf of a less privileged requester.
In our scenario, imagine that a bot maintainer can edit workflows A and B, while the requesting user can edit only A. If the bot uses the maintainer’s credentials without checking the requester’s permissions, the user could potentially modify B through the bot.
A prompt saying “respect user permissions” would not establish an enforceable boundary. The check needs to happen in the backend, where the API request is authorized.
OWASP’s guidance on excessive agency makes a similar point: actions performed on a user’s behalf should run within that user’s security context and use the minimum privileges necessary.
For this type of platform agent, I would define the effective access as:
User permissions + Agent’s allowed operations + Authorization for the current task
This introduces three independent limits.

The user may have broad access, but the agent might be restricted to reading configurations and preparing drafts. The agent may support draft creation, but the user might lack edit access to the target project. Both may permit an operation, but the current task may authorize changes to only one workflow.
This also means the system should distinguish between the person requesting the work and the service executing it.
Delegated authorization provides an established foundation for that distinction. OAuth 2.0 Token Exchange, for example, supports representing a subject and an actor: the party on whose behalf an action is performed and the party performing it. The specific implementation depends on the existing identity infrastructure, but the design requirement is clear: both identities need to remain visible.
The backend should derive the user’s identity from an authenticated session or messaging integration. It should never accept a model-generated user ID, or a statement such as “I am the project administrator,” as proof of authority.
3. Separate Recommendations from Execution
The discussion also raised a product question: how much decision-making should we delegate to the agent?
For an early version, I would prioritize diagnosis and recommendations. Given the user’s context, the agent should explain what it found, present reasonable options, and help the user understand their consequences.
A useful recommendation should include:
- The evidence supporting it.
- The conditions under which it applies.
- The expected result.
- The affected resources and potential side effects.
- Any uncertainty or missing information.
There is no need to produce three options every time. If only one approach is well supported, the agent should say so. If the available evidence is insufficient, it should identify what is missing and suggest a practical next step, such as contacting the relevant RD or submitting a ticket.
This is where domain knowledge and skills can help. They can guide the investigation, explain platform-specific behavior, and define when to escalate. They improve the quality of the recommendation, while authorization remains the responsibility of the backend.
The distinction between recommendation and execution also needs to appear in the interaction.
A user selecting “Option B” has chosen a direction. They may not yet have approved the exact changes required to implement it.
Before a consequential update, the system should show the concrete change: the target resource, current version, proposed values, and expected impact. Approval should be tied to that specific proposal.
If the proposal changes, or the underlying configuration has moved to a newer version, the previous approval should not silently carry forward.
4. Make the Safer Workflow the Default
For this platform use case, I would structure the workflow as:
Inspect → Recommend → Prepare a draft → Review → Apply through existing controls

Each stage has a clear purpose and permission boundary.
Inspection uses the user’s read access. Recommendations explain possible approaches. Draft creation requires permission to create or edit the relevant object. Applying a change follows the platform’s existing review and approval process.
This approach also preserves familiar operating practices. If a manual change requires a reviewer, an agent-assisted change should not bypass that requirement simply because it started in a chat.
One detail deserves particular attention: “draft” must describe actual system behavior.
A draft that creates shared nodes, starts background jobs, or affects resources used by live workflows may already have operational consequences. The implementation needs to establish whether drafts are isolated, who owns them, who can see them, and how abandoned drafts are cleaned up.
The same principle applies to tool design. Narrow tools such as read_workflow, validate_configuration, and create_draft are easier to authorize than a general-purpose tool that can call arbitrary endpoints.
For a first release, a small set of clearly scoped tools would make the system easier to review and test.
5. Logging the User as Owner Is Necessary, but Incomplete
One proposal in the discussion was to record the requesting user as the owner of the case.
That is a useful starting point. However, a single owner field cannot explain the full sequence of events.
Suppose a user asks for a workflow fix, selects a proposed approach, and later discovers an unexpected change. We need to establish:
- What did the user originally request?
- What evidence did the agent retrieve?
- Which options did it present?
- What did the user select or approve?
- What did the agent actually submit?
- Which permission checks and approvals allowed it?
- Did the final result match the approved change?
I would therefore keep a structured audit record alongside the conversation history.
| Record | Purpose |
|---|---|
| Requesting user | Identifies who initiated the task |
| Agent service identity | Identifies what executed the API calls |
| Task and conversation IDs | Connects the request, discussion, and actions |
| Proposal and approval reference | Records exactly what the user accepted |
| Resource and version | Establishes the target and relevant state |
| Change details and result | Shows what actually happened |
| Authorization decision | Explains why the operation was allowed or denied |

These records should be produced by the application and execution services. An agent-generated summary can help people read the case, but it should not be the sole evidence of what occurred.
Retention and access also matter. Logs may contain sensitive configuration or user data, so they need appropriate access controls, redaction, and a defined retention policy.
Importantly, recording a user’s approval should not turn into a blanket transfer of responsibility. If the agent executed something different from the approved proposal, the audit trail should make that difference visible.
The purpose is to reconstruct the facts accurately.
6. Read Access and Group Conversations Need Their Own Boundaries
It is easy to focus on write operations because their impact is visible. Read access deserves the same attention.
An agent can disclose information through an explanation, a log excerpt, or a summary—even when it never changes a configuration.
Group conversations introduce another complication: the person asking the question may have access to a resource that other group members cannot view.
Permission to retrieve information for that person does not automatically establish permission to post it to the entire group.
Depending on the data involved, the bot could return a permission-checked platform link, move the detailed response to a private conversation, or provide a limited summary.
Conversation memory needs similar care. Context retrieved for one user or project should not become available in an unrelated session.
These details are easy to miss when the initial design treats the bot as a convenient messaging interface. Once the bot can retrieve internal data, the destination of its response becomes part of the access-control design.
7. What I Would Validate Before Expanding the Agent
Before adding more execution capabilities, I would test a few concrete cases:
- A user requests changes to a resource they cannot edit.
- A user attempts to make the bot act as another person.
- Retrieved content contains instructions to perform an unrelated action.
- A user approves one change, but the agent submits different parameters.
- The target resource changes after approval.
- A group request would expose information to unauthorized members.
- A retry could create duplicate objects.
These cases test whether the system maintains its boundaries when the conversation or execution becomes messy.
They also affect how we measure success. A correct refusal or a well-prepared escalation can be the right outcome. Counting every refusal as a failure would create an incentive to make the agent more permissive than the product requires.
--
References
- OWASP: Excessive Agency
- OWASP: AI Agent Security Cheat Sheet
- -RFC 8693: OAuth 2.0 Token Exchange