CodamAIDocs
Topicdone

One person in two tenants

A consultant works for two customers. How she chooses between the tenants, which roles apply in each and which attribute values apply to her.

Variants
two organizations (selection)static tenant with allowedTenants (privileged switch)roles per tenantattribute values per tenantno selection sent

What this is about

Clara consults for two customers: nordbau and suedlogistik. She has one account and one password. Still, none of her requests may mix the data of the two customers.

CIAS solves that by having every single request run under exactly one tenant. Which one it is, is decided afresh before every request – and the roles and the attribute values that apply to that one request hang off it.

The picture

flowchart TB
    K["One account in Keycloak<br/>clara@example.example"]
    K --> T["One token:<br/>organization: nordbau, suedlogistik<br/>roles per organization<br/>realm roles<br/>profile attributes"]
    T --> R{"Which tenant<br/>for this request?"}
    R -- "header tenant: nordbau" --> N["request in nordbau<br/>roles from nordbau<br/>values for nordbau"]
    R -- "header tenant: suedlogistik" --> S["request in suedlogistik<br/>roles from suedlogistik<br/>values for suedlogistik"]
    N --> ND[("database nordbau")]
    S --> SD[("database suedlogistik")]

How Clara ends up in two tenants

There are two shapes, and they behave differently:

Two ways into two tenants
Two organizationsOne static tenant, more allowed
How she belongsmember of both organizationsattribute tenant names her tenant, allowedTenants names further targets
Where it sits in the tokenclaim organization with both aliasesclaims tenant and allowedTenants
What she has to do to changesend the header tenant – no special role neededthe header tenant and the realm role allowed-tenant-context-switch
Which roles applythe roles of her membership in the chosen tenanther own roles – she carries them into the target tenant
How she got thereone invitation per tenant, which she redeemed herselfan administrator set the attributes

The first is called selection, the second a privileged switch. The difference is not cosmetic: selection happens during tenant resolution, the switch only after it. And the roles are determined in between.

How the choice is made

The filter chain reads the header tenant before the token, because the chosen organization decides which roles apply. Then it works through the rules in order.

Which tenant, which roles, which attribute values?
organizations in the tokenheader tenantrealm role allowed-tenant-context-switchResult
nordbau, suedlogistiksuedlogistik–selection: request in suedlogistik, with Clara's roles there and the values CIAS holds for her in suedlogistik
nordbau, suedlogistikmissing–403 cias.authentication.tenant-unresolved – with two organizations and no choice it is unclear whose data is meant
only nordbaumissing–request in nordbau, there is nothing to choose
none, attribute tenant = nordbausuedlogistik, and it is in allowedTenantspresentprivileged switch: request in suedlogistik, but with her own roles from nordbau
none, attribute tenant = nordbausuedlogistikmissingsilently ignored: the request runs in nordbau, without an error and without a hint
nordbau, suedlogistik––target suspended, closed or unknown: 403 cias.authentication.tenant-not-served

Every rule in detail: Determine the tenant of a request and Switch between tenants.

Which roles apply in each tenant

Roles can sit in two places in the token: globally and inside an organization. From those CIAS builds the effective roles on every request.

Token
In Clara's token:

global (resource_access):      report-read
organization nordbau:          order-read, order-edit
organization suedlogistik:     order-read
realm_access:                  user
Effective roles
Effective in nordbau:          order-read, order-edit   (+ realm: user)
Effective in suedlogistik:     order-read               (+ realm: user)

report-read falls away in both tenants. That is deliberate: if Clara has any role in her active, dynamic tenant, the roles of that tenant replace the global client roles. Nobody granted a globally granted role for this one customer, so it does not apply there. Realm roles, by contrast, always apply, in every tenant.

With a privileged switch it works differently: there Clara carries her own roles into the target. Roles somebody has in the target tenant she does not get. Details: Effective roles: global or in the tenant.

Which attribute values apply in each tenant

An attribute is a fact about a person that reaches the token as a claim and that CDMS uses to filter rows. On declaration, every module says what the attribute is about:

BindingMeaningWhere the value sits
USERone value per person, the same in every tenanton the account in Keycloak, arrives through the token
USER_IN_TENANTone value per person and tenantin CIAS, per person and tenant

For an attribute with USER_IN_TENANT, CIAS asks on every request for the values it holds for Clara in the active tenant.

How the effective value comes about
  1. 1
    CIAS
    reads the attributes from the token, as they stand on the account
  2. 2
    CIAS
    fetches the values it holds for this person in this tenant
  3. 3
    CIAS
    for every key it holds values for, those replace the token's value. They are never merged
  4. 4
    CIAS
    where CIAS holds no values for a key in this tenant, the token's value stays
  5. 5
    CDMS
    filters the rows with whatever ends up in the RequestContext
    Result: Same person, same token, different rows per tenant
Values
On the account (token): region = nord, sued
In CIAS, nordbau:       region = nord
In CIAS, suedlogistik:  region = sued
Effect in the filter
Effective in nordbau:      region = nord
Effective in suedlogistik: region = sued

If CIAS cannot fetch the values and has no buffered value either, it refuses the request instead of falling back to the token’s value: 403 cias.authentication.tenant-not-served. CIAS keeps the answers per person and tenant for 30 seconds.

What does not differ per tenant

WhatWhy
account and passwordthe address is the account. There is exactly one
realm rolesthey apply to the whole platform. No organization can add to them
groupsgroup membership hangs off the account, not off the tenant
profile attributes (USER)one value per person, the same in every tenant
home tenant in the user recordCIAS keeps one home tenant per person. Further memberships live in Keycloak and arrive through the token
language (locale)a preference of the person

The home tenant in the CIAS user record is therefore no statement about which tenant a request runs in. That is always decided by the token. See The user record.

The path of a request, shortened

Clara's request with header tenant = suedlogistik
  1. CIAS
    Read the header
    The header is read before the token, because it co-decides the roles
  2. CIAS
    Resolve
    Is suedlogistik one of her own organizations? Then it is the tenant of the request
    ↳ no 403 cias.authentication.tenant-unresolved
  3. CIAS
    Tenant gate
    Is suedlogistik served, that is ACTIVE and inside its validity window?
    ↳ no 403 cias.authentication.tenant-not-served
  4. CIAS
    Roles and attributes
    roles from the organization suedlogistik, attribute values for suedlogistik
  5. CDMS
    Permission check
    Does an effective role allow this model?
    ↳ no 403
  6. CDMS
    Database
    In MULTI, the database of suedlogistik
  7. Rows from suedlogistik, filtered with Clara's values for suedlogistik

The full path is described by From login to the data.

Watch out

Next

Sources in the code and the knowledge base
  • CIAS/cias-authentication – OrganizationTenantResolver, TenantResolution, TokenParser.admit (order: resolution, gate, roles, attributes, context, switch, second gate), buildAllowedTenants, ContextSwitch, TenantGate
  • CIAS/cias-authentication – EffectiveRoles.resolve, EffectiveAttributes.resolve, AttributeLookup, KeycloakOrganizationClaimReader
  • CIAS/cias-user – TenantBoundAttributeService, TenantBoundAttributeReader (cias_user_attribute.tenant_key), UserService.record (homeTenantKey)
  • CIAS/cias-registration – RegistrationService.assignTenant, writeTenantAttributes
  • commons – models.AttributeBinding (USER, USER_IN_TENANT)
  • CIAS/cias-authentication/docs/adr – ADR-006, ADR-021, ADR-042; CIAS/cias-authorization/docs/adr – ADR-023
Search