Skip to content
AB

Multi-Tenant Authorization Requires More Than Checking a JWT

Authentication proves identity. Safe multi-tenant APIs must also scope every resource query and action to the caller's organization.

Ashish BhakhandPublished

A valid JWT tells me who made a request. It does not prove that person may read project abc, invite members to an organization, or edit another tenant's data.

This distinction is the foundation of multi-tenant authorization:

  • authentication establishes identity;
  • authorization decides whether that identity may perform this action on this resource.

Scope the lookup itself#

A dangerous flow loads a resource by its global ID and checks permissions later. It is easy to forget the second step in one handler.

Prefer repository operations that include tenant scope:

Code
const project = await projects.findByIdForOrganization(
  input.projectId,
  actor.organizationId,
);

if (!project) throw new ProjectNotFoundError();

The query should include both values:

Code
collection.findOne({
  _id: new ObjectId(projectId),
  organizationId: new ObjectId(organizationId),
});

This reduces accidental cross-tenant reads and avoids revealing whether another organization's resource exists.

Authorize actions, not routes#

Roles are inputs to a decision, not the decision itself. “Admin” may invite members but might not transfer ownership. A member may edit their own profile but not someone else's.

Code
policy.assertCan(actor, "member.invite", organization);

A policy function can consider role, organization membership, resource state, and ownership. Keep this rule near application logic so worker and HTTP entry points use the same decision.

Do not trust tenant IDs from the body#

Derive tenant context from verified identity or a membership lookup. If a route contains an organization ID, confirm membership before using it. Never accept organizationId in a request body and treat it as authority.

For administrative endpoints, require both a suitable role and the target organization scope. Global support access should be explicit, narrowly granted, and audited.

Test the negative matrix#

Test the permitted case, wrong organization, insufficient role, missing membership, and a resource that does not exist. The cross-tenant case is especially important because ordinary happy-path tests never expose it.

JWT verification is only the first gate. Multi-tenant safety comes from carrying trusted tenant context through the application and including it in every resource decision and persistence query.

← Back to all writing