CodamAIDocs
Topicdone

In SINGLE the tenant in the token does not count

In the operating mode SINGLE the tenant in the token is ignored: no gate, no switch. Why this is so and why one switch carries two meanings here.

Variants
SINGLEMULTI

What this is about

The operating mode of an installation is set in codamai.persistence.tenant.mode (environment variable CODAMAI_PERSISTENCE_TENANT_MODE). There are two values:

  • MULTI: Every tenant has its own database. The installation has several customers.
  • SINGLE: There is a single database. The installation has no tenants, usually only one customer, often the operator itself.

Keycloak is often shared by several installations. Its tokens carry organizations and tenant attributes that are meant for the other installations. An installation in SINGLE would never create these tenants. If it checked them anyway, the gate would refuse every request: “tenant unknown”.

SINGLE and MULTI side by side

What CIAS does with the tenant in the token
MULTISINGLE
Resolutionruns, by the six rulesdoes not run, result always "without tenant"
Several organizations without selection403 tenant-unresolvedno error
Tenant gateasks for every tenantnever asks
Tenant in the RequestContextthe resolved keyempty
List of allowed tenantsown tenant, organizations, allowedTenantsempty
Header tenantselection or privileged switchdoes nothing, even with the role
Token without tenant403 tenant-required, except /cias/**admitted
Roles from an organizationapply in the selected tenantdo not apply
Creating tenantsdatabase is set upnothing to set up, ACTIVE right away
Work without a request with a named tenantasks the gateasks the gate anyway

The list of allowed tenants stays empty in SINGLE on purpose. A switch is only possible into a tenant on this list. That way the tenant that was just skipped does not come back by a detour through the header tenant.

What happens to the roles without an organization

In MULTI, roles can be attached to an organization: a person has them only while working in this organization. In SINGLE there is no tenant, so there is also no organization the person is currently working in.

Which roles apply in SINGLE

When: realm_access.roles in the token

Apply as in MULTI.

Result: unchanged

When: resource_access.<client>.roles in the token

Apply. They are not attached to any organization.

Result: These are the roles an installation in SINGLE works with.

When: Roles that the token grants only through a membership in an organization

Do not apply. They belong to a tenant this installation does not have.

Result: If a SINGLE installation needs these roles, they must be granted in Keycloak as global client roles.

Why one switch carries two meanings here

codamai.persistence.tenant.mode actually describes the data storage: one database or many. CIAS reads the same switch to decide how it evaluates tokens. This is intended: whether an installation has tenants is a statement about the whole installation, and a second setting could contradict the first. But you need to know the consequence:

Who reads the switchWhat it does with it
Persistence (CDMS)SINGLE: everything into the one database, no check of the allowed tenants. MULTI: one database per tenant
Setup of new tenantsSINGLE: nothing to set up. MULTI: create and migrate the database
Filter chain (CIAS)SINGLE: skip tenants in the token. MULTI: resolve, admit, refuse without a tenant

So whoever sets an installation to SINGLE because it has only one database also switches off the tenant check of CIAS.

If the switch is missing entirely, that is not SINGLE. Such an installation has no CDMS persistence, for example a CIAS without any data storage for tenants. It evaluates tenants in the token and asks the gate, but does not require every request to have a tenant. Where the CDMS persistence exists, the switch must be set, otherwise the application does not start.

Pitfalls

Where to go next

Sources in the code and the knowledge base
  • CIAS/cias-kernel – TenantRequirement (required, ignoresTenants, codamai.persistence.tenant.mode)
  • CIAS/cias-authentication – TokenParser.admit (tenantless → TenantResolution.none, allowedTenants empty), TenantGate.admit (admitted without tenant), JwtSessionFilter.tenantMissing, EffectiveRoles
  • CIAS/cias-authentication – TenantScope (unchanged)
  • commons-persistence – NoOpTenantProvisioningAdapter, DatabaseRequestContext.resolveTenant
  • CIAS/cias-authentication/docs/adr – ADR-035; CIAS/cias-tenancy/docs/adr – ADR-045
Search