Skip to main content

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]
  1. alice logs into customer-project using her octosystem credentials
  2. The Identity Service finds the OctoTenantIdentityProvider pointing to octosystem
  3. It validates alice's credentials against the parent tenant
  4. It finds an ExternalTenantUserMapping for alice, granting her the Development and DashboardViewer roles
  5. 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

  1. Creates an ExternalTenantUserMapping with all available roles in the target tenant
  2. Adds the mapping to the TenantOwners group (granting all default roles via group inheritance)
  3. 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"
info

When a new child tenant is created, an OctoTenantIdentityProvider pointing to the parent is automatically created. You do not need to create it manually.

note

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 AllowSelfRegistration and DefaultGroupRtId

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:

PropertyDescription
SourceTenantIdThe parent tenant where the user originates
SourceUserIdThe user's ID in the parent tenant
SourceUserNameThe user's name in the parent tenant
RoleIdsRoles assigned in the child tenant
GroupNamesGroups 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:

  1. Login tenant — always included
  2. Home tenant — for cross-tenant users (username starts with xt_), their parent tenant is extracted and included
  3. Ancestor tenants — walks up the tenant hierarchy by following OctoTenantIdentityProvider parent references (up to 10 levels, with cycle detection)
  4. 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.