<img height="1" width="1" style="display:none" src="https://www.facebook.com/tr?id=1063935717132479&amp;ev=PageView&amp;noscript=1 https://www.facebook.com/tr?id=1063935717132479&amp;ev=PageView&amp;noscript=1 "> Authorization Models

Authorization Models

Once a request is authenticated, authorization decides what it may do. This is the 403 half of the AuthN/AuthZ split from lesson one. Two models dominate, and they answer the question at different granularities.

RBAC: Role-Based Access Control

Permissions are grouped into roles; users are assigned roles. The check asks "does this user's role include this permission?"

const rolePermissions = {
  admin:  ["read", "write", "delete"],
  editor: ["read", "write"],
  viewer: ["read"],
};

function roleAllows(user, action) {
  return rolePermissions[user.role]?.includes(action) ?? false;
}

Simple, easy to audit, and enough for most apps. Its limit is granularity: roles describe kinds of users, not relationships to specific resources. "An editor can edit" is easy; "an editor can edit only documents in their own department" forces you to invent ever-narrower roles (editor-marketing, editor-sales), and they multiply fast. That's the classic role explosion .

ABAC: Attribute-Based Access Control

ABAC decides from attributes evaluated at request time: attributes of the user, the resource, and the context (time, location, IP):

// "You can edit a doc if you own it, or you manage the team that owns it."
function canEditDocument(user, doc) {
  return user.id === doc.ownerId
      || (user.dept === doc.dept && user.role === "manager");
}

This expresses rules RBAC can't reach without exploding into special-case roles: ownership, team membership, "business hours only," "same region." The cost is complexity: policies are harder to enumerate and reason about than a flat list of roles.

Underneath, the two models are answering different questions. RBAC asks "who are you?" once; ABAC asks "what's your relationship to this thing, right now?" on every request. That one difference drives every row:

RBAC ABAC
What decides? A role assigned ahead of time Attributes of user, resource, and context, evaluated live
How fine can it cut? Kinds of users: admins, editors, viewers This user, this record, this moment
"Edit your own docs only"? Fight it with ever-narrower roles One comparison: user.id === doc.ownerId
Can you audit who has access? Yes, read the role table Only by evaluating the policy against every user and resource
How does it break down? Role explosion Policy sprawl nobody can reason about

The audit row is the sleeper. "Show me everyone who can delete customer records" is a compliance question you will be asked, and RBAC answers it with a query while ABAC answers it with a research project. That's why the expressiveness of ABAC isn't free even when the policies are simple.

So don't pick one. Use RBAC for the broad strokes, the coarse buckets you want to audit at a glance ("is this an admin?"), and reach for ABAC only where roles genuinely can't express the rule: ownership, team membership, "business hours only." Every ABAC policy you add is auditability you're spending; make sure the rule is worth it.

Authorization challenge · runs in your browser

Wire up the permission policy

A real HTTP server and a demo document app run in your browser. The server is done except for two blank functions in permissions.js: the RBAC check and the full policy that layers ABAC on top.

  1. Implement roleAllows(user, action). Return true only when the user's role in rolePermissions includes the action; fall back to false for unknown roles.
  2. Implement canPerform(user, action, doc). Admins may do anything. If the role allows the action, read is always fine but write/delete are scoped to ownership or the same department. If the role does not allow it, the doc's owner may still read or write their own doc. Otherwise deny.

Start the server, then click Run permission checks in the demo app. When every row matches its expected outcome, the challenge passes. Edit the code and restart the server to try again.

permissions.ts

~15 min

So far everything, login, credential, roles, has lived inside one app. The next lesson breaks that boundary: what happens when the data a user wants to share lives in someone else's system.