CodamAIDocs
Topicdone

Static and dynamic tenants

A static tenant hangs on a user attribute, a dynamic one on a Keycloak organization. When each one fits.

Variants
STATICDYNAMIC

What this is about

Every tenant in CIAS has a type, in the code TenantType. It does not say what the tenant is in business terms. A customer is a customer, whatever its type. The type only says how Keycloak knows the tenant, and so how it gets into the token:

  • STATIC: Keycloak does not know the tenant at all. The person carries it as the user attribute tenant on their account.
  • DYNAMIC: Keycloak keeps the tenant as an organization. The person is a member of this organization.

The two types side by side

STATIC and DYNAMIC
STATICDYNAMIC
In Keycloaknothingorganization, its alias is the tenant key
How a person belongs to itattribute tenant on the accountmembership in the organization
Where it is in the tokenclaim tenantclaim organization
Tenants per personone, plus switch targets in allowedTenantsany number of memberships
Own roles in the tenantno, the roles of the token applyyes, roles per organization
Field on the CIAS recordexternalOrganizationId emptyexternalOrganizationId = ID of the organization
Typical usea few customers set up once; existing installationsself-registration of companies, people in several companies

The alias of an organization is its short name in Keycloak, for example nordbau. CIAS always creates organizations so that the alias equals the tenant key. That is why the filter chain can read the tenant directly from the token.

What it looks like in the token

Request
STATIC – Anna belongs to the tenant stadtwerke-nord for good

{
  "sub": "3f2a…",
  "tenant": "stadtwerke-nord",
  "allowedTenants": ["stadtwerke-nord"],
  "resource_access": { "cdms": { "roles": ["customer-read"] } }
}
Response
DYNAMIC – Ben is a member of the organization nordbau

{
  "sub": "8c1d…",
  "tenant": "nordbau",
  "allowedTenants": ["nordbau"],
  "organization": {
    "nordbau": {
      "id": "b7e0…",
      "resource_access": { "cdms": { "roles": ["order-edit"] } }
    }
  }
}

With a dynamic tenant, the key is in the token twice: as the organization and in the attribute tenant. This is on purpose. When CIAS assigns a person to a tenant, it always writes tenant and allowedTenants, also for an organization. Both values are the same, so they cannot contradict each other. If the attribute named a different tenant than the organizations, the filter chain would refuse the request, see Determine the tenant of a request.

Roles per type

Which roles apply in the tenant

When: Anna works in the static tenant stadtwerke-nord.

The client roles from resource_access and the realm roles apply. Every role Anna has applies in Anna's tenant. A second tenant with other roles is not possible.

Result: Simple and predictable: one person, one tenant, one set of roles.

When: Ben works in the dynamic tenant nordbau.

If the organization carries its own roles, these replace the global client roles. If it carries none, the global client roles apply. Realm roles always apply, and no organization extends them.

Result: Ben can be an editor in nordbau and only a reader in a second organization.

How the effective roles come about in detail is described in Effective roles: global or in the tenant.

How a tenant gets its type

The type is set when the tenant is created and does not change afterwards.

PathTypeWhat happens in Keycloak
Self-registration of a company (CREATE_NEW)always DYNAMICCIAS creates the organization, the alias is the new key
Admin API POST /cias/admin/tenantsmust be in the call, there is no defaultnothing; for DYNAMIC the call names the ID of an organization that already exists
First tenant at startup (bootstrap)from the configurationnothing

Why the admin API has no default: A dynamic tenant without an organization cannot be determined from any token. And a static tenant that does have an organization would be resolved for the wrong reason. So the call must say which type it means. More in Create and provision a tenant.

Which type fits?

STATIC or DYNAMIC?
People belong to several companies?Roles should differ per company?Companies register themselves?Recommendation
yes––DYNAMIC
–yes–DYNAMIC
––yesDYNAMIC, self-registration only creates this type anyway
nononoSTATIC is enough; one person, one tenant, the roles from the token

Both types may exist side by side in one installation. The type belongs to the tenant, not to the installation.

Pitfalls

Next

Sources in the code and the knowledge base
  • CIAS/cias-kernel – TenantType (STATIC, DYNAMIC), TenantContext (ofStatic, ofOrganization)
  • CIAS/cias-tenancy – Tenant (type, externalOrganizationId), V1__cias_tenant.sql (tenant_type)
  • CIAS/cias-authentication – OrganizationTenantResolver, EffectiveRoles, CiasTokenProperties (tenant, allowedTenants, organization)
  • CIAS/cias-registration – RegistrationService.assignTenant (CREATE_NEW creates DYNAMIC, writeTenantAttributes)
  • CIAS/cias-authentication/docs/adr – ADR-006; CIAS/cias-tenancy/docs/adr – ADR-037
Search