CodamAIDocs
Themafertig

Vom Login bis zu den Daten

Eine Person meldet sich an und öffnet eine Liste. Jeder Schritt über Browser, BFF, Keycloak, CIAS und CDMS bis zur Datenbank und zurück, in beiden Betriebsarten.

Ausprägungen
eingebettet (ein Prozess)getrennt (zwei Dienste)erste Anfrage nach der Anmeldungspätere Anfrage aus der laufenden SitzungAccess-Token abgelaufenAbbruch an jeder Station

Worum es geht

Das hier ist der Weg, den jede Anfrage in einer CodamAI-Anwendung geht. Eine Person meldet sich an, klickt auf „Kunden“ und sieht eine Liste. Dazwischen liegen sechs Programme, und jedes tut genau eine Sache.

Wenn du diese Seite verstanden hast, kannst du jede andere Seite lesen: Alle anderen Abläufe sind Abzweigungen von diesem einen.

Wer welche Farbe hat und wer überhaupt beteiligt ist, steht unter Die Beteiligten einer Anfrage. Diese Seite spielt den Weg einmal von vorn bis hinten durch.

Die zwei Hälften

Der Ablauf zerfällt in zwei Teile, die nichts miteinander zu tun haben – außer dass der zweite den ersten voraussetzt:

1. Anmelden
einmal je Sitzung
  • Browser, BFF und Keycloak
  • endet damit, dass der BFF drei Tokens hat
  • CDMS und CIAS sind daran nicht beteiligt
2. Daten holen
bei jedem Klick
  • Browser, BFF, CIAS, CDMS, Datenbank
  • beginnt damit, dass der BFF das Access-Token hervorholt
  • Keycloak wird höchstens noch für den Token-Tausch gefragt

Teil 1: Anmelden

sequenceDiagram
    autonumber
    participant B as Browser
    participant F as BFF
    participant K as Keycloak
    B->>F: öffnet /kunden
    F-->>B: keine Sitzung, Weiterleitung auf /login
    B->>K: Login-Seite von Keycloak
    K-->>B: Passwort, ggf. Einmalcode
    B->>K: Eingaben
    K-->>B: Weiterleitung zurück mit einem einmaligen Code
    B->>F: /api/auth/callback/keycloak?code=…
    F->>K: tauscht den Code gegen Tokens
    K-->>F: Access-, Refresh- und ID-Token
    F-->>B: verschlüsseltes Sitzungs-Cookie, zurück auf /kunden

Drei Dinge daraus, die den Rest der Seite tragen:

  • Das Passwort sieht nur Keycloak. Die Anwendung bekommt es nie. Details unter Anmelden im Browser.
  • Die Tokens liegen im BFF, im Browser liegt nur ein verschlüsseltes Cookie. Details unter Sitzung im BFF und Cookies.
  • Das Access-Token ist der Ausweis für alles Weitere. Es gilt kurz (voreingestellt 5 Minuten) und wird vor Ablauf erneuert, siehe Token erneuern.

Teil 2: Der Klick auf „Kunden“

Jetzt kommt der Weg, um den es hier geht. Die Person ist angemeldet und klickt in der Oberfläche auf „Kunden“.

sequenceDiagram
    autonumber
    participant U as Benutzer
    participant B as Browser
    participant F as BFF
    participant K as Keycloak
    participant FK as Filterkette (CIAS)
    participant T as Mandanten-Tor (CIAS)
    participant C as CDMS
    participant DB as Mandanten-DB
    U->>B: klickt auf „Kunden“
    B->>F: GET /api/kunden, Sitzungs-Cookie geht mit
    F->>F: Cookie entschlüsseln, Access-Token herausnehmen
    opt Token läuft bald ab
        F->>K: POST /token, grant_type=refresh_token
        K-->>F: neues Access-Token
    end
    F->>FK: POST /api/rest/crm/customer/query<br/>Authorization: Bearer …
    FK->>FK: Signatur und Ablauf prüfen
    FK->>K: Token tauschen (oder aus dem Cache)
    K-->>FK: Token für den CIAS-Client
    FK->>FK: Identität lesen, Mandant auflösen
    FK->>T: wird nordbau bedient?
    T-->>FK: ja (30 s gemerkt)
    FK->>FK: effektive Rollen und Attribute bilden
    FK->>C: RequestContext gefüllt, weiterreichen
    C->>C: response auflösen (welche Felder?)
    C->>C: Leserolle für customer prüfen
    C->>C: Sicherheitsfilter anhängen (Besitzer, Attribute, eigene)
    C->>DB: SELECT der erlaubten Spalten und Zeilen
    DB-->>C: Zeilen
    C->>C: READ-Hook je Zeile, dann in DTOs umwandeln
    C-->>F: 200 mit data und meta
    F-->>B: die Liste als JSON der eigenen Route
    B-->>U: die Tabelle erscheint

