What this is about
Once it is settled which tenant is meant, the filter chain asks next whether that tenant is served at all today. This question is asked by the tenant gate, and only CIAS can answer it — because only CIAS keeps the tenants.
This page shows how the question is asked. What “served” means, and why all refusals look alike, is under Admit the tenant.
One interface, two adapters
The gate does not know CIAS. It only knows an interface, the TenantLookupPort, and behind it sits exactly one adapter:
codamai.cias.tenancy.lookup = localLocalTenantLookupAdapterincias-tenancy, in the same process- a method call that looks into the system database
- no network, no token, no timeout
- "unknown tenant" is an empty answer
codamai.cias.tenancy.lookup = remoteRemoteTenantLookupAdapterincias-tenancy-client, inside the CDMS serviceGET /cias/lookup/tenants/{key}to the CIAS service- with the CDMS service's own service token, by default 2 s to connect and 2 s for the answer
- "unknown tenant" is a 404
The setting lookup has no default. A service that says nothing about it does not start. A gate without an answering side would either let everybody through or refuse everybody, and both would be a decision nobody made.
The same request, two routes
When: CIAS runs in the same process, for example in the hub backend or in a generated project.
sequenceDiagram
participant F as Filter chain
participant T as Tenant gate
participant A as LocalTenantLookupAdapter
participant DB as System DB
F->>T: may "kunde-a" be served?
T->>T: already remembered and younger than 30 s?
T->>A: method call
A->>DB: read standing and validity window
DB-->>A: ACTIVE, valid
A-->>T: served
T-->>F: admitted, remembered for 30 s
Result: No network call. The cache saves a database query, nothing more.
When: CIAS runs as a separate service (cias-runtime).
sequenceDiagram
participant F as Filter chain
participant T as Tenant gate
participant C as cias-tenancy-client
participant S as CIAS service
F->>T: may "kunde-a" be served?
T->>T: already remembered and younger than 30 s?
T->>C: go and ask
C->>S: GET /cias/lookup/tenants/kunde-a<br/>Authorization Bearer service token
S->>S: may this caller ask?
S-->>C: 200 key kunde-a served true
C-->>T: served
T-->>F: admitted, remembered for 30 s
Result: One extra HTTP call – but only when nothing is remembered.
The endpoint discloses exactly two things: the key and whether it is served. No display name, no standing, no data. And it has a role of its own, separate from the administrative API: otherwise every CDMS node would carry a token that can also close customers, just to ask a yes-or-no question.
What the gate remembers
The question comes with every request, and the answer rarely changes. So the gate remembers every answer — including every no.
| Value | Setting | |
|---|---|---|
| How long an answer is valid | 30 seconds | codamai.cias.tenant-gate.ttl |
| How many tenants are remembered | 10,000 | codamai.cias.tenant-gate.max-entries |
| Where the memory sits | in the memory of the CDMS process, per node | – |
The memory lives in cias-authentication and is therefore inside the CDMS process in both operating modes. Not in the small client, and not at CIAS. The client itself remembers nothing and does not try a second time either: a retry would multiply the waiting time of every request before the memory ever got a say.
When the store is full, expired entries go first; if that does not help, it is emptied entirely. That costs one question per tenant — better than no longer recording new answers.
What the gate answers
| Remembered answer | CIAS answers | CIAS says | Outcome for the request |
|---|---|---|---|
| younger than 30 s | – | – | the remembered answer, without asking |
| none or older | yes | served | admitted, and remembered |
| none or older | yes | not served | 403 tenant-not-served, and remembered |
| none or older | yes | knows no such tenant | 403 tenant-not-served, and remembered |
| present | no | – | the remembered answer still applies, even if it was a no |
| none | no | – | 403 tenant-not-served |
A key that cannot be a tenant key at all — Nordbau with capital letters, for instance — is treated like an unknown tenant. The gate does not even ask about it.
The five variants
When: There is an answer for this tenant that is younger than the remembering time.
The gate answers straight from memory. Neither the method call nor the HTTP call happens. This is the most common case, because 30 seconds are many requests.
Result: No call, no waiting.
When: The first request for this tenant, or the remembering time has passed.
A method call into cias-tenancy that looks up standing and validity window in the system database. That takes as long as a database query and can only fail if the database does not answer.
Result: An answer, remembered for 30 seconds.
When: The same, but CIAS is a separate service.
An HTTP call with the service token. 200 with served is the answer, 404 means "no tenant carries that key" – which is an answer too, and it is remembered.
Result: An answer, remembered for 30 seconds.
When: The CIAS service does not accept the connection, or does not answer in time.
After 2 seconds to connect, or 2 seconds waiting for the answer, CIAS counts as unreachable. The timeouts are deliberately short and deliberately not a tuning knob: this call sits on the path of every request, and a slow CIAS service would otherwise be a slow platform.
Result: Handled like every other outage, see When CIAS is not reachable.
When: CIAS rejects the service token – it is missing, expired, or does not carry the lookup role.
A 401 or a 403 is not a statement about the customer but about this service's own credentials. Turning it into "this tenant is not served" would be wrong: a misconfigured installation would silently lock every customer out.
Result: That is why it is not a no about the tenant. The request is refused immediately all the same, even when an answer is remembered, see When CIAS cannot be reached.
When: The CDMS service needs a new service token, but Keycloak does not answer.
The service gets its token from the IAM itself (client credentials) and renews it before it expires. While the old token is still valid it keeps using it and tries again after 10 seconds. Only without a valid token is the question to CIAS not asked at all.
Result: That counts like a CIAS outage: remembered answers keep applying. If Keycloak rejects the client instead (wrong secret, client disabled), the request is refused immediately.
The second question: attribute values in the tenant
Right behind the gate stands a second question of the same build: which attribute values does this person hold in this tenant? An attribute is a value on a person that CDMS uses to filter rows, for example regions. Some attributes apply per tenant.
| Tenant gate | Attribute lookup | |
|---|---|---|
| Question | Is this tenant served? | What does this person hold here? |
| Embedded | method call into cias-tenancy | method call into cias-user |
| Standalone | GET /cias/lookup/tenants/{key} | GET /cias/lookup/users/{id}/attributes?tenantKey=… |
| Remembering time | 30 s (codamai.cias.tenant-gate.ttl) | 30 s (codamai.cias.attribute-lookup.ttl) |
| Asked | on every request with a tenant | on every request with a tenant, if a module registered an attribute per tenant |
Both use the same remembering time, the same service token and the same rule during an outage. One difference matters: for the attribute lookup a 404 is not an answer but a failure. A person CIAS has no record of is answered with 200 and an empty list — so a 404 means “this endpoint does not exist”, for example because the URL is wrong. More under One value per person or per tenant.
After a tenant switch the gate is asked again
If a request carries the header tenant and the person may switch, a different tenant sits in the RequestContext at the end of the filter chain than the one the gate just admitted. The gate then asks again, this time for the target:
-
1CIASresolves the tenant from the token and asks the gate
-
2CIASbuilds roles and attributes for that tenant
-
3CIASchecks the header
tenantagainst the realm role and the list of allowed tenantsIf the role is missing, or the target is not in the list, the switch is silently ignored. -
4CIASIf the tenant changed, the gate is asked once more – usually a hit from memoryResult: Everything behind the filter chain may rely on the tenant in the RequestContext being one CIAS confirmed. For an administrator too: a suspended tenant is suspended for everybody.
More about the switch itself: Tenant switch by header.
Watch out
Next
- Admit the tenant (tenant gate): what “served” means and why all refusals look alike
- When CIAS is not reachable
- The path of the token and From login to the data
- CIAS embedded, CIAS as a separate service, compared
- The view from CDMS: Is the tenant served?