Direct answer
Multi-tenant means every query is scoped to a workspace the caller belongs to. A role name in the JWT is not enough. If a user can change an ID in the URL and see another firm’s file, you do not have a portal.
OWASP’s authorization cheat sheet prefers explicit checks on every request and warns that sprawling role names become untestable. Start small.
The three layers you actually need
Tenant: which workspace. Role: owner vs member vs operator. Object: this record, not ‘all invoices’. Hide the control in the UI and still enforce it on the server.
Forms need visible labels and errors that do not leak whether another tenant’s object exists. WebAIM’s form guidance still applies inside authenticated apps.
A practical sequence
Ship the checks before the theme.
- Put workspace_id on every row that is not global
- Resolve membership before the handler runs
- Deny by default when membership is missing or the store is down
- Test IDOR: swap IDs between two seeded workspaces
- Log allow and deny without file contents or tokens
Invites, offboarding, and leftovers
Most leaks are people, not crypto. Invites must expire. Offboarding must revoke sessions. Shared ‘team passwords’ are not a tenant model.
Do not leave staging copies of production objects on a public host. Indexing rules for /login and /app belong in robots and noindex, not in hope.
What to send if you want a senior review
Share how many workspaces, which roles, and one example of data that must never cross tenants. Related work at Kiwi: SaaS MVP design and development.
Frequently asked questions
Do we need ABAC on day one?
Usually no. Owner/member/operator plus workspace scope is enough for a first portal. Add attributes when a job cannot be expressed as a role without lying.
Should the marketing site and the portal share a login?
They can share a design system. Login, cookies, and robots rules should be explicit. Public pages stay indexable; app routes stay out of the sitemap.
What is the smallest useful test?
Two workspaces, two users, one object. If user A can read user B’s object by guessing an ID, the portal is not ready.
Sources
- OWASP — Enforce access controls
- OWASP Cheat Sheet Series — Authorization
- WebAIM — Creating accessible forms
Figures cited above are drawn from the linked publications and are the responsibility of their sources; we date and scope them rather than presenting them as universal guarantees.