Das ist der ganze Weg. Die folgenden Abschnitte gehen ihn in vier Etappen noch einmal langsam durch.

Etappe 1: Browser und BFF

Der Browser ruft nie CDMS oder CIAS direkt auf. Er ruft eine Route des eigenen Frontends auf, und die liegt auf dem BFF, dem Server-Teil der Oberfläche.

Vom Klick zur ausgehenden Anfrage
  1. 1
    Browser→BFF
    ruft eine Route der eigenen Anwendung auf, z. B. GET /api/kunden. Das Sitzungs-Cookie schickt der Browser automatisch mit
  2. 2
    BFF
    entschlüsselt das Cookie mit dem Geheimnis des Portals und holt das Access-Token heraus
  3. 3
    BFF→Browser
    kein Access-Token oder ein Fehler in der Sitzung → 401, die Oberfläche startet einen neuen Login
  4. 4
    BFF→Keycloak
    Token bald abgelaufen? Dann erst ein neues holen
    Die Portale erneuern 90 Sekunden vor Ablauf. Der Hub prüft zusätzlich vor jedem API-Aufruf, ob das Token schon abgelaufen ist, und erneuert dann sofort.
  5. 5
    BFF→CDMS
    schickt POST /api/rest/crm/customer/query mit Authorization: Bearer <Access-Token> und einem Körper aus response und parameter
    Ergebnis: Nur der BFF kennt das Token. Der Browser hat es nie gesehen.

Warum dieser Umweg? Weil das Token im Browser von jedem Skript der Seite lesbar wäre. Im BFF ist es das nicht. Die ganze Begründung steht unter Sitzung im BFF und Cookies.

Der BFF ist außerdem die Stelle, an der aus einer fachlichen Route („gib mir die Kunden“) eine CDMS-Anfrage wird: Er setzt die Feldauswahl (response) und die Suchparameter zusammen. Siehe Aufbau einer Suche.

Etappe 2: Die Filterkette von CIAS

Die Anfrage kommt beim Backend an – und läuft dort zuerst durch die Filterkette von CIAS, noch bevor eine einzige Zeile CDMS-Code läuft.

POST /api/rest/crm/customer/query mit Bearer-Token
  1. Filterkette
    Offener Pfad?
    Ein paar Pfade brauchen kein Token: OPTIONS-Anfragen des Browsers und was ein Modul ausdrücklich veröffentlicht. /v3/api-docs und /swagger-ui haben eine eigene Anmeldung mit Benutzer und Passwort. Alles andere braucht eines
    ↳ nein 403, wenn kein Token da ist
  2. Filterkette
    Token prüfen
    Ist die Signatur mit einem Schlüssel des Realms gültig und das Token nicht abgelaufen? Keycloak wird dafür nicht gefragt
    ↳ nein 401 mit WWW-Authenticate: Bearer error="invalid_token"
  3. CIAS
    Token tauschen
    Keycloak tauscht das Token gegen eines für den Client von CIAS. Erst darin stehen alle Rollen und Attribute
    ↳ nein die Anfrage läuft ohne Identität weiter, jede Rollenprüfung lehnt danach ab
  4. CIAS
    Mandant auflösen
    Welcher Mandant ist gemeint? Genau einer, oder bewusst keiner?
    ↳ nein 403 cias.authentication.tenant-unresolved
  5. CIAS
    Mandanten-Tor
    Wird dieser Mandant heute bedient?
    ↳ nein 403 cias.authentication.tenant-not-served
  6. CIAS
    Rollen und Attribute
    Welche Rollen und welche Attributwerte gelten in genau diesem Mandanten?
    ↳ nein Attributwerte nicht abrufbar: 403 cias.authentication.tenant-not-served
  7. CIAS
    Mandant Pflicht?
    Trennt die Installation nach Mandanten, ist die Person bekannt, hat aber keinen Mandanten?
    ↳ nein 403 cias.authentication.tenant-required
  8. Die Anwendung bekommt die Anfrage mit gefülltem RequestContext

Das Ergebnis heißt RequestContext: ein Merkzettel, der genau für diese eine Anfrage gilt. Darin stehen Benutzer-ID, Name, Mandant, erlaubte Mandanten, Rollen, Gruppen, Attribute – dazu IP-Adresse und Browser-Kennung, die die Filterkette aus der Anfrage gelesen hat. Alles danach liest nur noch dort nach, nie wieder im Token.

Zwei Punkte, die man leicht übersieht:

  • Die Filterkette ist immer Teil des CDMS-Prozesses, auch wenn CIAS als eigener Dienst läuft. Sie ist ein Jar, kein Server.
  • Sie räumt den Merkzettel nach der Antwort wieder ab. Der nächste Aufruf auf demselben Thread fängt leer an.

