What this is about
For an attribute filter in CDMS to read a value, the value has to travel a long way: from the account in Keycloak through the token into the RequestContext. The RequestContext is the object in which CIAS records, for every request, who is asking, with which roles and which attributes.
The route
sequenceDiagram
participant M as Module
participant C as CIAS
participant K as Keycloak
participant B as Browser/BFF
participant D as CDMS
M->>C: registers region (declaration)
C->>K: profile: region allowed
C->>K: module's client: mapper region → claim region
Note over C,K: once, during reconciliation
C->>K: admin writes region = nord, west on the account
B->>K: log in or renew the token
K-->>B: token with "region": ["nord", "west"]
B->>D: request with token
D->>C: check token (filter chain)
C->>C: claim region → attribute, insert value per tenant if needed
C-->>D: RequestContext: effectiveUserAttributes.region = [nord, west]
D->>D: attribute filter: region IN (nord, west)
The stations
| Station | What happens there | Who takes care of it |
|---|---|---|
| User profile | Keycloak only stores attributes that are in the profile. It also says whether it is a list and who may change it | the reconciliation, from the registration |
| Value on the account | the actual value of the person | an administrator via POST /cias/admin/users/{id}/profile-attributes, for a preference also the person themselves |
| Claim mapper | an entry on the client that says: “Put the attribute region into the token as claim region.” It applies to access token, ID token and UserInfo | the reconciliation, per module on its client |
| Token | the claim is in the token. For a list as a JSON array, otherwise as text | Keycloak, on every login and renewal |
| RequestContext | CIAS turns every remaining claim into an attribute, always as a list of texts. If the attribute is registered per tenant, CIAS inserts the value of the active tenant | CIAS, on every request |
| Filter | CDMS reads the attribute from effectiveUserAttributes | CDMS |
The mapper sits on the module’s client, that is, the client whose token the module checks. Which claims CIAS takes as an attribute at all and which not is described in What is read from the token.
The variants
When: Attribute without multivalued, the account has customerId = 4711.
The token contains "customerId": "4711". CIAS turns it into a list with one entry.
Result: effectiveUserAttributes.customerId = ["4711"]
When: Attribute with multivalued, the account has nord and west.
The token contains "region": ["nord", "west"]. The filter also splits each entry at commas, so nord,west in one entry works the same.
Result: effectiveUserAttributes.region = ["nord", "west"]
When: The person has no value, and the attribute has no default value.
Keycloak leaves the claim out. The attribute is missing in the RequestContext. An attribute filter in CDMS then refuses the request instead of returning everything.
Result: 422 missing-attribute-on-profile for a filtered model
When: The attribute is registered as USER_IN_TENANT.
After the tenant check, CIAS fetches the person's values for the active tenant and inserts them for this key. They replace the value from the token, they are not mixed in. Only keys the application itself registers per tenant are replaced, all others come unchanged from the token.
Result: see One value per person or per tenant
Where it can get stuck
| In the profile? | Mapper on the client? | Value on the account? | New token fetched? | Result |
|---|---|---|---|---|
| no | – | – | – | Keycloak accepts no value. Does a module register the attribute? Did the reconciliation run? |
| yes | no | – | – | value is on the account but not in the token. Does the module run with the right client? |
| yes | yes | no | – | no claim, CDMS answers 422 |
| yes | yes | yes | no | old token without the new value |
| yes | yes | yes | yes | arrives |