Role-Based Access Control for Agents

9 min read Module 6 of 10 Topic 17 of 30

What you'll learn

  • Distinguish agent identity from human identity and explain why agents need their own role hierarchy
  • Define fine-grained permission scopes at the tool level and enforce them before execution
  • Write Rego policies in Open Policy Agent that express agent authorization rules as code
  • Update agent permissions dynamically via OPA bundle updates without restarting any agent process
Building this at your company? For enterprise and company teams taking this to production: book a 30-minute session with our AI engineers for architecture guidance, code review, and a rollout plan for your use case.
Book a Team Session

In enterprise agent networks, authorization is not a feature you bolt on after the system is running, it is a structural property of the architecture. When 40 agents can autonomously call hundreds of tools, the question of who is allowed to call what determines your entire risk surface. Human-derived RBAC models fail here because agents have fundamentally different usage patterns than the people they work alongside. This lesson builds a purpose-built RBAC system for agent networks, enforced centrally by Open Policy Agent.

Agent Identity vs Human Identity

Human identity in enterprise systems is built around job functions: an analyst can read reports, a manager can approve workflows, an admin can configure the system. These roles map to UI actions that a human consciously chooses.

Agent identity is categorically different. An agent’s “role” must reflect its workflow position, what tools it needs to accomplish a specific task, not its organizational equivalent. Consider three reasons this distinction matters in practice:

Call frequency. A human analyst might export a report twice a day. An equivalent agent might call the same export tool 500 times an hour as part of an automated pipeline. The risk profile is entirely different.

Scope creep. Humans navigate permission boundaries through UI affordances, they see what they can click. Agents will call any tool they are authorized to call, programmatically, without hesitation. Overly broad agent roles become operational vulnerabilities.

Credential sharing. Humans have passwords and MFA. Agents share service account credentials across instances. If one instance is compromised, all instances with the same role are compromised unless the role itself is tightly scoped.

mindmap
  root((Agent RBAC))
    Agent Roles
      planner-agent
        tool:decompose_task
        tool:delegate_to_worker
      data-agent
        tool:read_financial_records
        tool:query_database
      report-agent
        tool:write_report
        tool:send_notification
    Human Roles
      analyst
        ui:read_dashboard
        ui:export_report
      manager
        ui:approve_workflow
        ui:view_audit_log

Tool-Level Authorization with OPA

Open Policy Agent (OPA) is a general-purpose policy engine that decouples authorization logic from application code. Instead of scattering if agent.role == "data-agent" and tool == "read_financial_records" checks across every agent and service, you centralize all authorization decisions in OPA and make every tool executor query it before running.

The architecture uses OPA as a sidecar in each agent pod. Before executing any tool call, the agent executor sends an authorization request to the local OPA instance on localhost:8181. OPA evaluates the request against the active Rego policy bundle and returns allow or deny in under 1ms, the overhead is negligible.

sequenceDiagram
    participant PA as Planner Agent
    participant WA as Worker Agent
    participant OPA as OPA Sidecar
    participant TOOL as Tool Executor
    participant AUDIT as Audit Logger

    PA->>WA: Delegate task (with agent JWT)
    WA->>OPA: POST /v1/data/agent/authz/allow\n{agent_id, tool, action, context}
    OPA-->>WA: {allow: true, reason: "role:data-agent has tool:read_db"}
    WA->>AUDIT: Log authorization decision (allow)
    WA->>TOOL: Execute tool
    TOOL-->>WA: Tool result
    WA->>AUDIT: Log tool execution result

Writing Rego Policies for Agent Authorization

Rego is OPA’s declarative policy language. It reads like structured English once you understand its evaluation model: a rule is true if all conditions in its body are true, and OPA evaluates every rule for a given query.

# rego/agent_authz.rego
# This policy is the single source of truth for what each agent role can do.
# It is version-controlled, reviewed, and deployed as a bundle: not hardcoded in agent code.

package agent.authz

import future.keywords.if
import future.keywords.in

# Default deny: if no rule explicitly allows, the request is denied.
# This is the most important line in the policy: fail closed, not open.
default allow := false

# Allow the request only if the agent's role grants the requested tool scope.
allow if {
    # Extract the agent's role from the verified JWT sub claim.
    # The token was already verified by mTLS at the transport layer.
    agent_role := data.agent_roles[input.agent_id]
    # Check that this role explicitly includes the requested tool.
    # 'in' requires an exact match: no glob expansion, no inheritance by default.
    input.tool in data.role_permissions[agent_role]
}