Jede Station im Einzelnen steht unter Was bei jeder Anfrage mit dem Token passiert, die Mandantenfrage unter Die Mandantenprüfung in beiden Betriebsarten.

Etappe 3: CDMS

Jetzt erst ist CDMS dran. Es sieht kein Token, nur den RequestContext.

Innerhalb von CDMS
  1. 1
    CDMS
    REST-Layer: liest den Körper, löst response auf – welche Felder, welche Referenzen, welche Listen sollen zurück?
    Ohne response weiß CDMS nicht, was es liefern soll, und lehnt ab. Siehe Feldauswahl mit response.
  2. 2
    CDMS
    System-Layer: Erlaubt eine der effektiven Rollen das Lesen von customer?
  3. 3
    CDMS→BFF
    keine passende Rolle → 403 missing-permission|<rolle>
  4. 4
    CDMS
    hängt die Sicherheitsfilter an: Owner-Filter, Attributfilter und eigene Filter des Projekts
    Der Filter des Clients und die Sicherheitsfilter landen zusammen in einer UND-Klammer. Kein ODER des Clients kann sie deshalb aushebeln.
  5. 5
    CDMS→Mandanten-DB
    Persistenz: wählt die Datenbank und liest nur die angeforderten Spalten der erlaubten Zeilen
    System-Modelle gehen in die System-Datenbank, Mandanten- und Benutzer-Modelle in die Datenbank des Mandanten aus dem RequestContext.
  6. 6
    Hook
    READ-Hook für jedes geladene Objekt, bevor es zur Antwort wird
  7. 7
    CDMS→BFF
    antwortet mit data (die Liste) und meta (Trefferzahl, Seite)
    Ergebnis: Keine Treffer sind kein Fehler: 200 mit leerer Liste.

Die Stationen innerhalb von CDMS haben eine eigene Seite: Der Weg einer Anfrage durch die Schichten. Welche Zeilen eine Person sieht, steht unter Die drei Ebenen im Überblick.

Etappe 4: Zurück zum Bildschirm

Der Rückweg ist kurz, hat aber zwei Besonderheiten:

  • Vor dem Schreiben der Antwort schreibt CDMS seine Transaktion fest. Beim Lesen fällt das nicht auf, beim Schreiben ist es der entscheidende Punkt – siehe Ein Schreibvorgang über alle Schichten.
  • Der BFF gibt die Daten nicht roh weiter. Er formt sie in das um, was die Oberfläche braucht, und übersetzt Fehler: Ein missing-permission|projekt wird zu „dir fehlt eine Rolle für dieses Modell“, ein tenant-required zu „dein Konto gehört zu keinem Mandanten“. Die Oberfläche schickt jemanden damit an die richtige Stelle statt auf die Login-Seite.

Derselbe Weg in beiden Betriebsarten

CDMS und CIAS können in einem Prozess laufen oder als zwei Dienste. Der Weg der Anfrage ist derselbe – nur die Frage „wird der Mandant bedient?“ nimmt einen anderen Weg.

„Liste der Kunden“, eingebettet und getrennt

Wann: CDMS und CIAS laufen im selben Prozess, so wie das Hub-Backend.

sequenceDiagram
    autonumber
    participant F as BFF
    participant FK as Filterkette (CIAS)
    participant T as Mandanten-Tor (CIAS)
    participant U as cias-user
    participant C as CDMS
    participant DB as Mandanten-DB
    F->>FK: POST /api/rest/crm/customer/query + Token
    FK->>FK: Token prüfen, tauschen, Mandant auflösen
    FK->>T: darf nordbau bedient werden?
    Note over FK,T: Methodenaufruf, kein Netz
    T-->>FK: ja (30 s gemerkt)
    FK->>U: welche Attributwerte gelten hier?
    U-->>FK: regionen = [nord]
    FK->>C: RequestContext gefüllt
    C->>DB: SELECT der erlaubten Zeilen
    DB-->>C: Zeilen
    C-->>F: data + meta

Ergebnis: Ein Prozess, kein Netzwerkaufruf zwischen CDMS und CIAS. CIAS kann nicht allein ausfallen.

Wann: CIAS läuft als eigener Dienst (cias-runtime), CDMS als zweiter.

