What this is about
Normally a request works in the tenant from the token. Sometimes someone has to work in another tenant on purpose: a person who belongs to two companies, or support staff helping a customer. For this the client sends the tenant header with the desired tenant key:
POST /api/rest/crm/customer/query
Authorization: Bearer <token>
tenant: globex
{ "response": ["id", "name"] }The search runs in the database of globex,
provided the switch is allowed.The header is not trustworthy, because any client can set it. It is therefore only a wish. Whether it takes effect is decided by information from the signed token.
There are two kinds of switch:
- Selection: The person is a member of the target tenant, that is of this organization. No special role is needed for that.
- Privileged switch: The person is not a member of the target tenant. This needs the realm role
allowed-tenant-context-switchand the target must be in the person’s list of allowed tenants. A realm role is a role in Keycloak that applies to the whole platform, not just to one application.
The decision table
| Target is an own organization | Target in the list of allowed tenants | Realm role allowed-tenant-context-switch | Target is served | Result |
|---|---|---|---|---|
| yes | – | – | yes | request runs in the target tenant (selection) |
| no | yes | yes | yes | request runs in the target tenant (privileged switch) |
| no | yes | no | – | header is silently ignored, request runs in the own tenant |
| no | no | – | – | no switch; the first access to a tenant model ends with 403 CDMS_TENANT_SWITCH_NOT_AUTHORIZED |
| – | yes | yes | no | 403 cias.authentication.tenant-not-served |
”–” means: does not matter in this row.
The flow
sequenceDiagram
participant C as Client
participant F as CIAS filter chain
participant G as Tenant gate (CIAS)
participant P as CDMS persistence
participant DB as Database globex
C->>F: Request with token (tenant acme) and header tenant: globex
F->>F: Determine tenant from the token: acme
F->>G: Is acme served?
G-->>F: yes
F->>F: Role allowed-tenant-context-switch? globex allowed?
F->>F: Tenant of the request := globex
F->>G: Is globex served?
G-->>F: yes
F->>P: Pass the request on
P->>P: globex in the list of allowed tenants? yes
P->>DB: read and write
P-->>C: Response
Two checks stand out:
- CIAS asks the tenant gate twice: once for the tenant from the token and, after the switch, once more for the target. A suspended tenant stays suspended even for someone who switches into it, administrators included.
- The persistence checks the list of allowed tenants a second time, independently of CIAS. Two separate checks protect the same fact.
Variants
When: The person is a member of the organizations acme and globex and sends tenant: globex.
-
1CIASrecognizes
globexas an own organization and selects it -
2CIASdetermines roles and attributes for
globex -
3CDMS→Databaseworks in the database
globex
Result: No special role needed. In globex the person has exactly the roles they have there as a member.
When: A support person with tenant acme, role allowed-tenant-context-switch and globex in the attribute allowedTenants sends tenant: globex.
-
1CIASdetermines
acmefrom the token, gate says yes -
2CIASrole present, target allowed → tenant of the request becomes
globex -
3CIASasks the gate for
globex: yes -
4CDMS→Databaseworks in the database
globex
Result: The person takes their own roles and attributes along. They do not get roles that someone has in globex.
When: globex is in the list of allowed tenants, the role is missing.
-
1CIASrole missing → the wish is not applied
-
2CDMS→Databaseworks in the database
acme
Result: No error, no hint in the response. The data comes from the own tenant.
When: globex is not in the list of allowed tenants, with or without the role.
-
1CIAStarget not allowed → the wish is not applied
-
2CDMSsees the refused wish on the first access to a tenant model
-
3CDMS403
CDMS_TENANT_SWITCH_NOT_AUTHORIZED
Result: If the request only accesses system models, there is no error, because they need no tenant.
When: The switch would be allowed, but globex is suspended, closed or unknown.
-
1CIASswitches to
globexand asks the gate -
2CIAS→Client403
cias.authentication.tenant-not-served
Result: The request does not reach CDMS. You manage a suspended tenant through the CIAS administration API, not through the switch.
When: CODAMAI_PERSISTENCE_TENANT_MODE=SINGLE
There are no tenants and the list of allowed tenants is empty. The tenant header does nothing, not even with the role, and does not lead to an error either.
Result: Everything stays in the one database.
Pitfalls
Where to go next
- Working on behalf of another person: User switch by header
- When a tenant is served: Is the tenant served?
- The CIAS view: Switching between tenants
- An example from start to finish: Support looks into a tenant