In a B2B application, deciding who can view, modify, or approve a resource affects the product, operations, and risk. A model that is too simple may grant excessive access; one that is overly detailed can be difficult to administer and explain. Choosing between roles and contextual permissions is not about selecting a technical label. It is about representing real business rules in a way that is understandable, verifiable, and sustainable.
The right option depends on how much access varies across users, resources, and situations, and on who will have to manage those differences. Start with real use cases, not a list of controls. Then adopt the simplest model that covers them and define specific signals for when to evolve it.
Authentication and authorization answer different questions

Authentication verifies who a user is, for example, through a session or an identity provider. Authorization determines what that identity can do with a specific resource. Signing in does not imply permission to view every invoice, project, or company record.
To design authorization, describe each decision using four elements: the actor, the action, the resource, and the applicable conditions. For example: “A person with billing permission can download invoices belonging to their organization.” This statement forces you to clarify whether the permission applies to every organization, a selected organization, or only certain documents.
It is also worth defining data scope from the outset. In a product serving multiple customer companies, correctly checking a role is not enough if a query returns resources belonging to another customer. Authorization must cover both the action and access to the resource, including whether it belongs to the relevant scope.
Role-based permissions: clarity when patterns repeat
In role-based access control, or RBAC, permissions are grouped into roles, and roles are assigned to users. A role such as “organization administrator” might allow someone to manage members and settings; another, such as “analyst,” might provide read access to reports. The application checks whether the user’s role includes the permission required for the action.
This approach works well when product responsibilities or job functions recur and differences between users are relatively stable. It makes access easier to explain in the interface, create common profiles, and review assignments. It also gives product, support, and engineering teams a shared vocabulary: it is easier to discuss an “editor” than to maintain an opaque list of individual permissions.
However, roles should not automatically become job-title labels. The same person may have different responsibilities in each organization, and a global role may grant too much access. A role often needs a scope: for example, “administrator” within one organization, not across the entire platform. Define precisely who assigns roles, which resources they apply to, and whether a person can hold multiple roles.
Choose roles when permissions form recognizable groups, change infrequently, and can be managed without creating a new role for every exception. If combinations multiply (“an editor with access to only two projects, except during an approval”), the role may be trying to represent too many dimensions.
Contextual rules: access based on the user, resource, or situation
A contextual rule takes attributes into account in addition to identity or role. Access can depend on who is requesting the action, which resource they want to use, and the conditions that apply. For example, editing a project might be allowed only if the person belongs to the assigned team and the project is in draft status. This approach is often associated with attribute-based access control, or ABAC.
Contextual rules are useful when the same users have different permissions for different resources, or when changes in business status affect what is allowed. They can represent team membership, record ownership, data classification, or an approval stage. This avoids creating a separate role for every user-and-resource combination.
Flexibility comes at a cost: decisions become less visible if they rely on scattered attributes or conditions that are difficult to explain. A rule may fail because a resource’s status is out of date, a value is missing, or behavior for an unknown value has not been defined. For every condition, identify its source, owner, update process, and error handling. If a required value is missing, the policy should safely deny the action.
“Contextual permissions” do not necessarily mean implementing a complex engine. In a small application, they may be an explicit check of team membership and status. What matters is that the rule is consistent, can be centralized and tested, and does not depend on a particular architecture.
How to compare the approaches using real cases
Prepare a small matrix of representative users, actions, and resources. Include ordinary cases and exceptions: a user who belongs to two organizations, a shared resource, a person who changes teams, and an approval action. For each row, record the expected result and the reason. Then compare how difficult each approach makes expressing and managing the rules.
- Variability: Do permissions depend mainly on stable responsibilities, or do they change according to the resource and its status?
- Administration: Can a customer administrator understand and maintain assignments without constant technical support?
- Exceptions: Are they occasional and manageable, or do they recur until they form an informal second role system?
- Auditability: Can you explain why an operation was allowed or denied and reconstruct which rule was applied?
- Error impact: What data could be exposed, or what operation could be blocked, if a condition is configured incorrectly?
Do not compare only the number of roles or rules. A model with few elements can still be difficult to understand if their effects combine implicitly. Also assess the experience of the person configuring access and how easily support can answer the question, “Why can’t this person open this document?”
Signs of excessive complexity and premature complexity
Roles are probably growing excessively when nearly identical names appear for minor variations, every customer requests a dedicated role, or resource-specific conditions are encoded as exceptions within roles. It is also a warning sign if nobody can explain the difference between two profiles without consulting the code.
Conversely, contextual rules may be premature if almost all users share the same permissions, there is no need for resource-level access, and the team lacks reliable data for evaluating attributes. In that situation, building a general policy system adds failure points and maintenance costs without solving a real problem.
Use these signals as reasons to review the model, not as automatic instructions to migrate. Grouping roles, clarifying scopes, or correcting assignments may be enough. If exceptions represent legitimate, recurring differences, then it is worth designing more explicit rules.
Evolving without breaking existing permissions

A gradual evolution reduces surprises. First, inventory current permissions and describe expected cases with examples. Keep authorization policies separate from interface presentation: hiding a button can improve the user experience, but it does not replace checking authorization on the server whenever the action is performed.
- Define a central decision: Establish how to check whether an actor can perform an action on a resource, avoiding contradictory checks scattered throughout the application.
- Write access tests: Cover allowed and denied cases, boundaries between organizations, status changes, and missing attributes. Review both read and write operations.
- Compare before replacing: During a transition, evaluate the new model alongside the old one and record discrepancies without granting additional access based on that comparison.
- Migrate in bounded cases: Move one action or resource type, verify the results with business owners, and keep a controlled way to roll back the change.
- Record relevant decisions: Keep useful information for investigating denials or sensitive access, while avoiding unnecessary personal data in logs.
Before deployment, ask: Who can grant access, and within what scope? What happens when someone is removed from a team? How does the system behave if an attribute is missing? Can one organization access another organization’s resources? Can every exception be explained and tested? If the answers are unclear, the next step is to make the rules more precise, not to add more roles or conditions.
As a practical guideline, start with roles when responsibilities are stable and easy to understand. Add contextual rules when a recurring need depends on resources or circumstances that roles cannot represent well. In either case, prioritize explicit scopes, safe denial, testing, and understandable administration. The best model is the simplest one that reflects real business decisions and can be reviewed as they change.