sequenceDiagram
    autonumber
    participant F as BFF
    participant FK as Filterkette (im CDMS-Dienst)
    participant TC as cias-tenancy-client
    participant S as CIAS-Dienst
    participant C as CDMS
    participant DB as Mandanten-DB
    F->>FK: POST /api/rest/crm/customer/query + Token
    FK->>FK: Token prüfen, tauschen, Mandant auflösen
    FK->>TC: darf nordbau bedient werden?
    TC->>S: GET /cias/lookup/tenants/nordbau<br/>mit dem Dienst-Token von CDMS
    S-->>TC: 200 served: true
    TC-->>FK: ja (30 s gemerkt)
    FK->>TC: welche Attributwerte gelten hier?
    TC->>S: GET /cias/lookup/users/{id}/attributes?tenantKey=nordbau
    S-->>TC: regionen = [nord]
    TC-->>FK: Werte
    FK->>C: RequestContext gefüllt
    C->>DB: SELECT der erlaubten Zeilen
    DB-->>C: Zeilen
    C-->>F: data + meta

Ergebnis: Zwei zusätzliche HTTP-Aufrufe – aber nur, wenn nichts gemerkt ist. Das Token dafür ist ein Dienst-Token von CDMS, nie das der Person.

Die Unterschiede in voller Breite stehen unter Eingebettet und getrennt im Vergleich. Für den Anfrageweg zählt vor allem: Die Filterkette läuft in beiden Fällen im CDMS-Prozess, und die Antwort der beiden Lookups wird 30 Sekunden gemerkt. Was passiert, wenn der CIAS-Dienst schweigt, steht unter Wenn CIAS nicht erreichbar ist.

Was in welcher Anfrage steckt

Drei Anfragen hintereinander, und jede sieht anders aus:

AbschnittWas geht mitWer prüft es
Browser → BFFSitzungs-Cookie (verschlüsselt, httpOnly)der BFF, mit dem Geheimnis des Portals
BFF → Keycloak (nur beim Erneuern)Refresh-Token, Client-ID, Client-GeheimnisKeycloak
BFF → CDMSAuthorization: Bearer <Access-Token>, dazu response und parameter im Körperdie Filterkette von CIAS, dann CDMS
CDMS → CIAS-Dienst (nur getrennt)ein eigenes Dienst-Token von CDMSder CIAS-Dienst, über eine eigene Leserolle
CDMS → Datenbankkeine Anmeldedaten der Person, sondern die Verbindung des Mandantendie Datenbank selbst

Wo der Weg endet, wenn etwas fehlt

Warum die Liste nicht kommt
Sitzung im BFFToken gültigMandant zulässigLeserolle für das ModellWas die Person sieht
nein–––Die Oberfläche schickt zur Anmeldung
janein––401 – das Token wird erneuert, die Anfrage wiederholt
jajanein–403 aus der Filterkette, mit error: cias.authentication.…
jajajanein403 aus CDMS, mit messageKey: missing-permission|<rolle>
jajajaja200 mit den Zeilen, die die Filter durchlassen – vielleicht auch mit keiner

Zwei Arten von 403 also, und sie sehen verschieden aus: Die Filterkette schreibt { "error": …, "message": … }, CDMS schreibt { "messageKey": …, "code": …, "layer": … }. Am Feldnamen erkennst du sofort, wer abgelehnt hat. Siehe 401, 403 oder 404?.

Eine leere Liste ist übrigens kein Fehler und sieht genauso aus wie „es gibt nichts“. Das ist gewollt: Wer eine Zeile nicht sehen darf, soll nicht daraus schließen können, dass es sie gibt. Siehe Warum Unsichtbares 404 liefert.

Fallen

Wie es weitergeht

Quellen im Code und in der Wissensdatenbank
  • hub-frontend – server/api/auth/[...].ts, server/utils/sessionToken.ts, refreshToken.ts, backendFetch.ts, ciasFetch.ts, useCmsApi.ts, server/api/hub/projects/index.get.ts
  • CDMS/frontend, CIAS/cias-frontend – server/utils/useCmsApi.ts, app/middleware/auth.global.ts
  • CIAS/cias-authentication – SessionConfig (Filterkette hinter dem BearerTokenAuthenticationFilter, offene Pfade), JwtSessionFilter (IP, User-Agent, Header `tenant`/`user`, RequestContext), TokenParser.tokenParser/admit, TenantGate, EffectiveRoles, EffectiveAttributes, AttributeLookup, ContextSwitch
  • CIAS/cias-tenancy – TenantLookupController; CIAS/cias-tenancy-client – RemoteTenantLookupAdapter
  • CDMS/cdms-rest-api – AbstractRestApi, Expander, RequestTransactionCommitter
  • CDMS/cdms-system-layer – AbstractSystemLayer.queryObjects, AbstractLayer.recursiveQuery (Rollenprüfung, Sicherheitsfilter in einer UND-Klammer, READ-Hook)
  • CDMS/cdms-authorization – AbstractAuthorizationLayer, AbstractAttributeFilter
  • commons-persistence – DatabaseRequestContext.getEntityManager, resolveTenant
Suchen