# Emergency revocation: deny any agent in the revoked list, regardless of role.
# This rule takes precedence and is used for incident response.
deny if {
    input.agent_id in data.revoked_agents
}

# Final decision: allow only if allowed AND not denied.
# Separating allow and deny lets us add revocation without touching the allow logic.
final_decision := {"allow": allow and not deny}

The policy data, agent_roles (mapping agent IDs to roles) and role_permissions (mapping roles to allowed tools), lives in OPA’s data document, updated via bundle. The Rego logic never changes for permission updates; only the data does.

# agent_executor.py
import httpx
from functools import wraps

OPA_ENDPOINT = "http://localhost:8181/v1/data/agent/authz/final_decision"

def require_authorization(tool_name: str):
    """
    Decorator that gates every tool execution behind an OPA authorization check.
    Applied at the executor layer so no agent code can bypass it.
    The agent_id comes from the verified JWT, not from the request payload
    preventing agents from spoofing each other's identities in authorization requests.
    """
    def decorator(func):
        @wraps(func)
        async def wrapper(self, *args, agent_jwt_claims: dict, **kwargs):
            response = await httpx.AsyncClient().post(
                OPA_ENDPOINT,
                json={
                    "input": {
                        "agent_id": agent_jwt_claims["sub"],
                        "tool": tool_name,
                        # Include context for attribute-based extensions later.
                        "environment": agent_jwt_claims.get("env", "production"),
                    }
                },
                timeout=0.5,  # Hard timeout: authorization must be fast or fail closed.
            )
            decision = response.json().get("result", {})
            if not decision.get("allow"):
                raise PermissionError(
                    f"Agent {agent_jwt_claims['sub']} not authorized for tool '{tool_name}'"
                )
            return await func(self, *args, **kwargs)
        return wrapper
    return decorator

Dynamic Policy Updates and Emergency Revocation

OPA bundles enable the most operationally important property of this system: you can change agent permissions in production without restarting a single agent process. Bundle configuration points OPA to an S3 bucket or HTTP endpoint hosting your compiled policy bundle; OPA polls it on a configurable interval (60 seconds is typical).

The update workflow for a permission change is: modify the data document (e.g., add "tool:web_search" to role_permissions["research-agent"]), recompile the bundle with opa build, push to S3, and wait for OPA to pull it. No Kubernetes rolling update, no pod restart, no in-flight request disruption.

Emergency revocation follows the same path but faster. Add the compromised agent’s ID to the revoked_agents list in the data document, push an emergency bundle, and within the polling interval every service using OPA will deny that agent’s requests. The revocation is centralized, auditable, and takes effect in under a minute.

Every authorization decision, allow and deny, must be written to your audit log with the full input context. When a security incident occurs, you need to reconstruct exactly which tools a compromised agent was authorized to call, which calls it actually made, and when the revocation took effect. That reconstruction is only possible if authorization decisions are logged with the same fidelity as the tool executions themselves.

The next lesson builds on this authorization layer by adding the compliance dimension: making your audit logs legally defensible, enforcing GDPR and HIPAA data handling constraints, and automating evidence collection for SOC 2 audits.

Knowledge Check

3 questions to test your understanding

1 A financial compliance agent and a human compliance analyst both have the role 'compliance-reader'. The agent autonomously calls a bulk-export tool that the analyst never uses. What RBAC design flaw does this reveal?

2 You need to add a new permission allowing the research-agent to call the 'web_search' tool in production. Your OPA bundle is hosted on S3. What is the correct update sequence?

3 An agent's credentials are compromised in a production incident at 2:47 AM. The on-call engineer needs to revoke all permissions for that agent immediately. What is the fastest zero-downtime revocation path?

Discussion

Questions and notes from learners on this topic

Loading discussion…

Go further with expert guidance

Ready to build production AI?
Talk to our R&D team.

These courses give you the foundation. Our embedded AI teams take you from prototype to production in 30–90 days, with your team, your codebase, your goals. Book a free strategy call to see how we can accelerate your AI initiative.

30 minutes · No obligation · Expert AI engineers, not sales reps

AI Architecture Review

Audit your current stack and identify high-impact improvements

Project Review

Get expert feedback on your AI implementation and codebase

Team Mentoring

Upskill your engineers with hands-on AI coaching sessions

AI Strategy

Define your AI roadmap, prioritization, and implementation plan