Worum es geht
Ein Konto, das CIAS anlegt, hat kein Passwort. CIAS nimmt nie eines entgegen. Die Person soll es selbst bei Keycloak setzen, auf der Seite von Keycloak.
Nur: Wie kommt sie dorthin? Die Pflichtaktion „Passwort setzen“ (UPDATE_PASSWORD) hilft allein nicht. Keycloak fragt Pflichtaktionen erst nach der Anmeldung ab, und ohne Passwort kann sich niemand anmelden. Übrig bliebe nur „Passwort vergessen“.
Keycloak kann einen Einmal-Link ausstellen, der genau dieses Problem löst. Er gilt einmal, läuft ab, gehört zu einem Konto, einer Liste von Aktionen, einem Client und einer Rücksprungadresse. Keycloak signiert ihn mit dem Schlüssel des Realms, also kann ihn niemand außerhalb von Keycloak bauen, auch CIAS nicht. Aber Keycloaks eigene Admin-API liefert diesen Link nur in einer Mail, die Keycloak selbst verschickt, mit Keycloaks Text und Keycloaks Absender.
Die Erweiterung ist kein Adapter. Der Adapter (cias-iam-keycloak) läuft in CIAS und ruft sie über HTTP auf. Die Erweiterung ist das andere Ende dieses Aufrufs, im Server von Keycloak.
Der Ablauf
sequenceDiagram
participant C as CIAS
participant K as Keycloak
participant X as Erweiterung in Keycloak
participant M as E-Mail
participant B as Benutzer
C->>K: Konto freischalten, UPDATE_PASSWORD verlangen
C->>X: POST /admin/realms/{realm}/cias-action-links
X->>X: prüft Aufrufer, Konto, Aktionen, Client, Rücksprungadresse
X-->>C: Link und Ablaufzeit
C->>M: Mail mit dem Link, in CIAS' eigener Vorlage
M-->>B: Mail
B->>K: öffnet den Link
K-->>B: Seite „Neues Passwort“
B->>K: Passwort setzen
K-->>B: weiter zur Rücksprungadresse, also zur Anmeldung der Anwendung
CIAS sieht dabei nie ein Passwort. Es trägt nur den Link zur Person, so wie es auch den Bestätigungslink einer Registrierung trägt.
Anfrage und Antwort
POST /admin/realms/codamai/cias-action-links
Authorization: Bearer <Token des Dienstkontos cias-admin>
{
"userId": "5c9e…",
"actions": ["UPDATE_PASSWORD"],
"clientId": "hub-frontend",
"redirectUri": "https://app.example.com/login",
"lifespan": 86400
}HTTP 200
{
"link": "https://sso.example.com/realms/codamai/login-actions/action-token?key=…",
"expiresAt": "2026-09-23T09:14:03Z"
}| Feld | Bedeutung |
|---|---|
userId | das Konto in Keycloak |
actions | die Pflichtaktionen, durch die der Link führt, in dieser Reihenfolge |
clientId | der Client der Oberfläche, bei der sich die Person danach anmeldet |
redirectUri | wohin es nach dem Passwort geht; muss zu diesem Client passen |
lifespan | Gültigkeit in Sekunden, höchstens 72 Stunden; fehlt sie, gilt die Einstellung des Realms für Links, die ein Administrator auslöst, ebenfalls gekappt auf 72 Stunden |
link | der Einmal-Link |
expiresAt | wann er abläuft |
Was die Erweiterung prüft
Die Erweiterung sitzt unter /admin/realms/{realm}. Keycloak prüft deshalb das Token des Aufrufers, bevor ihr Code überhaupt läuft. Danach prüft sie selbst:
-
KeycloakAnmeldungGültiges Token für die Admin-API?↳ nein 401
-
KeycloakAufruferSteht der Client, für den das Token ausgestellt ist, in der Serveroption
allowed-clients?↳ nein 403, bevor irgendetwas anderes geprüft wird -
KeycloakPflichtfelderSind Konto und mindestens eine Aktion genannt?↳ nein 400
-
KeycloakKontoGibt es das Konto in diesem Realm?↳ nein 404
-
KeycloakRechtDarf der Aufrufer dieses Konto verwalten? Dasselbe Recht wie für Keycloaks eigene Mail.↳ nein 403
-
KeycloakKonto nutzbarHat das Konto eine E-Mail-Adresse und ist es freigeschaltet?↳ nein 400
-
KeycloakAktionenIst jede Aktion erlaubt (
UPDATE_PASSWORD,UPDATE_PROFILE,TERMS_AND_CONDITIONS), istUPDATE_PASSWORDdabei, und ist jede Aktion im Realm eingeschaltet?↳ nein 400, mit den betroffenen Aktionen im Text -
KeycloakClientGibt es den Client, und ist er eingeschaltet?↳ nein 400
-
KeycloakRücksprungadresseIst eine Adresse genannt, und passt sie zu den erlaubten Adressen dieses Clients?↳ nein 400
-
KeycloakGültigkeitIst die Gültigkeit positiv und höchstens 72 Stunden, falls angegeben?↳ nein 400
- Link wird gebaut; im Admin-Ereignis von Keycloak stehen Konto, Aktionen, Client und Gültigkeit, aber nie der Link
Jede Prüfung hat einen Grund. Anders als bei Keycloaks eigener Mail landet der Link beim Aufrufer, nicht im Postfach der Person. Ohne die Liste der Aufrufer könnte deshalb jeder, der Konten verwalten darf (manage-users), sich Links für beliebige Konten abholen. Ohne die feste Liste der Aktionen ließe sich ein Link bauen, der zum Beispiel ein Konto löscht (delete_account). Die Obergrenze von 72 Stunden sorgt dafür, dass ein Link nicht wochenlang gültig in einem Postfach liegt. Ohne die Prüfung der Rücksprungadresse könnte eine Mail von der Plattformadresse auf eine fremde Seite führen. Ohne die Prüfung der Aktionen gäbe es Links, die mitten in der Anmeldung stecken bleiben. Ein Konto, das nicht freigeschaltet ist, könnte sich nach dem Passwort ohnehin nicht anmelden. Deshalb lehnt die Erweiterung hier laut ab, statt einen nutzlosen Link auszugeben.
Was CIAS mit dem Link macht
Zwei Stellen fragen nach einem Link:
- Die Willkommensmail der Registrierung, nur für neue Konten. Siehe Das Passwort setzen.
- „Link erneut schicken“ durch einen Administrator. Siehe Passwort-Setz-Link erneut schicken.
| Einstellung | Bedeutung |
|---|---|
codamai.cias.registration.password-setup.client-id | der Client der Oberfläche, für den der Link der Registrierung gilt. Leer heißt: die Registrierung fragt nach keinem Link |
codamai.cias.registration.password-setup.valid-for | wie lange der Link gilt, höchstens 72 Stunden; leer heißt: Einstellung des Realms, gekappt auf 72 Stunden. Ein größerer Wert verhindert den Start |
codamai.cias.keycloak.password-setup-actions | welche Aktionen der Link verlangt; leer heißt nur UPDATE_PASSWORD. Wer zusätzlich die Zustimmung zu Nutzungsbedingungen (TERMS_AND_CONDITIONS) oder ein vollständiges Profil (UPDATE_PROFILE) will, trägt sie hier ein, und die Person erledigt alles in einem Besuch. Andere Aktionen sind nicht erlaubt, und UPDATE_PASSWORD muss in der Liste stehen, denn sie ersetzt die Voreinstellung. Sonst startet CIAS nicht |
Die Varianten
Wann: Das Jar liegt in Keycloaks Verzeichnis providers/, Keycloak hat es beim Start geladen, und die Anfrage besteht alle Prüfungen.
Die Erweiterung gibt Link und Ablaufzeit zurück. CIAS setzt den Link als passwordUrl in die Mail.
Ergebnis: ein Klick zum eigenen Passwort
Wann: Das Jar ist nicht installiert.
Keycloak kennt den Pfad cias-action-links nicht und antwortet 404. Der Adapter liest das als „kein Link“. Die Registrierung wird trotzdem fertig. Die Willkommensmail führt zur Anmeldung und erklärt „Passwort vergessen“. Der Administrator-Aufruf „Link erneut schicken“ verschickt dagegen gar keine Mail.
Ergebnis: Umweg über „Passwort vergessen“
Wann: Keycloak kennt das Konto nicht.
Die Erweiterung antwortet ebenfalls 404. Für den Adapter sieht das aus wie eine fehlende Erweiterung: kein Link.
Ergebnis: kein Link
Wann: Eine der Prüfungen scheitert, etwa eine Rücksprungadresse, die nicht zum Client passt, eine Aktion, die im Realm abgeschaltet ist, oder eine Gültigkeit über 72 Stunden.
Die Erweiterung antwortet 400 oder 403. Bei der Registrierung schreibt CIAS eine Warnung ins Log, ohne Link und ohne Adresse, und die Mail geht ohne Link hinaus. Beim Administrator-Aufruf scheitert der Aufruf.
Ergebnis: kein Link, Hinweis im Log
Wann: Die Erweiterung ist geladen, aber der Client, mit dem CIAS sich anmeldet, steht nicht in allowed-clients, etwa weil die Einstellung bei der Installation vergessen wurde.
Die Erweiterung antwortet 403, bevor sie irgendetwas anderes prüft. Keycloak schreibt schon beim Start „no client may request links“ ins Protokoll und bei jeder Ablehnung den Client dazu. CIAS verhält sich wie bei jeder abgelehnten Anfrage.
Ergebnis: kein Link, Hinweis in beiden Logs
Wann: codamai.cias.iam-provider: memory
Der Speicher-Adapter gibt einen Link zurück, der mit memory:// beginnt und nirgendwohin führt. So lassen sich Mails mit Link testen. Eine Gültigkeit über 72 Stunden lehnt er ab wie Keycloak.
Ergebnis: Test-Link
Wann: Keycloak startet mit --optimized und wurde nicht mit dem Jar zusammen gebaut.
Keycloak überspringt dann das Einlesen neuer Erweiterungen. Das Jar liegt im Verzeichnis, wird aber nie geladen. Der Pfad antwortet 404, und alles verhält sich wie ohne Erweiterung, ohne Fehlermeldung.
Ergebnis: kein Link
password-setup.client-id gesetzt? | Neues Konto? | Erweiterung geladen? | Prüfungen bestanden? | |
|---|---|---|---|---|
| nein | – | – | – | ohne Link, mit Hinweis auf „Passwort vergessen“ |
| ja | nein | – | – | ohne Link: wer schon ein Konto hat, hat ein Passwort |
| ja | ja | nein | – | ohne Link |
| ja | ja | ja | nein | ohne Link, Warnung im Log |
| ja | ja | ja | ja | mit Link |
Die Erweiterung installieren
Keycloak lädt Erweiterungen beim Start aus dem Verzeichnis /opt/keycloak/providers. Das Jar heißt cias-iam-keycloak-provider.jar und meldet sich über den Dienstnamen cias-action-links an.
Wann: Du startest den Stapel aus cias-runtime/docker-compose.yml.
Erst das Jar bauen (mvn install in cias-iam-keycloak-provider), dann in der Compose-Datei die vorbereitete Zeile für den Mount einkommentieren. Keycloak läuft dort mit start-dev und lädt das Jar beim Start.
Ergebnis: Link lokal testbar
Wann: Keycloak läuft in Kubernetes.
Ein kleines Image mit dem Jar läuft als Init-Container im Pod von Keycloak. Es kopiert das Jar in ein gemeinsames Verzeichnis und beendet sich. Erst danach startet Keycloak und findet das Jar. Die Vorlage dafür liegt unter deploy/keycloak-sidecar.yaml.
Ergebnis: Jar vor dem Start von Keycloak an Ort und Stelle
Das Jar allein genügt nicht. Keycloak muss außerdem wissen, welche Clients Links abholen dürfen. Ohne diese Einstellung lehnt die Erweiterung jede Anfrage ab.
KC_SPI_ADMIN_REALM_RESTAPI_EXTENSION__CIAS_ACTION_LINKS__ALLOWED_CLIENTS=cias-admin
| Eintrag | Bedeutung |
|---|---|
cias-admin | der Client dieses Namens in dem Realm, der verwaltet wird. Der Normalfall: CIAS meldet sich mit einem Dienstkonto des Realms an, den es verwaltet |
master/admin-cli | der Client dieses Namens in genau diesem Realm, für einen Aufrufer, der sich in einem anderen Realm anmeldet, als er verwaltet |
Mehrere Einträge trennt ein Komma. Ein Eintrag ohne Realm passt nie auf einen gleichnamigen Client in einem anderen Realm, denn Client-Namen sind nur innerhalb eines Realms eindeutig. In cias-runtime/docker-compose.yml ist cias-admin schon eingetragen, im Cluster setzt deploy/keycloak-sidecar.yaml die Variable am Keycloak-Container.
Ob die Erweiterung wirklich geladen ist, siehst du nicht an einem Aufruf ohne Anmeldung. Die Admin-API prüft die Anmeldung vor dem Pfad und antwortet in beiden Fällen 401. Verlässlich sind zwei Wege: Im Startprotokoll von Keycloak taucht ActionLinkResourceProviderFactory auf, oder ein angemeldeter Aufruf ohne Inhalt antwortet 400 oder 403 (Erweiterung da) statt 404 (Erweiterung fehlt). Das Skript deploy/smoke-test.sh fragt einen echten Link an und prüft danach, dass eine fremde Rücksprungadresse, eine unbekannte Aktion, eine nicht erlaubte Aktion und eine Gültigkeit über 72 Stunden abgelehnt werden. Der Client, mit dem sich das Skript anmeldet, muss dafür in allowed-clients stehen.