Keycloak: Custom E-Mail-Theme fuer Einladungen (Frontend-Styling, executeActions) #203

Closed
opened 2026-07-03 18:13:11 +02:00 by niboer · 0 comments
Owner

Was wir erreichen wollen

Die Einladungs-E-Mail, die aktuell beim POST /api/v1/persons/:person_id/invite-Endpunkt via Keycloaks execute-actions-email-API versendet wird, enthaelt nur den generischen Keycloak-Text "Update Your Account" ohne Bezug zur App.

Ziel: Eine optisch ansprechende E-Mail im Stil des Frontends (lila/gold-Design), die:

  • "Willkommen bei [KEYCLOAK_REALM_DISPLAY_NAME]" als Betreff und Begruessung anzeigt
  • Das App-Logo bzw. den App-Namen praesentiert
  • Einen prominenten Button/Link zum Setzen des Passworts enthaelt
  • Die Frontend-Farben und -Schriftart (Inter) verwendet
  • Nach dem Passwort-Setzen auf das Frontend-Dashboard weiterleitet

Der "Passwort vergessen"-Flow bleibt dabei unveraendert, da Keycloak dafuer unabhaengige Message-Keys (passwordReset*) verwendet.

Wie wir es umsetzen

Keycloak verwendet fuer die execute-actions-email-Admin-API die Message-Keys executeActionsSubject/executeActionsBody/executeActionsBodyHtml — unabhaengig von passwordReset*. Daher koennen wir die Einladungs-Mail isoliert anpassen, indem wir ein Email Theme im bestehenden Custom-Theme familienfeierplaner anlegen.

1. Email Theme anlegen (6 neue Dateien)

