Attribute-Based Access Control (ABAC) is a permission system that makes decisions based on attributes of users, resources, and the environment. Instead of asking "Does this role have permission?", ABAC asks "Do the attributes of this request satisfy the policy?"
The Core Concept
ABAC evaluates permissions using four categories of attributes:
| Category | Description | Examples |
|---|---|---|
| Subject | Who is making the request | user.role, user.department, user.id |
| Resource | What is being accessed | document.status, document.creatorId, document.isLocked |
| Action | What operation is requested | read, create, update, delete |
| Environment | Contextual factors | Time of day, IP address, device type |
How ABAC Differs from RBAC
RBAC: User → Role → Permission
ABAC: (Subject + Resource + Action + Environment) → Policy → Decision
In RBAC, you ask: "Does the admin role have the document:update permission?"
In ABAC, you ask: "Can a user with role=editor and department=Engineering update a document with status=draft and isLocked=false?"
A Simple ABAC Policy
Here's what an ABAC policy looks like conceptually:
// "Authors can edit their own unlocked draft documents"
{
action: "update",
resource: "document",
conditions: {
"user.role": "author",
"document.creatorId": "user.id", // Dynamic comparison!
"document.isLocked": false,
"document.status": "draft"
}
}
Why ABAC Solves Our Problems
Remember the permissions that broke our RBAC system?
| Requirement | RBAC Approach | ABAC Approach |
|---|---|---|
| "Edit own documents" | Create document:update:own permission + manual check |
condition: { creatorId: user.id } |
| "Edit unlocked only" | Create document:update:unlocked permission + manual check |
condition: { isLocked: false } |
| "View non-drafts" | Create document:read:non-draft permission + manual check |
condition: { status: { $ne: "draft" } } |
ABAC expresses these naturally as conditions, not encoded permission strings.
ABAC in Code
Here's a simplified ABAC implementation:
function getUserPermissions(user: User) {
const builder = new PermissionBuilder()
if (user.role === "author") {
builder
.allow("document", "read", { status: "published" })
.allow("document", "read", { status: "archived" })
.allow("document", "read", { status: "draft", creatorId: user.id })
.allow("document", "create")
.allow("document", "update", {
creatorId: user.id,
isLocked: false,
status: "draft",
})
}
return builder.build()
}
// Usage
const permissions = getUserPermissions(user)
permissions.can("document", "update", document) // Checks all conditions!
Implementation
Let's implement a simple ABAC system into our project. This system will follow the exact same policies of our current RBAC system.
The only additional changes we will make are adding a few database optimizations and finally ensuring users can only create/edit documents within projects they have access to.
Checkpoint
We just finished implementing most of the ABAC permissions file, so now it is your turn to define the remaining permissions and use them in the code.
You can checkout this branch to follow along from where we are:
git checkout 5.5-basic-abac-checkpoint
What We Gain
| Benefit | Description |
|---|---|
| Unified API | One can() function for all checks |
| Declarative policies | Conditions describe what's allowed |
| No helper functions | No more canUpdateDocument, canReadProject, etc. |
| Type safety | TypeScript enforces valid resources, actions, and conditions |
| Composable | Easy to add new conditions without new permission strings |
| Database Optimizations | Caching saves repeated queries and improves performance |
| Document Policies | Documents are correctly limited to the user's access |
Branch Checkpoint
After completing this conversion, your code should match:
Branch: 6-basic-abac
Run the following to sync up:
git checkout 6-basic-abac
What's Next
This basic ABAC system has the same functionality as our RBAC + helpers approach, just with cleaner architecture. In the next lessons, we'll add advanced features that ABAC makes possible like field-level permissions and automatic query filtering.