SOFTWARE / SYSTEMS / AIEngineering news. Technical depth.
Authorization / 8 MIN READ

Multi-tenant authorization boundaries: beyond a successful login

Design tenant-aware authorization across resource access, permission relationships, caches, background jobs, and database policies.

Evaluate the principal, tenant, action, and resource together at every protected operation. Treat a successful login as identity verification—not permission to access a tenant’s data. OWASP distinguishes authentication, which verifies identity, from authorization, which determines whether an entity may perform a requested action. OWASP Authorization Cheat Sheet

The design goal is a boundary that remains intact when someone changes an object identifier, switches tenants, follows an inherited permission, or executes work through a different application path. Use the following checklist to make that policy explicit, including any deliberate sharing or delegation.

Define the inputs to each authorization decision

Start with four inputs, adding further context when your policy depends on it:

Input Question to answer Recommended source
Principal Who is making the request? Verified authentication or workload identity
Tenant In which tenant is the operation being requested? A selector validated against authoritative membership or delegation
Action What operation is being attempted? The server-side operation being executed
Resource Which object is affected, and which tenant owns it? Authoritative resource metadata

OWASP describes the access-control risk of changing an object identifier supplied through a URL or another request field. Apply that concern to both tenant selectors and resource identifiers: treat a submitted tenant ID as a selector, not proof of access. Resolve it to an authorized tenant context, then verify the target resource’s tenant independently. OWASP Authorization Cheat Sheet

For example, suppose an identity is an administrator in tenant A and a viewer in tenant B. A write to B should be evaluated under B’s permissions, even if the same identity has administrative privileges elsewhere. Selecting A while supplying an object owned by B should not make that object writable.

For tenant-private resources, adopt an explicit rule: require authorized participation in the selected tenant, a resource belonging to that tenant, and permission for the requested action. Deny when those conditions cannot be established. This follows OWASP’s deny-by-default recommendation. OWASP Authorization Cheat Sheet

For creation, authorize the destination tenant or parent container and set ownership from validated context rather than trusting a submitted ownership field. If cross-tenant sharing is required, define a separate permission path rather than weakening the tenant-private rule globally.

Separate identity, protocol authorization, and resource permissions

Ory’s Network documentation illustrates three distinct responsibilities:

  • Identity and user management: Ory documents login, registration, recovery, verification, and profile and account-management flows for its Kratos-based identity service. Ory Kratos documentation
  • OAuth 2.0 and OpenID Connect: Ory describes its Hydra-based service as a headless API. It connects to Ory Identities by default but can work with an existing user management system. Ory Hydra documentation
  • Fine-grained permissions: Ory’s Keto documentation describes relationships such as a user being a document reader, with permission inheritance through groups, roles, and hierarchies. Ory Keto documentation

When composing these responsibilities, document which part of the application enforces each tenant-policy condition. Identify where membership or delegation is validated, where resource ownership is resolved, and which operation consumes the permission decision. Do not use the presence of an identity record or token as a substitute for that authorization design.

Scope permission relationships and protect their mutation

Give tenant-specific roles and groups an unambiguous tenant scope. An administrative role in A should not become a global administrative role merely because the application uses the same role name in B.

Review both direct and inherited access. Keto’s documented inheritance through groups, roles, and hierarchies makes those relationships relevant to a tenant-boundary review—not just the final user-to-resource relationship. Ory Keto documentation

For each path that can grant access, ask:

  • Does the group or role belong to the intended tenant?
  • Can a resource inherit access from a parent in another tenant?
  • Who may add a member, assign a role, or change a parent relationship?
  • What authority is required over the source and destination of an ownership transfer?

Treat permission changes as protected operations themselves. Require appropriate authority before writing membership or access relationships, and validate both endpoints of the proposed relationship.

For intentional sharing, specify the resource, recipient, permitted actions, who may grant access, and how access is revoked. Make broader inheritance an explicit choice rather than an accidental consequence.

Enforce the boundary beyond individual object endpoints

