Worum es geht
Nach dem Login hat der BFF drei Tokens von Keycloak. Die Frage ist: Wo werden sie aufbewahrt? Die Antwort bei CodamAI: in einem verschlüsselten Cookie, das nur der BFF öffnen kann. Aufrufe an CDMS und CIAS macht der BFF, nicht der Browser.
Wo welches Token liegt
flowchart LR
B["Browser<br/>Sitzungs-Cookie (verschlüsselt)"] -- "Cookie bei jeder Anfrage" --> F["BFF<br/>entschlüsselt:<br/>Access-, Refresh-, ID-Token"]
F -- "Authorization: Bearer <Access-Token>" --> A["CDMS / CIAS"]
F -- "Refresh-Token, ID-Token" --> K["Keycloak"]
| Token | Wo | Wer benutzt es |
|---|---|---|
| Access-Token | im Sitzungs-Cookie. Die Seite kann es über /api/auth/session abfragen | der BFF bei jedem API-Aufruf |
| Refresh-Token | nur im Sitzungs-Cookie | der BFF, um ein neues Access-Token zu holen |
| ID-Token | nur im Sitzungs-Cookie | der BFF beim Abmelden |
Warum nicht einfach im Browser speichern, etwa im localStorage? Weil jedes Skript auf der Seite dort lesen kann. Ein einziges eingeschleustes Skript könnte dann das Refresh-Token stehlen und sich damit stundenlang neue Tokens holen. Ein httpOnly-Cookie ist für Skripte unsichtbar.
Das Sitzungs-Cookie
| Eigenschaft | Wert | Bedeutung |
|---|---|---|
httpOnly | ja | kein JavaScript kann das Cookie lesen |
sameSite | lax | der Browser schickt es nicht bei Formularen oder Skript-Anfragen von fremden Seiten mit |
secure | ja unter https | nur über verschlüsselte Verbindungen. Der Name bekommt dann das Präfix __Secure- |
path | / | gilt für die ganze Anwendung |
| Inhalt | verschlüsselt (JWE) | Tokens, Ablaufzeit, ein Fehlerfeld. Schlüssel ist das Geheimnis des Portals (NUXT_AUTH_SECRET) |
| Größe | wird bei mehr als etwa 4 KB aufgeteilt | Teile heißen …session-token.0, .1 usw. |
Daneben setzt der BFF während der Anmeldung kurzlebige Cookies für state, PKCE und das Rücksprungziel. Sie gelten höchstens 15 Minuten.
JWE steht für JSON Web Encryption: Der Inhalt ist nicht nur signiert, sondern verschlüsselt.
Laufzeit: eine Stunde, gleitend
Die Sitzung gilt eine Stunde. Die Stunde wird verlängert, solange die Seite offen ist: Die Oberfläche fragt jede Minute und beim Zurückkehren in den Tab /api/auth/session ab, und jede Abfrage schiebt das Ende nach hinten. Dabei erneuert der BFF auch das Access-Token, wenn es bald abläuft, siehe Token erneuern.
| Tab offen, Seite fragt regelmäßig | letzte Abfrage älter als 1 h | Refresh bei Keycloak möglich | Was passiert |
|---|---|---|---|
| ja | nein | ja | Sitzung läuft weiter, Tokens werden erneuert |
| – | ja | – | Cookie abgelaufen, beim nächsten Seitenaufruf neuer Login |
| ja | nein | nein | Sitzung bekommt einen Fehler, die Oberfläche startet einen neuen Login |
Eigenes Präfix je Portal
Cookies gehören zu einem Rechnernamen, nicht zu einem Port. Laufen mehrere Portale auf demselben Rechner, etwa lokal auf localhost:3000 und localhost:3001, sehen sie gegenseitig ihre Cookies. Hätten alle denselben Cookie-Namen, würde jedes Portal das Cookie des anderen überschreiben, und jedes hat einen anderen Schlüssel:
-
1Browser→FrontendLogin im Hub setzt
next-auth.session-token, verschlüsselt mit dem Schlüssel des Hubs -
2Browser→FrontendLogin im CDMS-Portal überschreibt dasselbe Cookie mit dem eigenen Schlüssel
-
3FrontendDer Hub kann das Cookie nicht mehr entschlüsselnSitzungsfehler, neuer Login, der wiederum das andere Portal abmeldet
Deshalb verwendet das CIAS-Portal das eigene Präfix cias-auth: Seine Cookies heißen cias-auth.session-token, cias-auth.csrf-token usw. Der Hub und das CDMS-Portal verwenden die Standardnamen next-auth.*.
Wie der BFF die API aufruft
-
1Browser→BFF
GET /api/kunden, das Sitzungs-Cookie geht automatisch mit -
2BFFentschlüsselt das Cookie. Fehlt das Access-Token oder steht ein Fehler in der Sitzung?dann 401 an den Browser, die Oberfläche startet einen neuen Login
-
3BFF→CDMS
POST /api/rest/crm/customer/querymitAuthorization: Bearer <Access-Token> -
4CDMS→BFFAntwort
-
5BFF→Browsergibt die Daten weiter
Die Header tenant und user für einen Mandanten- oder Benutzerwechsel schicken die Portale nicht. Siehe Mandantenwechsel per Header.