Model Your Authorization
Good authorization models start with product workflows, not database tables. Before creating roles, list the decisions your app needs to make:
- Can this user view this page?
- Can this user update this record?
- Can this user invite another user?
- Can this user approve a high-value operation?
- Can this service account call this internal API?
Then map those decisions to principals, resources, actions, and scopes.
Start with tenants
For most SaaS products, tenant scope is the default authorization boundary. A user might be an admin in one tenant and a viewer in another.
Use tenant-scoped role assignments for product access:
{
"principalType": "USER",
"principalId": "user-alice",
"roleId": "role-admin",
"scope": "tenant-acme-prod"
}Use global roles only for platform-level operators or cross-tenant automation.
Keep roles coarse and policies precise
Roles should describe responsibilities:
tenant-adminbilling-managersupport-viewerdevice-operatorauditor
Policies should describe rules:
- Owners can edit their own drafts.
- Invoices over 10,000 require finance approval.
- Production resources cannot be deleted by support.
- Exports are allowed only from corporate IP ranges.
If a role name includes a condition, such as invoice-approver-under-5000, that condition probably belongs in a policy.
Prefer product actions
Avoid modeling API implementation details as permissions. POST /invoices/:id/void may be the route, but the permission should be:
action: void
resource: invoice
service: billingThis keeps authorization stable when your API changes.
Use groups for teams and inheritance
Groups are useful when users need access because of team membership.
Examples:
- Engineering group gets read access to staging resources.
- Finance group can approve invoices.
- A regional support group can view only tenants in its region.
Groups should not replace roles. A group answers "who belongs together?" A role answers "what can they do?"
Use denies for hard stops
Deny rules are powerful because they override allows. Use them for constraints that should win over every other assignment.
Good deny use cases:
- Block destructive actions in production.
- Prevent support users from exporting customer data.
- Stop inherited admin roles from performing billing changes.
Poor deny use cases:
- Replacing normal role design.
- Encoding every exception as a deny.
- Hiding unclear ownership rules.
Plan for change propagation
Role, policy, and hierarchy changes may affect many principals. SaaSLayer Permissions handles permission rebuilds and cascades, but you should still design for understandable blast radius.
Before making broad changes:
- Preview delete impact when available.
- Use dry-run operations where supported.
- Prefer changesets for multi-step updates.
- Watch audit and permission history after rollout.
A good first production model
Start with:
- One organization per customer.
- One tenant per workspace or environment.
- Five to ten roles per tenant.
- Policies only for conditions that roles cannot express.
- Direct grants only for explicit sharing workflows.
- Access requests only for elevated or temporary permissions.
This model is simple enough to explain and strong enough to grow.