Auth & Security/
Lesson

You built an RBACWhat is rbac?Role-Based Access Control - assigning permissions to roles (like admin or editor), then giving users roles instead of individual permissions. system with admin, editor, and viewer roles. It works well until someone asks: "Can editors only edit their own posts?" RBAC cannot express this. The "editor" role grants posts:update, it says nothing about which posts. The permission applies to all posts or none. You need a system that considers attributes beyond just the user's role: who created the resource, when, what department they are in, whether the resource is published or in draft.

This is where ABACWhat is abac?Attribute-Based Access Control - an authorization model that makes decisions based on attributes of the user, resource, and environment rather than fixed roles. comes in. Attribute-based access control evaluates policies against attributes of the user, the resource, the action, and sometimes the environment (time of day, IP addressWhat is ip address?A numerical label (e.g., 172.217.14.206) that identifies a device on a network - DNS translates domain names into IP addresses., device type).

When RBACWhat is rbac?Role-Based Access Control - assigning permissions to roles (like admin or editor), then giving users roles instead of individual permissions. is not enough

RBAC breaks down when access rules depend on relationships or context:

RuleCan RBAC handle it?Why not?
"Admins can delete any post"YesRole-based, no context needed
"Editors can edit their own posts"NoRequires knowing who created the post
"Managers can approve their team's expenses"NoRequires knowing team membership
"Users can only access documents in their department"NoRequires matching user and resource attributes
"Support agents can view tickets assigned to them"NoRequires assignment relationship
"Content is only editable during business hours"NoRequires environmental context

If your access control rules include the words "own," "their," "assigned," or "during," you are describing ABACWhat is abac?Attribute-Based Access Control - an authorization model that makes decisions based on attributes of the user, resource, and environment rather than fixed roles. policies, not RBAC.

02

The ABACWhat is abac?Attribute-Based Access Control - an authorization model that makes decisions based on attributes of the user, resource, and environment rather than fixed roles. model

ABAC evaluates four categories of attributes to make access decisions:

Policy Engine
  ├── Subject attributes (who): role, department, clearance level, team
  ├── Resource attributes (what): owner, type, status, classification
  ├── Action attributes (how): read, write, delete, approve
  └── Environment attributes (when/where): time, IP, device, location

A policy is a rule that combines these attributes:

ALLOW action:update ON resource:post
  WHERE subject.id == resource.authorId
  AND subject.role IN ['editor', 'admin']
  AND resource.status != 'archived'

This single policy expresses: "Editors and admins can update posts they authored, as long as the post is not archived." Try expressing that with RBACWhat is rbac?Role-Based Access Control - assigning permissions to roles (like admin or editor), then giving users roles instead of individual permissions. alone.

03

The ownership check: ABACWhat is abac?Attribute-Based Access Control - an authorization model that makes decisions based on attributes of the user, resource, and environment rather than fixed roles. in practice

The most common ABAC pattern in web applications is the ownership check. It is so common that many developers implement it without realizing they are doing ABAC.

Here is the pattern AI generates when you ask for a "user can edit their post" feature:

// AI-generated: ownership check in the route handler
app.put('/api/posts/:id', authenticate, async (req, res) => {
  const post = await db.posts.findById(req.params.id);
  if (!post) return res.status(404).json({ error: 'Not found' });

  if (post.authorId !== req.user.id) {
    return res.status(403).json({ error: 'Not your post' });
  }

  await db.posts.update(req.params.id, req.body);
  res.json({ success: true });
});

This works for a single route, but the ownership check is buried in the handler. When you have 20 routes that need ownership checks, you end up with 20 slightly different implementations. Some check authorId, some check userId, some check createdBy, and some forget to check at all.

AI pitfall
AI almost never adds ownership checks unless you specifically ask. If you prompt "create an endpoint to update a post," the result will verify authentication but allow any logged-in user to update any post. You must explicitly ask "only allow the author to update their own post" to get the ownership check.
04

Extracting ABACWhat is abac?Attribute-Based Access Control - an authorization model that makes decisions based on attributes of the user, resource, and environment rather than fixed roles. into a policy function

