Multi-Tenant
Scenario
This example creates the same userId and roleId in two scopes. Each subject can read only the resource granted inside its own complete tenant/application scope, proving that IDs are not global authorization identities.
Run
The canonical source is the docs:multi-tenant:start to docs:multi-tenant:end block in examples/multi-tenant.mjs.
First Check the Result
A successful run confirms ok: true, both own-resource checks are true, and both crossTenantResource checks are false.
Source walkthrough
Each scope owns its own manager definition and binding set. A cross-tenant check reads the current subject scope, so it returns false by default.
1. Build tenant A authorization state
Purpose and target. This operation explains scope, roles.create, roles.allow, userRoles.assign in the order the runnable source uses them. It identifies which object is being created, read, projected, or enforced and which output group records the evidence.
State, arguments, and result. The arguments come from trusted scope, role, subject, menu, API, or data state already shown in the source block. Each call either returns its own raw envelope or contributes a selected field to tenantA.
Failure and next step. If validation, revision, source integrity, authentication, or authorization fails, stop at that layer, refresh the trusted state, and rerun the matching operation. Do not widen permissions or bypass the guarded facade to make the example pass.
API reference. See core-and-contexts, roles, user-roles for exact signatures, response wrappers, and public error codes.
2. Build tenant B authorization state
Purpose and target. This operation explains scope, roles.create, roles.allow, userRoles.assign in the order the runnable source uses them. It identifies which object is being created, read, projected, or enforced and which output group records the evidence.
State, arguments, and result. The arguments come from trusted scope, role, subject, menu, API, or data state already shown in the source block. Each call either returns its own raw envelope or contributes a selected field to tenantB.
Failure and next step. If validation, revision, source integrity, authentication, or authorization fails, stop at that layer, refresh the trusted state, and rerun the matching operation. Do not widen permissions or bypass the guarded facade to make the example pass.
API reference. See core-and-contexts, roles, user-roles for exact signatures, response wrappers, and public error codes.
3. Compare own-scope and cross-scope decisions
Purpose and target. This operation explains forSubject, userRoles.getDirect, can in the order the runnable source uses them. It identifies which object is being created, read, projected, or enforced and which output group records the evidence.
State, arguments, and result. The arguments come from trusted scope, role, subject, menu, API, or data state already shown in the source block. Each call either returns its own raw envelope or contributes a selected field to identity, tenantA, tenantB.
Failure and next step. If validation, revision, source integrity, authentication, or authorization fails, stop at that layer, refresh the trusted state, and rerun the matching operation. Do not widen permissions or bypass the guarded facade to make the example pass.
API reference. See core-and-contexts, user-roles for exact signatures, response wrappers, and public error codes.
Expected output
The following JSON is the Example summary output generated by printExample(). It combines selected fields from several API calls and is not the raw response of one method.
identity provenance. This output group is produced by the tenant-decisions walkthrough and should be read together with can. It is a selected, documented example field rather than a new API response shape.
tenantA provenance. This output group is produced by the tenant-state-a walkthrough and should be read together with getDirect. It is a selected, documented example field rather than a new API response shape.
tenantB provenance. This output group is produced by the tenant-state-b walkthrough and should be read together with can. It is a selected, documented example field rather than a new API response shape.
Production boundary
Fixture scopes are fixed test data. Production scopes must come from authenticated server state or a trusted resolver, and business collections must map every active scope dimension through scopeFields.
Related
See Multi-Tenant Model, Authentication Boundary, and Authorized Collection API.