Agent permissions: RBAC and OAuth

Lesson 3.3 · 4 min read · by Vinay C

We'll cover the following

The rule: the agent acts with the user's permissions, never more. The LLM proposes, the code disposes.

RBAC

Role-Based Access Control: permissions attach to a role (customer, support agent, admin) and each user has a role. Enforce it in the tool code, not in the prompt. A prompt can be jailbroken; a check in code cannot.

Where the identity comes from

def cancel_order(order_id, ctx):      # ctx injected by your code, invisible to the LLM
    order = db.get(order_id)
    if order.user_id != ctx.user_id:  # ownership check
        return "Error: order not found"
    ...
  • Your own backend: the user logs in, you hold a session or JWT, and the tool reads user_id from it. The LLM only supplies order_id. Never let the LLM pass the user id as an argument.
  • Third-party systems (Gmail, Slack, GitHub): use the user's OAuth token (delegated access). The user consents once, and scopes limit what the agent can do. The API itself enforces what that user may see.
  • Anti-pattern: one shared service account with admin rights. A clever prompt can then fetch another user's data. This is the confused deputy problem.

Example: "cancel my last order and refund me"

  1. Ownership check in the tool (RBAC).
  2. Validate arguments: order exists, is cancellable, refund is at most the order total.
  3. Human confirmation before the irreversible step: "Cancel order #42 and refund $30. Confirm?"
  4. Idempotency, so a retry cannot refund twice.
  5. Audit log of every tool call, who triggered it, and the result.
  6. Least privilege: only the tools the agent needs; keep read tools and write tools separate.
Check yourselfWhy not just write "only touch the user's own orders" in the system prompt?

Prompts are not enforcement. A jailbreak or injected instruction can override them. The ownership check has to live in the tool code, using the identity from the session.

Summary: say it in the interview

  • "The LLM proposes, the code disposes. Identity comes from the authenticated session, never from model output."