Apply the same design discipline across every delivery and mutation path:

  • Lists and searches: constrain returned objects and any protected counts or summaries by tenant and permission.
  • Bulk operations: authorize the affected resources and define whether one denied item rejects the entire operation or produces a partial result.
  • Exports and file delivery: authorize both creation of the artifact and subsequent access to it under a documented policy.
  • Administrative tools: define privileged access explicitly rather than silently bypassing tenant checks.
  • Background jobs: carry the tenant, initiating principal or delegation, action, and resource references needed to evaluate authority.

For asynchronous work, decide whether the initiating user’s authority must still exist at execution time. If a job must survive that user’s membership removal, define a separate, bounded delegation for that behavior. Do not let “the job was queued earlier” become an undocumented permission rule.

Distinguish an explicit allow decision from an unavailable authorization service. Define an error path that neither releases protected data nor performs a protected mutation merely because the check failed.

Make revocation and cache behavior part of the policy

Design authorization-cache keys around all decision-relevant inputs. Include the tenant alongside the principal, action, and resource identity; include additional context when the policy depends on it. Do not reuse a decision across tenants simply because the user and role label match.

Write down the acceptable delay between a permission change and its enforcement. Apply that requirement to:

  • Membership removal and role changes.
  • Resource ownership transfers.
  • Relationship and policy updates.
  • Cached decisions and requests made through active sessions.
  • Queued jobs and future access to previously generated artifacts.

Choose expiration, invalidation, or re-evaluation mechanisms that meet the requirement. Establish revocation timing for the whole application rather than assuming it from a component’s architecture.

A useful regression scenario is to populate a cache or queue a job, change the relevant authority, and then repeat the protected operation. Evaluate the result against the documented revocation window and any explicit delegation—not merely against whether the original request was allowed.

Use database row security as an additional boundary

PostgreSQL row-level security can restrict which rows normal queries return and which rows data-modification commands may insert, update, or delete. Once row security is enabled, a table with no policy uses default-deny behavior for access subject to row security. PostgreSQL row security documentation

The documented exceptions and policy-combination rules matter:

  • Bypass roles: superusers and roles with BYPASSRLS always bypass row security. Table owners normally bypass it, although FORCE ROW LEVEL SECURITY can subject an owner to it.
  • Whole-table operations: operations such as TRUNCATE are outside row security.
  • Combined policies: applicable permissive policies combine using OR; restrictive policies combine using AND.
  • Integrity checks: referential-integrity checks, including unique, primary-key, and foreign-key checks, bypass row security. PostgreSQL warns that these checks can expose information indirectly.

Review the combined effect of policies and privileges rather than inspecting each policy alone. PostgreSQL row security documentation

If using row security for tenant isolation, test with the actual application database role—not only an administrative role. Review both row visibility and the rows allowed by inserts or updates. Also examine privileged functions and how pooled connections establish and clear tenant context. Keep action-level application permissions explicit alongside database restrictions.

For policies that consult other rows or tables, include concurrent permission changes in the review. PostgreSQL documents race conditions in such policies that can permit information leakage; a sequential allow/deny test alone does not exercise that case. PostgreSQL row security documentation

Test attempts to cross the boundary

Build a regression suite around boundary violations, not just successful requests. The following are proposed outcomes for the tenant-private policy described above, with sharing and delegation tested separately:

Test Expected policy outcome
Same identity, administrator in A and viewer in B B’s permissions govern operations on B
Valid A context with a B object identifier Denied unless an explicit sharing rule allows it
Unauthorized tenant selector with a valid object identifier Denied
Cross-tenant group or relationship assignment Denied unless explicitly authorized
Tenant membership removed while a session remains active Membership-derived access ends within the defined revocation window
Request repeated through caches, bulk APIs, exports, and jobs Each path enforces its documented tenant policy, including any bounded delegation
Authorization dependency unavailable No protected access through an implicit fallback allow

Check both disclosure and mutation, and verify the resulting state—not only the response status. Where sharing is intentional, add positive tests for the permitted action and negative tests for adjacent actions. OWASP identifies unit and integration tests as useful tools for detecting authorization problems, while cautioning that they do not replace manual security testing. OWASP Authorization Cheat Sheet

A useful completion criterion is simple: for every protected operation, the implementation can identify who may do what, to which resource, in which tenant, under which current authority.

SOURCES & REVIEW

Sources are linked throughout this guide. Product capabilities can change; consult the linked documentation for your deployment.

Read our editorial approach ↗