Datei Zweck
keycloak/themes/familienfeierplaner/email/theme.properties parent=keycloak.v2 – erbt von Keycloaks Base-Email-Theme
keycloak/themes/familienfeierplaner/email/html/template.ftl HTML-Wrapper: Inline-CSS mit Design-Tokens (#9a47ab primary, #d4a61e gold, #f9f0fb bg, Inter), Header aus realm.displayName, Footer
keycloak/themes/familienfeierplaner/email/html/executeActions.ftl "Willkommen bei ${realm.displayName!realmName}" + Button zum Passwort-Setzen + Ablauf-Hinweis
keycloak/themes/familienfeierplaner/email/text/executeActions.ftl Plaintext-Fallback
keycloak/themes/familienfeierplaner/email/messages/messages_en.properties executeActionsSubject=Welcome to {2} - Set your password
keycloak/themes/familienfeierplaner/email/messages/messages_de.properties executeActionsSubject=Willkommen bei {2} - Passwort setzen

2. Backend anpassen (2 Dateien geaendert)

backend/src/lib/keycloak-provision.ts

  • REALM_DEFAULTS um emailTheme: "familienfeierplaner" ergaenzen (neben bestehendem loginTheme/accountTheme)

backend/src/routes/persons.ts (Z. 548-552)

  • execute-actions-email-Call erweitern: statt nur ['UPDATE_PASSWORD'] ein Objekt senden mit:
    • actions: ['UPDATE_PASSWORD']
    • redirect_uri: process.env.FRONTEND_ORIGIN + '/app/dashboard'
    • lifespan: 43200 (12 Stunden)
  • FRONTEND_ORIGIN aus process.env lesen (wird im Docker-Stack gesetzt)

3. Config anpassen (1 Datei)

docker-compose.yml (backend-api service section)

  • FRONTEND_ORIGIN: ${FRONTEND_ORIGIN:-http://localhost:5173} als Environment-Variable ergaenzen

4. Tests anpassen (1 Datei)

backend/src/__tests__/persons.test.ts

  • Mock-Erwartung fuer den execute-actions-email-Call anpassen: statt ['UPDATE_PASSWORD'] nun Objekt mit actions, redirect_uri, lifespan erwarten

5. Dokumentation ergaenzen (2 Dateien)

  • docs/design/05-authentifizierung.md – Abschnitt "E-Mail-Theme" ergaenzen (analog zu Login-/Account-Theme)
  • PROJECT_STATE.md – Status aktualisieren

Warum kein Custom Keycloak-SPI / nodemailer

  • Ein Java-SPI zur Definition einer neuen Required Action ist unverhaeltnismaessig aufwaendig (~1 Woche)
  • Nodemailer im Backend erfordert temporaere Passwoerter statt Action-Tokens (schlechtere UX)
  • Der reine Theme-Ansatz (emailTheme) nutzt die bestehende Keycloak-Email-Infrastruktur und Aendert nur Look & Feel

Technische Randbedingungen

  • Der bestehende Volume-Mount ./keycloak/themes:/opt/keycloak/themes in docker-compose.yml deckt das neue email/-Verzeichnis automatisch ab
  • Das Theme wird per Admin-API via emailTheme gesetzt (kein Volume-Mount fuer die Config noetig)
  • realm.displayName im FTL wird via KEYCLOAK_REALM_DISPLAY_NAME-Env-Var gesteuert
  • SVGs werden in E-Mails vermieden (schlechter Client-Support) – stattdessen textbasierter Header
## Was wir erreichen wollen Die Einladungs-E-Mail, die aktuell beim `POST /api/v1/persons/:person_id/invite`-Endpunkt via Keycloaks `execute-actions-email`-API versendet wird, enthaelt nur den generischen Keycloak-Text "Update Your Account" ohne Bezug zur App. **Ziel:** Eine optisch ansprechende E-Mail im Stil des Frontends (lila/gold-Design), die: - "Willkommen bei [KEYCLOAK_REALM_DISPLAY_NAME]" als Betreff und Begruessung anzeigt - Das App-Logo bzw. den App-Namen praesentiert - Einen prominenten Button/Link zum Setzen des Passworts enthaelt - Die Frontend-Farben und -Schriftart (Inter) verwendet - Nach dem Passwort-Setzen auf das Frontend-Dashboard weiterleitet Der "Passwort vergessen"-Flow bleibt dabei **unveraendert**, da Keycloak dafuer unabhaengige Message-Keys (`passwordReset*`) verwendet. ## Wie wir es umsetzen Keycloak verwendet fuer die `execute-actions-email`-Admin-API die Message-Keys `executeActionsSubject`/`executeActionsBody`/`executeActionsBodyHtml` — unabhaengig von `passwordReset*`. Daher koennen wir die Einladungs-Mail isoliert anpassen, indem wir ein **Email Theme** im bestehenden Custom-Theme `familienfeierplaner` anlegen. ### 1. Email Theme anlegen (6 neue Dateien) | Datei | Zweck | |---|---| | `keycloak/themes/familienfeierplaner/email/theme.properties` | `parent=keycloak.v2` – erbt von Keycloaks Base-Email-Theme | | `keycloak/themes/familienfeierplaner/email/html/template.ftl` | HTML-Wrapper: Inline-CSS mit Design-Tokens (`#9a47ab` primary, `#d4a61e` gold, `#f9f0fb` bg, Inter), Header aus `realm.displayName`, Footer | | `keycloak/themes/familienfeierplaner/email/html/executeActions.ftl` | "Willkommen bei `${realm.displayName!realmName}`" + Button zum Passwort-Setzen + Ablauf-Hinweis | | `keycloak/themes/familienfeierplaner/email/text/executeActions.ftl` | Plaintext-Fallback | | `keycloak/themes/familienfeierplaner/email/messages/messages_en.properties` | `executeActionsSubject=Welcome to {2} - Set your password` | | `keycloak/themes/familienfeierplaner/email/messages/messages_de.properties` | `executeActionsSubject=Willkommen bei {2} - Passwort setzen` | ### 2. Backend anpassen (2 Dateien geaendert) **`backend/src/lib/keycloak-provision.ts`** - `REALM_DEFAULTS` um `emailTheme: "familienfeierplaner"` ergaenzen (neben bestehendem `loginTheme`/`accountTheme`) **`backend/src/routes/persons.ts` (Z. 548-552)** - `execute-actions-email`-Call erweitern: statt nur `['UPDATE_PASSWORD']` ein Objekt senden mit: - `actions: ['UPDATE_PASSWORD']` - `redirect_uri: process.env.FRONTEND_ORIGIN + '/app/dashboard'` - `lifespan: 43200` (12 Stunden) - `FRONTEND_ORIGIN` aus `process.env` lesen (wird im Docker-Stack gesetzt) ### 3. Config anpassen (1 Datei) **`docker-compose.yml`** (backend-api service section) - `FRONTEND_ORIGIN: ${FRONTEND_ORIGIN:-http://localhost:5173}` als Environment-Variable ergaenzen ### 4. Tests anpassen (1 Datei) **`backend/src/__tests__/persons.test.ts`** - Mock-Erwartung fuer den `execute-actions-email`-Call anpassen: statt `['UPDATE_PASSWORD']` nun Objekt mit `actions`, `redirect_uri`, `lifespan` erwarten ### 5. Dokumentation ergaenzen (2 Dateien) - **`docs/design/05-authentifizierung.md`** – Abschnitt "E-Mail-Theme" ergaenzen (analog zu Login-/Account-Theme) - **`PROJECT_STATE.md`** – Status aktualisieren ### Warum kein Custom Keycloak-SPI / nodemailer - Ein Java-SPI zur Definition einer neuen Required Action ist unverhaeltnismaessig aufwaendig (~1 Woche) - Nodemailer im Backend erfordert temporaere Passwoerter statt Action-Tokens (schlechtere UX) - Der reine Theme-Ansatz (`emailTheme`) nutzt die bestehende Keycloak-Email-Infrastruktur und Aendert nur Look & Feel ### Technische Randbedingungen - Der bestehende Volume-Mount `./keycloak/themes:/opt/keycloak/themes` in `docker-compose.yml` deckt das neue `email/`-Verzeichnis automatisch ab - Das Theme wird per Admin-API via `emailTheme` gesetzt (kein Volume-Mount fuer die Config noetig) - `realm.displayName` im FTL wird via `KEYCLOAK_REALM_DISPLAY_NAME`-Env-Var gesteuert - SVGs werden in E-Mails vermieden (schlechter Client-Support) – stattdessen textbasierter Header
niboer self-assigned this 2026-07-03 18:13:11 +02:00
Sign in to join this conversation.
No description provided.