What this is about
A token is a small, signed JSON document. It says who somebody is and what applies to them. On the path of a request it changes hands several times, gets exchanged once, and in the end is no longer needed at all.
This page follows it from the login to the point where CDMS reads data. The same path told in business terms is under From login to the data.
Two words up front:
- A BFF (“backend for frontend”) is the small server that belongs to a portal. The browser talks to it, and it talks to the API.
- The filter chain is code from
cias-authenticationthat runs before every endpoint. It sits in the CDMS process in both operating modes, even when CIAS is a separate service.
The stations
-
KeycloakIssueAre the credentials correct? Then Keycloak issues an access, a refresh and an ID token↳ no no token – the login fails
-
BFFStoreThe three tokens go into an encrypted session cookie. The browser only gets that cookie
-
ClientSendThe BFF decrypts the cookie and attaches the access token as
Authorization: Bearer …to the API call↳ no 403 – on every protected path without a token -
Filter chainVerifyThe signature against a public key of the realm, plus start and expiry. Keycloak is not asked for this↳ no 401 with
WWW-Authenticate: Bearer error="invalid_token" -
Filter chainExchangeKeycloak exchanges the token for one for CIAS's own client↳ no the request continues without an identity, and every role check refuses afterwards
-
Filter chainReadFrom the exchanged token: user, name, realm roles, business roles, groups, organizations, attributes
-
CIASCompleteResolve the tenant, ask the tenant gate, build roles and attributes for exactly this tenant↳ no 403 with a key
cias.authentication.… -
Filter chainStore in contextAll of it goes into the RequestContext of this one request
- CDMS works – and only reads the RequestContext
Every single station has a page of its own. This one only shows that it is one chain: verify the token, exchange the token, read the claims, resolve the tenant, admit the tenant, build the effective roles.
Where each token is stored
Several tokens travel along one request path, and they belong to different parties.
| Token | Who issues it | Where it is stored | Who uses it |
|---|---|---|---|
| the user’s access token | Keycloak | in the BFF’s session cookie | the BFF, on every API call |
| refresh token | Keycloak | only in the session cookie | the BFF, to get a new access token |
| ID token | Keycloak | only in the session cookie | the BFF when logging out |
| exchanged token | Keycloak, on CIAS’s request | in the memory of the CDMS process, at most 5 minutes | the filter chain, to read roles and attributes |
| service token for the lookups | Keycloak, for the CDMS service’s own client (client credentials) | in the memory of the CDMS service, renewed after three quarters of its lifetime | cias-tenancy-client, to ask CIAS over HTTP |
| reader token for the declaration | Keycloak, for CIAS’s reader client (client credentials) | in the memory of the CIAS service, renewed after three quarters of its lifetime | CIAS, to read GET /cias/fetch from CDMS |
The browser holds no token, only the encrypted cookie. Why that is so is under Session in the BFF and cookies.
The four paths in detail
When: The normal case – somebody clicks in the hub, the CDMS portal or the CIAS portal.
-
1Browser→BFFa click, the session cookie goes along automatically
-
2BFFdecrypts the cookie and takes the access token out of itIf it is missing, or the session carries an error, the BFF answers 401 itself and the portal starts a new login
-
3BFF→CDMS
POST /api/rest/crm/customer/querywithAuthorization: Bearer … -
4CDMS→BFFresponseResult: The BFF passes the data on to the browser
When: A browser follows a link, an <img src> loads a picture, a PDF viewer fetches a file.
None of these can set a header of their own. That is why the download endpoints take the token as ?access_token= in the URL. It is verified just the same: signature and expiry. The identity, however, is not read by the filter chain but by the endpoint itself – with the same steps from "exchange" onwards.
Result: If CIAS refuses along the way, the download ends with the same refusal as any other request. See Access without a token.
When: CIAS runs as a separate service and the CDMS service needs to know something.
-
1CDMStakes its service token – fetched from Keycloak with the service's client ID and secret (client credentials), renewed in good time before it expires
-
2CDMS→CIAS
GET /cias/lookup/tenants/{key}andGET /cias/lookup/users/{id}/attributes?tenantKey=… -
3CIASchecks the caller's role – one of its own per endpoint, with no default
-
4CIAS→CDMSthe answerResult: The CDMS service remembers it for the configured time
When: CIAS fetches the list of roles and attributes a module defines.
The direction is reversed: CIAS calls CDMS, GET /cias/fetch, with a reader token of its own, which CIAS fetches from Keycloak the same way. The answer is the complete permission map of the application, so it is nothing that should be public. CDMS checks a realm role of its own for it.
Result: This call is not on the request path. It runs at startup and when somebody triggers the reconciliation. See Modules register their roles.
What ends up in the RequestContext
The RequestContext is the only place CDMS reads from. Among other things it holds:
| Field | From | Used for |
|---|---|---|
userId | sub of the exchanged token | owner of new objects, history, owner filter |
userName | name, else preferred_username | display and logging |
userTenant | the resolved and admitted tenant | which database, which rows |
allowedTenants | own tenant, organizations, attribute | what a tenant switch may reach |
effectiveUserRealmRoles | realm_access.roles | platform roles, switch permissions |
effectiveUserRoles | business roles for the active tenant | model and field roles in CDMS |
effectiveUserGroups | groups | for information |
effectiveUserAttributes | the token plus the values per tenant | attribute filters on rows |
Which claim goes exactly where is under What is read from the token.
Who reads the RequestContext
-
1CDMSThe role check compares the business roles with the roles the model requiresSee Model roles and Permissions on relations.
-
2CDMSThe attribute filter takes an attribute value of the person and restricts the rows with itSee Attribute filters.
-
3CDMSThe owner filter compares the row's
_userIdwith theuserIdof the requestSee Only your own data. -
4CDMSOn a write, the system layer records the
userIdas the owner, and the history records it too -
5CDMSThe file storage stores files under the tenant of the request
-
6CIASCIAS's own endpoints read the same context as the caller: authenticated, tenant, roles, personResult: A tenant administrator therefore always administers their own tenant, never one taken from the request body
After the response, everything is gone
The filter chain clears the RequestContext at the end of every request — including a request that was refused. That is necessary because the context is attached to the thread, and threads are reused. If it stayed, the next piece of work on that thread could read the identity, tenant and roles of the previous request.