advanced
Policy-based and multi-tenant authorization
Keep authorization decisions close to protected resources, centralize reusable policy, and prevent tenant data leaks through scoped queries and tests.
Multi-tenant SaaS must scope every query and mutation by tenant ID from trusted auth context — never from unvalidated client body fields. Policy engines (OPA, Cedar, custom DSL) centralize rules while services enforce them close to data.
// Always bind tenant from session, not request body
async function listOrders(user) {
return db.orders.findMany({
where: { tenantId: user.tenantId },
});
}
// Policy check before action
if (!policy.allow(user, 'invoice:send', invoice)) {
throw forbidden('POLICY_DENIED');
}
Row-level security in PostgreSQL adds a database backstop. Integration tests should include cross-tenant access attempts that must fail.
On interviews: tenant isolation layers (app, ORM scopes, RLS); when to externalize policy vs inline checks; blast radius of a policy bug.
Common pitfalls: tenant ID in JWT without verifying membership; shared cache keys across tenants; background jobs without tenant context.
The trade-off is balancing simplicity, performance, safety, and operability — name which axis you optimized and what cost you accepted.
Checklist:
- Tenant from authenticated identity only.
- Scope ORM queries and cache keys by tenant.
- Test negative cross-tenant cases in CI.
- Version and audit policy changes.