Authorization models (IBAC, RBAC, PBAC, ABAC, ReBAC, ACL, DAC, MAC)
After the user logs in, the system usually issues a cookie, an access token (often a JWT), or some other form of identity data.
At this stage, we move on to authorization — checking what the user is allowed to do.
In ASP.NET, this is traditionally handled through policies, a flexible mechanism that lets you define custom permission rules. But before diving into practice, let’s review theory and discuss the main authorization models: IBAC, RBAC, PBAC, ABAC, ReBAC, ACL, DAC, and MAC.
In case you are new here, there are other articles on this topic.
Authentication theory:
- Authentication history (Basic, Digest, Cookie, Session, Token, JWT, API key, HMAC)
- Authorization models (IBAC, RBAC, PBAC, ABAC, ReBAC, ACL, DAC, MAC)
- Token gang (Bearer token, Reference token, Opaque token, Self-contained token, JWT, Access token, ID token, Refresh token)
- OAuth 2.0 and OpenID in simple terms
- SAML: What Developers Need to Know
How to implement authentication in C# environment:
- Cookie-based Authentication in ASP
- Authorization in ASP
- JWT Authentication
- Identity Server integration with ASP MVC
- Identity Server for microservices
- Identity Server integration with SPA
- SSO with Identity server
You may also check out other stories:
- What is the authentication schema in ASP about?
- Dynamic Authorization Policies in ASP
- Encapsulate authentication with DelegatingHandler
- How to parse token on client side
Meanwhile, we are starting.
Identity-Based Access Control (IBAC)
The most straightforward way to check if a user can perform an action is simply to ask: “Who is this user?” — “Is this Alice?” or “Is this Bob?” Maybe even, “Is this our CEO?” 😁
You might think this is a joke, but it is called Identity-Based Access Control (IBAC).
IBAC grants or denies permissions based on who the user is (Alice, Bob, CEO, etc).
I guess everyone understands the issue:
- scalability. as the number of users grows, managing individual permissions becomes difficult
But it can still be useful if the number of users is small or if you need to set specific exceptions for individual users.
Role-Based Access Control (RBAC)
One of the oldest and simplest approaches would be Role-Based authorization.
In this authorization model we have a list of roles (e.g.
Admin,Employee,User), and each role has a predefined set of allowed actions.
For example, in an e-shop the Admin can configure the entire system and add new products. Employees may edit existing products and update their quantities, but cannot create new ones. A regular User cannot modify product data at all — they can only browse and purchase items.
It’s a clean concept. Easy to understand. Easy to implement. It shines when:
- the system has clear organizational positions (e.g. HR Management system, ERP, CRUD admin panel, E-Shop)
- permissions rarely change (a CRM like Salesforce where standard roles such as Sales Rep and Sales Manager have predefined, stable access patterns)
- users in the same role need identical access
However, despite its simplicity, RBAC has multiple problems:
- multi-tenant limitations. every tenant may need custom roles, causing duplication, inconsistency, and eventual unmanageability
- role explosion. the number of roles grows uncontrollably as the system evolves (“AdminWithExport”, “LimitedManager”, etc.), making the model hard to maintain
- low granularity. roles cannot express fine-grained rules (e.g., “edit only your own items”, “access only department X”), leading to either over-privileged or under-privileged users
- poor flexibility. changes in business logic require creating new roles or restructuring existing ones
- role-permissions coupling. changing one permission affects all users in that role; granular changes are not possible without breaking role semantics
You can find an example of how to implement an RBAC authorization model in C# here.
Permission-Based Authorization (PBAC)
As applications grew more complex, Role-Based authorization quickly showed its limits.
Teams needed finer control — often more granular than what a fixed set of roles could express.
This is where Permission-Based Authorization (PBAC) emerged as the next evolution.
PBAC shifts the focus from who the user is (
Admin,Manager,Employee) to what the user can do by assigning individual permissions (product.read,product.edit,invoice.generate, etc).
It works best when:
- you need fine-grained control over actions and features (e.g., enable/disable
export,approve,delete— like in Jira) - permissions differ between users
- the system is modular, and each module introduces new actions (e.g., in ERP, adding a “Reports” module adds
view/create/exportreport permissions)
This model scales much better than RBAC because you no longer create new roles for every tiny variation. You simply assign or remove individual permissions.
Even though it overcomes some role-based model flows, it also has issues:
- permission dependency chains. some actions require multiple permissions that depend on each other (e.g.,
product.edit+product.publish), and missing just one from the chain leads to confusing behavior, difficult debugging, and inconsistent access rules - permission sprawl. over time the system accumulates dozens or hundreds of permissions (
product.manage.discounts,invoice.send.email,report.export.pdf), making it hard to manage without a centralized permission registry - management overhead. assigning many small permissions manually can become error-prone, especially if your admin UI or IdP does not support grouping or templates
In ASP.NET, a permission-based model is usually implemented with policies.
Attribute-Based Access Control (ABAC)
For more dynamic applications, Permission-Based and Role-Based authorization models started to feel too rigid.
Modern applications often require decisions based not only on who the user is or what permission they have, but also on context — time, environmental conditions, users' age, and more.
This is what Attribute-Based Access Control (ABAC) is about.
ABAC allows you to define rules based on attributes of the user, the resource, the action, location, time, environment, etc.
Instead of expanding roles or assigning dozens of permissions, you define policies such as:
* user can edit an invoice only if they’re a manager and it’s from their department
* a purchase order can be approved only during working hours
* admins can manage only resources belonging to their tenant
ABAC works best when:
- you need dynamic, context-aware decisions (e.g., allow access only during work hours or from a trusted IP)
- the same action may or may not be allowed depending on user
- tenants need custom rule sets
ABAC is incredibly flexible because it supports virtually any rule that can be expressed using attributes. This model scales extremely well in high-complexity domains.
Get iamprovidence’s stories in your inbox
Join Medium for free to get updates from this writer.
However, ABAC also introduces several challenges:
- increased complexity. policies often become hard to understand and debug, especially when they include multiple attributes or nested conditions
- lack of visibility. unlike roles or permissions, ABAC rules cannot always be easily listed, visually displayed to admins, or easily understood
- policy sprawl. as the system grows, policies multiply; without strict governance you end up with overlapping or contradictory rules
- difficult testing. every rule depends on dynamic attributes, requiring many test cases and edge-case scenario validation
- performance considerations. evaluating complex rules may require multiple DB lookups (e.g., fetching resource owner or department), unless cached or preloaded
In ASP.NET, ABAC-like behavior is often implemented using policies or external policy engines (OPA, Cedar, Casbin, PolicyServer) for complex cases.
Relationship-Based Access Control (ReBAC)
As systems became more collaborative and data started belonging to many users simultaneously, even Attribute-Based authorization began to fall short.
Real-world access rules often depend not only on who the user is or what they can do, but how they are related to the resource.
This is where Relationship-Based Access Control (ReBAC) comes into play.
In ReBAC instead of assigning explicit permissions, the system defines authorization based on relationships:
* user is a manager of an employee and can see his profile information
* user is the author of an article and is the one who can edit it
* user can view a project if they belong to the same team
These permissions emerge from the user’s relationship to the resource rather than from a predefined role or permission.
You frequently see ReBAC in collaborative platforms, multi-user document systems, social networks, and products with shared resources. It is often applied when:
- access depends on ownership, hierarchy, or membership
- users collaborate on shared resources (e.g., shared repositories, boards, or documents — like in GitHub or Trello)
- the system resembles a graph (teams, groups, folders, organizations, projects)
Despite its flexibility, ReBAC also introduces its own set of problems:
- relationship complexity. as the system grows, relationships can form deep, making authorization hard to reason about without dedicated tooling
- harder debugging. when access comes from multiple indirect relationships, it becomes difficult to understand why the user has (or does not have) a permission
- modeling difficulty. designing a clean relationship requires careful planning; a poor model leads to inconsistencies and special cases very quickly
Honestly, this is where the boundary between authorization and sensitive business logic blurs for me 😖. Yes, C# policies support resource-based evaluation, but that still means pulling infrastructure concerns into your business layer — and you rarely see teams doing that (probably for good reasons). In most cases, this kind of decision is handled directly in the code. Only when things get truly complex do teams reach for external authorization systems like SpiceDB (Google Zanzibar).
Access Control Lists (ACL)
Relationship is great, but some systems require a more explicit, per-resource permission model.
This is where Access Control Lists (ACLs) come in.
In ACL each resource maintains a list permissions for a user or groups and the actions they are allowed to perform.
Imagine a document management system:
- document 1 is shared with Alice (read/write) and Bob (read-only)
- document 2 is shared with the “HR Managers” group (read/write/delete)
- document 3 is private to Charlie (read/write)
The system simply checks the ACL on the requested resource to see whether the user can perform the requested action.
This approach gives developers fine-grained, per-object control, which is impossible in pure RBAC without creating hundreds of micro-roles.
ACLs are commonly used in collaborative platforms like Google Drive or SharePoint, where every file has its own sharing settings, and work great when:
- each resource has a unique set of users or groups with permissions
- you need to allow exceptions without creating new roles
- permissions must be applied directly to resources rather than inferred from roles or attributes (in HubSpot, you can give a user access to a specific asset)
The obvious issues with this model:
- management overhead. as the number of resources and users grows, maintaining ACLs becomes cumbersome and error-prone
- scalability issues. storing and checking ACLs for millions of resources and users can be expensive, requiring indexing or caching
- limited hierarchy support. ACLs do not naturally express roles, groups, or inheritance; everything must be explicitly assigned
- auditing complexity. tracking who has access across thousands of resources is difficult without dedicated tooling
Discretionary Access Control (DAC)
While ACL focuses on the implementation mechanism: each resource stores a list of users and their permissions, you may need a model that focuses on who owns the resource and lets the owner decide who can access it.
This is about Discretionary Access Control (DAC).
In DAC, each resource typically has an owner, and that owner can assign permissions to other users or groups.
DAC makes sense in systems where:
- resources are user-owned (personal files, shared folders, emails)
- flexibility is more important than strict centralized enforcement
- users need the ability to delegate access (give the user access to the specific document, like in Google Drive)
- collaboration is common, but centralized policies are not required
Despite its flexibility, DAC has several disadvantages:
- risk of privilege escalation. if owners share resources carelessly, unauthorized access may occur
- audit difficulties. tracking who has access to what across many resources can be complex
- lack of central control. administrators cannot enforce global rules easily without overriding user discretion
Mandatory Access Control (MAC)
While previous models are discretionary — where administrators or owners decide who can access a resource — some systems require strict, centralized control over all access decisions.
This is why we have Mandatory Access Control (MAC).
In MAC both users and resources are assigned security labels (e.g.,
Top Secret,Confidential). Access is granted only when the user’s label meets or exceeds the resource’s label, preventing unauthorized information flow.
Imagine a military document management system:
- Alice has
Secretclearance → she can read documents labeledSecretor lower, but cannot readTop Secret - Bob has
Confidentialclearance → he cannot accessSecretorTop Secretdocuments
A process cannot downgrade data to a lower classification (preventing leaks). All access is centrally controlled. Neither users nor administrators can bypass the rules.
MAC is widely used in high-security environments where policy enforcement must be guaranteed, such as government, banking, or military systems.
Despite its strict security, MAC has several limitations:
- rigidity. users cannot override rules even in legitimate cases, making flexibility limited
- complexity. assigning accurate security labels to every user and resource is time-consuming and error-prone
- administration overhead. policies must be centrally managed and audited continuously
- scalability. in large, dynamic environments, maintaining proper labels and rules can become cumbersome
- user experience limitations. because access is strictly enforced, users may be blocked from resources they intuitively need, requiring workarounds or additional processes
Final Thoughts
It’s important not only to understand and use each of these approaches independently, but also to combine them when needed. No matter how clean or theoretically perfect these models appear, in the real world, you will almost never rely on just one of them 😁.
In practice, you will almost always encounter a mix of these models. I’ve seen systems advertised as role-based, but internally they were implemented as a set of fine-grained permissions to simplify future migration. In others, permissions were configured not per user but per custom-defined roles. Some parts of your application may require attribute-based checks, while things like uploaded files might be better handled through relation-based rules or explicit permission lists.
Just make sure you choose the model that fits the specific domain need, keep it consistent, and don’t over-engineer it 🙃.
Let me know in the comments what model your project has💬
Give this article a clap or a few if you like it 👏
You can support me with a link below ☕️
And don’t forget to follow if you are interested in reading about all those authentication ✅







