DEV Community

Akash Devdhar
Akash Devdhar

Posted on Originally published at akashdevdhar.com

RBAC vs ABAC: Choosing the Right Authorization Model

Every time an AI agent project hits its first "wait, this agent should not have been able to do that" moment, someone proposes the same fix: add more roles. Split "support_engineer" into "support_engineer_tier1" and "support_engineer_tier2." Add a "read_only_agent" role. This works for about two more incidents, and then you have forty roles, half of them overlapping, and nobody actually knows what any of them mean anymore. That is the point where the conversation should shift from RBAC to ABAC, and it is also the point where most teams do it wrong, because they treat ABAC as "RBAC but with more config" instead of an actually different way of thinking about the problem.

What RBAC is actually good at, and where it stops working

Role Based Access Control is simple, and simple is a feature, not a weakness. You define a role, "support_engineer," you attach a fixed bundle of permissions to it, "read tickets, read customer profile," and you assign the role to a subject, human or agent. This works great when permissions genuinely cluster into a small number of stable job functions. It is also exactly why it breaks for agents.

An agent's required permissions are not a fixed job function, they are a function of context. The same support agent should be able to read a ticket in its own team's queue but not in a different team's queue. It should be able to act during business hours on a live incident but maybe not at 3am unattended. It should be able to act on behalf of the specific user who invoked it, not on behalf of any user who happens to share its role. RBAC has no native concept of "in its own team's queue" or "on behalf of this specific user," so people fake it with more and more roles, and the role explosion is the actual symptom, not the disease.

RBAC: a role is a fixed bundle of permissions

What ABAC actually changes

Attribute Based Access Control does not ask "what role does this subject have." It asks "given everything we know about the subject, the resource, and the environment right now, does this specific action pass the policy." Subject attributes: which team, which clearance level, who delegated to it. Resource attributes: which team owns this ticket, how sensitive is it. Environment attributes: what time is it, is this an emergency escalation. The decision is computed at request time, not looked up from a static table.

ABAC: permission is a live decision computed from attributes

This is genuinely more powerful for the "agent acting on behalf of a specific person, in a specific context" problem that RBAC struggles with. It is also more work, and this is the part people skip. ABAC without a real, maintained policy set is not more secure than RBAC, it is just a different place to hide the same mess, and honestly a worse one, because a bad ABAC policy is harder to audit at a glance than a list of forty overlapping roles.

The honest tradeoff

I am not telling you ABAC is strictly better, that would be dishonest actually. RBAC is easier to reason about, easier to audit ("show me everyone with the admin role" is a one line query), and perfectly fine for systems where permissions genuinely map to stable job functions. ABAC is the right call once your access decisions depend on context that changes per request, which for agent systems is most of the time, but only if someone actually owns writing and testing the policies, the same way someone has to actually own the role definitions in an RBAC system that is not a mess. Neither model saves you from doing the thinking, they just organize where the thinking lives.

A quick code example

RBAC, a straightforward role lookup:

ROLE_PERMISSIONS = {
    "support_engineer": {"tickets:read", "customer_profile:read"},
}

def is_allowed_rbac(subject, action):
    role = subject.role
    return action in ROLE_PERMISSIONS.get(role, set())
Enter fullscreen mode Exit fullscreen mode

ABAC, a policy evaluated against subject, resource, and environment attributes:

def is_allowed_abac(subject, resource, action, env):
    if action == "tickets:read":
        same_team = subject.team == resource.team
        within_hours = env.hour in range(6, 22)
        acting_for_owner = subject.delegated_by == resource.owner_id
        return same_team and (within_hours or acting_for_owner)
    return False
Enter fullscreen mode Exit fullscreen mode

Notice the ABAC version is not longer because ABAC is inherently more complex, it is longer because it is actually encoding the real rule, "same team, and either business hours or acting for the resource's owner," instead of pretending that rule can be flattened into a role name.

Standards worth reading

  • INCITS 359-2012, the NIST RBAC model, for the formal definition of roles, permissions, and role hierarchies.
  • NIST SP 800-162, Guide to Attribute Based Access Control, for how to actually define subject, resource, and environment attributes and structure policies around them, this is the one to read before building an ABAC system, not after.

The takeaway

RBAC fails for agents not because roles are a bad idea, but because agent permissions are contextual and roles are not. ABAC fixes that by making the decision live instead of a lookup, but it only works if you put in the same discipline you would have needed for RBAC anyway, just applied to policies instead of role definitions. Pick RBAC when your access model genuinely maps to stable jobs. Pick ABAC when it depends on who, what, and when, and budget the time to actually own the policies, because an unmaintained ABAC system is not a safer RBAC system, it is a harder to audit one.

Top comments (0)