Instead of scattering ownership checks throughout your codebase, centralize them in a policy engine:

// Policy definitions: who can do what to which resource
const policies = {
  'posts:update': (subject, resource) => {
    // Admins can update any post
    if (subject.role === 'admin') return true;
    // Editors can update their own posts
    if (subject.role === 'editor' && resource.authorId === subject.id) return true;
    return false;
  },

  'posts:delete': (subject, resource) => {
    // Only admins can delete
    return subject.role === 'admin';
  },

  'posts:read': (subject, resource) => {
    // Published posts are readable by anyone authenticated
    if (resource.status === 'published') return true;
    // Drafts are only readable by the author or admins
    return resource.authorId === subject.id || subject.role === 'admin';
  },
};

// Generic policy check
function checkPolicy(action, subject, resource) {
  const policy = policies[action];
  if (!policy) return false; // Deny by default
  return policy(subject, resource);
}

Now every access decision is defined in one place. You can read all the rules at a glance, test them in isolation, and change them without touching route handlers.

05

ABACWhat is abac?Attribute-Based Access Control - an authorization model that makes decisions based on attributes of the user, resource, and environment rather than fixed roles. middlewareWhat is middleware?A function that runs between receiving a request and sending a response. It can check authentication, log data, or modify the request before your main code sees it. pattern

You can wrap the policy check into Express middleware that loads the resource and evaluates the policy before the handler runs:

function authorizeResource(resourceType, action, loadResource) {
  return async (req, res, next) => {
    const resource = await loadResource(req);
    if (!resource) return res.status(404).json({ error: 'Not found' });

    if (!checkPolicy(`${resourceType}:${action}`, req.user, resource)) {
      return res.status(403).json({ error: 'Forbidden' });
    }

    req.resource = resource; // Attach for handler to use
    next();
  };
}

// Usage
app.put('/api/posts/:id',
  authenticate,
  authorizeResource('posts', 'update', (req) => db.posts.findById(req.params.id)),
  async (req, res) => {
    // req.resource is already loaded and authorized
    await db.posts.update(req.resource.id, req.body);
    res.json({ success: true });
  }
);

The handler never touches access control. It only runs if the user is authenticated and authorized for the specific resource.

06

Policy engines for complex systems

For applications with dozens of resource types and complex rules, hand-written policy functions become hard to maintain. Policy engines externalize the rules into a declarative format:

EngineLanguageStyle
Open Policy Agent (OPA)RegoDeclarative rules, widely adopted
CasbinMultiple (Node, Go, Python)Model-based, flexible
CASLJavaScriptAbility-based, integrates with frontend
CerbosYAML policiesAPI-first, language-agnostic

These engines let you define, test, and audit policies without modifying application code. A policy change is a configuration update, not a code deployment.

07

RBACWhat is rbac?Role-Based Access Control - assigning permissions to roles (like admin or editor), then giving users roles instead of individual permissions. vs ABACWhat is abac?Attribute-Based Access Control - an authorization model that makes decisions based on attributes of the user, resource, and environment rather than fixed roles.: when to use which

Most applications do not need to choose one or the other. They use both:

FactorRBACABAC
ComplexitySimple to implement and understandMore complex, more flexible
Best forBroad access tiers (admin/editor/viewer)Resource-level rules (own posts, team documents)
ScalabilityFewer roles = easier managementCan handle fine-grained rules without role explosion
AI supportAI generates basic RBAC reasonably wellAI rarely generates ABAC without explicit prompting
AuditabilityEasy to answer "what can admins do?"Easy to answer "who can access this document?"

The practical approach: start with RBAC for coarse-grained access (who can access which features), then layer ABAC for fine-grained resource-level rules (who can act on which specific records). This hybrid is what most production applications actually use.

AI pitfall
When you ask AI to implement "editors can only edit their own posts," it often adds the ownership check but removes the role check, so now any user who authored a post can edit it regardless of their role. The correct implementation requires both: the user must have the editor role AND be the author. Always verify that ABAC rules are additive to RBAC, not replacements.