Request isolation flow
Incoming Request
API call with bearer token and target resource
Context Resolved
userId, orgId, and membership extracted from token claims
Tenant Scoped
All downstream queries and mutations bound to orgId boundary
Policy Evaluated
RBAC + tenant policy checked before any data access
Response
Allow / Deny decision with immutable audit event emitted
Isolation proof
Trace a request from edge to response.
Every request follows the same path: resolve tenant context, scope all operations, evaluate policy, emit audit event. There is no shortcut that bypasses isolation.
// 1. Resolve tenant context from the incoming token
const ctx = await authaz.orgs.resolveContext({
token: request.headers.authorization,
});
// 2. ctx is now scoped — userId, orgId, membership, roles
// Every downstream call inherits this boundary.
// 3. Check tenant-scoped policy before data access
const decision = await authaz.rbac.evaluate({
userId: ctx.userId,
orgId: ctx.orgId,
action: "invoices.read",
});
// 4. Audit event emitted automatically
// { actor: ctx.userId, org: ctx.orgId,
// action: "invoices.read", outcome: "allow",
// timestamp: "2026-02-26T14:32:01Z" }
if (!decision.allowed) throw new Error("forbidden");
// 5. Safe to proceed — query is already tenant-scoped
const invoices = await db.invoices.findMany({
where: { orgId: ctx.orgId },
});The orgId on ctx is not a parameter you pass manually. It is resolved from the token and enforced at every layer. There is no code path where a developer accidentally queries across tenant boundaries.
What isolation actually means.
Data boundary
Every query is scoped to the resolved tenant. There is no global read path. Cross-org joins are rejected at the query planner, not at the application layer.
Policy scope
Roles and permissions are evaluated inside the tenant boundary. A user who is admin in Org A has zero implicit privilege in Org B.
Audit trail
Every authorization decision is persisted with actor, organization, resource, and outcome. Traces are immutable and exportable per tenant.
SDK integration
Org context in three lines.
The SDK resolves and enforces tenant scope so your application code never has to build isolation logic from scratch.
Resolve org context
import { authaz } from "@authaz/sdk";
const ctx = await authaz.orgs.resolveContext({
userId,
orgId,
});
// ctx.orgId, ctx.roles, ctx.membership
// All scoped. No global fallback.Tenant-scoped check
const allowed = await authaz.rbac.can({
userId: ctx.userId,
orgId: ctx.orgId,
action: "settings.update",
});
if (!allowed) return deny();Context model
Token-derived, not manual
Cross-org leaks
Blocked at the SDK layer
Audit coverage
Automatic on every decision
Single or multi-tenant. You choose.
Understand the architecture that fits your product, then deploy with confidence.
Single Tenancy
One dedicated instance per customer. Complete data isolation and independent scaling.
- Full data isolation
- Independent scaling
- Custom configurations
- Ideal for enterprise
Multi Tenancy
Shared infrastructure with logical isolation. Cost-efficient and easy to manage at scale.
- Cost-efficient infrastructure
- Centralized management
- Logical data isolation
- Ideal for SaaS platforms
Authaz supports both models. Switch or combine as your product grows.