Skip to content

Engineering · 10 min read

RBAC and Audit Logging for B2B SaaS: Roles, Tenant Isolation and What to Log

Design RBAC and audit logs for a multi-tenant SaaS admin: role models, server-side checks, row level security and what to log.

Zain Khalid MalikZain Khalid MalikCTO & Co-founder, Innovation InsightPublished
Large open-plan office with rows of people working on laptops

Short answer

Model access as tenant, then role, then permission: users belong to a tenant, hold one or more roles, and roles grant named permissions checked on the server for every request. Enforce tenant isolation in the database as well as the code, with PostgreSQL row level security or a database per tenant. Write an append-only audit log of who did what, to which record, from where and when, and keep it at least 12 months if PCI DSS applies.

Why this is the part buyers ask about

Broken access control is first in the OWASP Top 10 for 2025; every application in OWASP's contributed data had some form of it. In a B2B SaaS product it shows up as one customer seeing another's data, a support agent able to change billing, or nobody able to say who deleted a record. In the 30 days to 13 October 2026, the job posts our team logged named multi-tenant 10 times, RBAC 6, admin dashboards 5 and audit logging 4, often in the same brief.

The model: tenant, role, permission

NIST's definition of RBAC is simple: each user is assigned one or more roles, and each role is assigned one or more privileges. For SaaS, add the tenant on top, because a person can be an admin in one customer account and a viewer in another.

ConceptExampleWhere it lives
TenantAcme Ltd (a customer organisation)organisations table; every business row carries organisation_id
Membershipjane@acme.com in Acmememberships table: user_id, organisation_id, status
RoleOwner, Admin, Manager, Agent, Viewer, Billingroles per tenant, or a fixed set shared by all tenants
Permissioninvoices.read, invoices.refund, users.invite, settings.billingNamed strings granted to roles; checked in code
Scope (optional)Manager of the London site onlyRole assignment carries a site, team or project ID
Platform staffYour support and operations teamSeparate roles outside any tenant, with impersonation logged

Check permissions, not role names: code that says can(user, "invoices.refund") survives a new role being added; code that says if role == "admin" does not. Start with five or six fixed roles; let customers define custom roles only when enterprise deals demand it, because custom roles multiply the test matrix.

Enforce on the server, then again in the database

  • Every API request resolves the user, the active tenant and the permissions before any business logic, in one middleware or guard. Hiding a button in the UI is not access control.
  • Every query is scoped by tenant. In code that means a repository layer that adds organisation_id automatically; in the database, PostgreSQL row level security policies that refuse rows from other tenants even if the code forgets.
  • Object-level checks: when a request names a record by ID, confirm the record belongs to the tenant and the user's scope. Insecure direct object references (changing an ID in the URL) are one of the most common access bugs in code audits.
  • Background jobs and webhooks carry the tenant context explicitly; they are where isolation leaks most often.

In our own builds: on Edge OCR, an AI document processing SaaS, RBAC is enforced with resolver-level permission decorators and subscription guards, each organisation has its own database, and a security-log interceptor records every mutation. On an AI body-camera operations platform, ZenStack access policies declared next to the Prisma models scope users, devices and audit logs to a tenant, client or site. On Seek My Service, a services marketplace, route-level guards enforce five roles: Admin, Client, Vendor, Sub-Admin and Logistic Sub-Admin.

Shared schema with RLS or a database per tenant?

Both work; the trade-off is cost and operations against isolation strength. Shared tables with row level security keep one schema to migrate and are cheapest to run; a database per tenant gives the strongest isolation and per-customer backup and restore, at the price of running many databases. Our multi-tenant SaaS architecture guide compares them in detail.

What to put in the audit log

FieldExampleWhy
When2026-10-13T09:41:22ZUTC timestamp from the server, not the client
Whouser_id, plus impersonator_id when support acts as a userAccountability, including your own staff
Tenantorganisation_idCustomers can be given their own log view
Actioninvoice.refunded, user.role_changed, export.downloadedA fixed vocabulary you can filter and alert on
TargetRecord type and IDWhat was affected
ChangeBefore and after values of changed fields, with secrets maskedAnswers "what did it used to say"
ContextIP address, user agent, request IDTies the entry to application logs and security investigations
Outcomesuccess or deniedDenied attempts are often the signal that matters
  • Log security-relevant events at minimum: sign-ins and failures, permission and role changes, invitations, data exports, deletions, billing changes, API key creation and every action taken while impersonating.
  • Make it append-only: application code can insert, never update or delete. Ship a copy to separate storage your application credentials cannot modify.
  • Do not log secrets, full card numbers or unnecessary personal data; mask them in the change field.
  • Give customers a filtered view of their own tenant's log in the admin. Enterprise buyers ask for it, and it reduces support questions.

How long to keep it

If you are in PCI DSS scope, requirement 10.5.1 of version 4.0.1 asks for at least 12 months of audit log history with the most recent three months immediately available for analysis. HIPAA and SOC 2 work usually centres on a documented retention policy that you can show you follow; many B2B products settle on 12 months hot and longer in cold storage. Write your retention period into your security documentation and your contracts so sales, legal and engineering give the same answer. Our PCI DSS compliant development and HIPAA compliant software pages cover the regulated cases.

Testing access control

  • A permission matrix test: for each role and each endpoint, assert allowed or denied. Generate it from the permission list so a new endpoint without a rule fails the build.
  • Cross-tenant tests: create two tenants and prove that every list, detail, update and export endpoint refuses the other tenant's IDs.
  • Database-level tests: with RLS on, run queries as the application role without the tenant setting and confirm nothing comes back.
  • Audit tests: each sensitive action writes exactly one audit entry with the right actor and target.

What it costs

In a new SaaS build, roles, permissions, tenant isolation and an audit log with an admin view usually add two to four weeks to the first release, and are far cheaper then than retrofitted later. Retrofitting them into a product that grew without them is a common first project in a codebase takeover. A multi-tenant SaaS MVP with these foundations sits in our $15k to $45k range; see multi-tenant SaaS development for scope and the SaaS development cost guide for budgets. Need a database specialist for the RLS work? Hire PostgreSQL developers.

Sources

Need a number for your project?

Send a short brief and get a written estimate.

A senior engineer replies within one business day. No sales call required.

Zain Khalid Malik, CTO & Co-founder, Innovation Insight

Zain Khalid Malik

CTO & Co-founder, Innovation Insight

Zain owns architecture, engineering standards and the platform team at Innovation Insight. He sets the bar for code quality, security and the tooling every squad ships with.

LinkedIn
FAQ

Related questions.

What is RBAC in a SaaS product?

Role-based access control: users hold roles within a tenant, roles grant named permissions, and the server checks those permissions on every request. It decides who can see and change what inside each customer account.

Is hiding buttons in the UI enough?

No. The server must check permissions and tenant ownership on every request, because anyone can call the API directly. The UI should mirror the server's rules, not replace them.

Should I use row level security?

In a shared-schema multi-tenant PostgreSQL database, yes, as a second layer under your application checks. It stops a query that forgets the tenant filter from returning another customer's rows.

How long should audit logs be kept?

At least 12 months with three months immediately available if PCI DSS applies. Otherwise set a documented policy, commonly 12 months hot plus longer cold storage, and follow it.