
SaaS customers ask for roles before they ask for features: who can pay, who can only view, who can invite. Role-based access control (RBAC) answers that with a fixed set of permissions and a small set of roles that bundle them. The design fails when every enterprise deal adds a one-off role in code.
Build permissions as data, scoped to the tenant, and check them on the server for every action.
Permission. A single action on a resource: invoice.read, invoice.refund, member.invite, payout.change. Name them after what the user does, not after a screen.
Role. A named bundle of permissions: Owner, Admin, Finance, Member, Read-only. Customers understand roles. Your code understands permissions.
Assignment. A user has a role inside one tenant. The same person can be Finance in one customer and Read-only in another. The assignment always carries the tenant id.
Request arrives with user + tenant
↓
Load that user’s role in that tenant
↓
Resolve the role to permissions
↓
Action requires one permission
↓
Allow or deny, and log the decision for sensitive actions
If the check only hides a button, the API is still open. Enforce the permission where the action runs.
Ship four or five system roles that match how customers actually work. Let an Owner invite people and assign those roles. Add a custom role later, as a tenant-defined bundle of the same permissions, if deals require it.
Custom roles are a product feature, not a column someone edits in production. The tenant admin picks permissions from your list. They cannot invent ledger.write if you never offered it.
Avoid a role called “Super Admin” that skips tenant checks. Your own support staff use a separate, audited break-glass path. They are not a role inside the customer’s account.
Tenant on every check. A Finance role in tenant A never reads tenant B. The permission and the tenant id are one decision.
Owner protection. A tenant always has at least one Owner. The last Owner cannot demote themselves. Billing and close-account sit on Owner, not on a role a member can grant themselves.
Sensitive permissions are narrow. Changing a payout destination, exporting personal data, and managing API keys are their own permissions. They do not hide inside a broad “Admin” that also edits profile photos, unless you mean for every admin to do those things.
Deny by default. A new endpoint with no permission requirement is a bug. Code review asks which permission guards it.
Cache carefully. Cache the role for the session, and invalidate it when an admin changes that user’s role. A stale admin token after removal is a common incident.
Audit grants. Who assigned Finance, and when. You will need it when a customer asks why a contractor could download invoices.
They will ask for “this person, but not refunds,” then for SSO group mapping, then for a reviewer who can see a payout but not release it. Design the permission list so those requests are a new bundle or a second person on the action, not a fork of the authorization code.
If a single permission is no longer enough—for example “refund only under a limit” or “only records in my branch”—that is a step beyond plain RBAC. Add it as a condition on a specific permission when a real customer needs it. Do not replace the whole model on the first enterprise call.
RBAC for SaaS is a short permission list, a few roles per tenant, and a server-side check on every action. Keep Owner protection and payout-level permissions explicit. Custom roles, when you add them, only bundle permissions you already defined.
At KIS Technology, we help businesses transform their ideas into secure and scalable digital solutions through software development, API integration, and quality engineering.
Build smarter. Build securely. Build for growth.
Connect with our Founder: