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.