Portal Community

How Tenant Scoping Works

Every IPassportClient request includes a TenantId field. This is always set to ctx.TenantId by the executor — it is not configurable in node config and cannot be overridden by an expression. Passport rejects any query that resolves a user outside the specified tenant.

// Executors always pass ctx.TenantId:
var request = new UserLookupRequest
{
    LookupBy = config.LookupBy,
    Value = lookupValue,
    TenantId = ctx.TenantId   // from execution context — not from config
};
var user = await _passport.LookupUserAsync(request, ct);

What Tenant Scoping Prevents

ScenarioResult
Tenant A workflow queries Tenant B user emailPassport returns null — user not found in tenant scope
Expression attempts to pass cross-tenant userIdPassport returns null or 404 — no error leakage
RoleMembers query for role in another tenantReturns empty members list
PermissionCheck for cross-tenant user IDReturns false — permission denied

Sub-Workflow Tenant Inheritance

Sub-workflows always inherit the parent execution's tenantId. There is no mechanism to execute a child workflow in a different tenant. This applies to both sync and async sub-workflow modes.

No opt-out: Tenant scoping on identity queries is not configurable and cannot be disabled — not by node config, expressions, or executor code. Any attempt to resolve cross-tenant identity returns a negative result, not an error. This is a deliberate security design: cross-tenant queries fail silently to prevent tenant enumeration attacks.