Cross-Tenant Authentication
OctoMesh supports a hierarchical tenant model where a parent tenant can authenticate users in child tenants. This allows organizations to maintain a single user directory while granting access to multiple isolated tenants.
How It Works
Parent Tenant (e.g., "octosystem")
├── User: alice (local user)
├── User: bob (local user)
│
└── Child Tenant (e.g., "customer-project")
├── OctoTenantIdentityProvider → points to "octosystem"
├── ExternalTenantUserMapping: alice → roles: [Development, DashboardViewer]
└── ExternalTenantUserMapping: bob → roles: [TenantManagement]
- alice logs into
customer-projectusing heroctosystemcredentials - The Identity Service finds the OctoTenantIdentityProvider pointing to
octosystem - It validates alice's credentials against the parent tenant
- It finds an ExternalTenantUserMapping for alice, granting her the
DevelopmentandDashboardViewerroles - A JWT token is issued with
tenant_id: customer-project,home_tenant_id: octosystem, and the mapped roles
Two things are required for cross-tenant authentication to work:
- An OctoTenantIdentityProvider in the child tenant pointing to the parent (created automatically when the child tenant is created)
- An ExternalTenantUserMapping for each user that should have access (created via admin provisioning or manually)
Granting Access to a Child Tenant
This is the most common scenario: you are logged into the parent tenant (e.g., octosystem) and want to grant yourself or another user access to a child tenant (e.g., meshtest). You do not need access to the child tenant to do this.
Quick Start: Grant Yourself Access
# 1. Log into the parent (system) tenant
octo-cli -c Config -tid "octosystem"
octo-cli -c LogIn -i
# 2. Provision yourself in the child tenant (grants all roles + TenantOwners group)
octo-cli -c ProvisionCurrentUser -ttid "meshtest"
# 3. Log into the child tenant (token now includes meshtest in allowed_tenants)
octo-cli -c Config -tid "meshtest"
octo-cli -c LogIn -i
That's it. You now have full access to meshtest.
What ProvisionCurrentUser Does
- Creates an ExternalTenantUserMapping with all available roles in the target tenant
- Adds the mapping to the TenantOwners group (granting all default roles via group inheritance)
- If the target tenant is still initializing (CK model not loaded), retries automatically
Grant a Specific User Access
To provision another user with selected roles:
# Still logged into the system tenant
octo-cli -c CreateAdminProvisioningMapping \
-ttid "meshtest" \
-stid "octosystem" \
-suid "<user-id>" \
-sun "alice" \
-rids "Development,DashboardViewer"
The user must log out and log back in to receive the updated allowed_tenants claim.
List and Delete Provisioned Users
# List all provisioned users in target tenant
octo-cli -c GetAdminProvisioningMappings -ttid "meshtest"
# Delete a provisioning
octo-cli -c DeleteAdminProvisioningMapping -ttid "meshtest" -mid "<mapping-rtid>"
Complete Workflow: New Tenant from Scratch
# 1. Log into the system tenant
octo-cli -c Config -tid "octosystem"
octo-cli -c LogIn -i
# 2. Create the child tenant
octo-cli -c Create -tid "new-project" -db "new_project_db"
# 3. Provision yourself (no access to child tenant needed)
octo-cli -c ProvisionCurrentUser -ttid "new-project"
# 4. Log into the new tenant
octo-cli -c Config -tid "new-project"
octo-cli -c LogIn -i
# 5. Optionally provision other users
octo-cli -c CreateAdminProvisioningMapping \
-ttid "new-project" \
-stid "octosystem" \
-suid "<bob-user-id>" \
-sun "bob" \
-rids "DashboardViewer,ReportingViewer"
When a new child tenant is created, an OctoTenantIdentityProvider pointing to the parent is automatically created. You do not need to create it manually.
Until at least one user is provisioned in a child tenant, the login page displays "This tenant is not available. Please contact your administrator." — the login form is not shown.
Key Concepts
OctoTenant Identity Provider
An OctoTenant identity provider delegates authentication to a parent tenant. When a user tries to log in and no local user is found, the Identity Service walks up the tenant hierarchy (via OctoTenantIdentityProviders) to find and authenticate the user.
- The hierarchy supports up to 10 levels of depth
- Cycle detection prevents infinite loops
- The provider can be configured with
AllowSelfRegistrationandDefaultGroupRtId
External Tenant User Mappings
An external tenant user mapping links a user from a parent tenant to a set of roles (and optionally groups) in the child tenant. Without a mapping, an authenticated cross-tenant user has no roles in the child tenant.
Each mapping contains:
| Property | Description |
|---|---|
| SourceTenantId | The parent tenant where the user originates |
| SourceUserId | The user's ID in the parent tenant |
| SourceUserName | The user's name in the parent tenant |
| RoleIds | Roles assigned in the child tenant |
| GroupNames | Groups the user belongs to in the child tenant |
Cross-Tenant User Prefix
When a cross-tenant user logs in for the first time, the Identity Service creates a local user record in the child tenant with the prefix xt_{parentTenantId}_{username}. This local record is linked to the external tenant user mapping.
Managing Mappings in the Child Tenant
Once you have access to the child tenant, you can also manage user mappings directly from within that tenant. This gives you more granular control than admin provisioning.
Create a Mapping
# Switch to the child tenant context
octo-cli -c Config -tid "customer-project"
octo-cli -c LogIn -i
# Create a mapping for a parent tenant user
octo-cli -c CreateExternalTenantUserMapping \
-stid "octosystem" \
-suid "<user-id-from-parent>" \
-sun "alice" \
-rids "Development,DashboardViewer"
List and Delete Mappings
# List all mappings
octo-cli -c GetExternalTenantUserMappings
# Filter by source tenant
octo-cli -c GetExternalTenantUserMappings -stid "octosystem"
# Delete a mapping
octo-cli -c DeleteExternalTenantUserMapping -id "<mapping-rtid>"
Manually Create an OctoTenant Identity Provider
This is only necessary if you need to point a child tenant to a different parent than the one it was created from, or if the provider was deleted:
octo-cli -c AddOctoTenantIdentityProvider \
-n "Parent Tenant" \
-ptid "octosystem" \
-e true
Allowed Tenants Resolution
At token issuance, the Identity Service resolves which tenants a user may access. The algorithm has four phases:
- Login tenant — always included
- Home tenant — for cross-tenant users (username starts with
xt_), their parent tenant is extracted and included - Ancestor tenants — walks up the tenant hierarchy by following OctoTenantIdentityProvider parent references (up to 10 levels, with cycle detection)
- Descendant tenants — BFS traversal down through child tenants, checking for ExternalTenantUserMapping entries matching the user. This cascades through multiple levels (e.g.,
octosystem → customer-project → sub-project)
The resulting list is emitted as allowed_tenants claims in the JWT access token. Services validate these claims to authorize tenant access.
Example
octosystem (parent)
├── customer-project (child)
│ └── sub-project (grandchild)
When user alice from octosystem has an ExternalTenantUserMapping in customer-project, and her cross-tenant identity xt_octosystem_alice has a mapping in sub-project, her allowed_tenants will include all three tenants.