Zum Inhalt

E-Mail-Verifizierung & Multi-Faktor-Authentifizierung

Eine perfekte Token-Pipeline nützt nichts, wenn die falsche Person ein Token erhalten kann. Wittgenstein kombiniert deshalb seine Bibliotheken zur Token-Verarbeitung mit starker Identitätssicherung am Identity Provider: Jedes Konto ist an eine verifizierte E-Mail-Adresse gebunden, und sensibler Zugriff erfordert einen zweiten Faktor.

Nichts davon steckt im Anwendungscode. Es wird von Keycloak erzwungen, bevor überhaupt ein Token ausgestellt wird — jede Anwendung der Plattform erbt es und kann es nicht versehentlich abschwächen.

Die Sicherungsleiter

Stufe Was Keycloak nachweist Durchgesetzt durch
1 — Verifizierte Identität Der Benutzer kontrolliert das hinterlegte Postfach Required Action VERIFY_EMAIL
2 — Verifizierte Identität + Besitz …und besitzt einen registrierten zweiten Faktor TOTP-Authenticator oder WebAuthn / Passkey
Wiederherstellung Ein verlorener Faktor oder ein Passwort lässt sich zurücksetzen, ohne dass ein Administrator ein Geheimnis anfasst Signierte, ablaufende E-Mail-Aktionslinks

Anwendungen lesen die erreichte Stufe aus dem Token (Claim acr) und entscheiden, was jede Route verlangt.

E-Mail-Verifizierung

Konten werden unverifiziert angelegt. Solange der Benutzer den Besitz der Adresse nicht nachweist, schließt Keycloak den Login nicht ab — die Required Action VERIFY_EMAIL unterbricht den Ablauf und versendet einen signierten Link.

sequenceDiagram
    participant U as Benutzer
    participant KC as Keycloak
    participant SMTP as Mail-Relay
    participant M as Postfach

    U->>KC: Erster Login (Passwort)
    KC->>KC: Required Action: VERIFY_EMAIL
    KC->>SMTP: Verifizierungs-E-Mail (signierter, ablaufender Link)
    SMTP->>M: Zustellung über authentifiziertes TLS
    U->>M: Öffnet die Nachricht, klickt auf den Link
    M->>KC: GET action-token
    KC->>KC: emailVerified = true
    KC-->>U: Login wird fortgesetzt → Token ausgestellt

Warum das wichtig ist:

  • Verhindert Tippfehler- und Squatting-Konten — niemand kann eine Adresse registrieren, die ihm nicht gehört.
  • Macht E-Mail zu einem vertrauenswürdigen Wiederherstellungskanal — Passwort- und Faktor-Resets gehen an ein nachgewiesenes Postfach, nicht an eine bloß eingetippte Adresse.
  • Macht email-Claims verwendbar — Dienste können sich beim Verknüpfen oder Provisionieren von Identitäten auf email_verified verlassen.

Die Zustellung übernimmt das plattformeigene authentifizierte SMTP-Relay, sodass Verifizierungs-Mails nicht von Dritten abhängen.

Multi-Faktor-Authentifizierung

Der Browser-Flow von Keycloak wird um einen zweiten Faktor erweitert. Unterstützte Faktoren:

Faktor Typ Hinweise
TOTP Authenticator-App (jede RFC-6238-App) Einrichtung über die Required Action CONFIGURE_TOTP
WebAuthn / Passkeys Hardware-Schlüssel, Plattform-Authenticator Phishing-resistent: Die Anmeldeinformation ist an den Origin der Website gebunden
Wiederherstellungscodes Einmalcodes Offline-Rückfall bei Geräteverlust

MFA lässt sich als Richtlinie statt als Einstellung pro Benutzer anwenden:

  • Immer — für alle Benutzer des Realms.
  • Nach Rolle oder Gruppe — z. B. wird jeder mit einer administrativen Rolle abgefragt, Nur-Lese-Benutzer nicht (bedingte Flows in Keycloak).
  • Step-up — der Benutzer ist auf Stufe 1 angemeldet und wird erst dann nach dem zweiten Faktor gefragt, wenn er etwas erreicht, das Stufe 2 verlangt.

Step-up in Ihrer API erzwingen

Keycloak hält die Authentifizierungsstufe im Claim acr fest. Da OidcMiddleware das Token der Anfrage bereits geparst hat, kann eine Route die stärkere Stufe in wenigen Zeilen verlangen:

from fastapi import Depends, HTTPException
from wittgenstein_oidc_backend import get_current_payload


def require_mfa(payload: dict = Depends(get_current_payload)) -> dict:
    if int(payload.get("acr", "0")) < 2:          # 2 = Passwort + zweiter Faktor
        raise HTTPException(
            status_code=401,
            detail="step-up required",
            headers={"WWW-Authenticate": 'Bearer error="insufficient_user_authentication"'},
        )
    return payload


@app.delete("/tenants/{tenant_id}", dependencies=[Depends(require_mfa)])
async def delete_tenant(tenant_id: str): ...

Die SPA reagiert auf diese Antwort mit einem Redirect zu Keycloak mit acr_values=2; der Benutzer schließt den zweiten Faktor ab und kehrt mit einem Token zurück, das die Route erfüllt.

Sensible Kontoänderungen laufen nie über die Anwendung. Um ein Passwort zu ändern, bittet die App Keycloak, dem Benutzer einen Required-Action-Link zu mailen; der Benutzer wählt das neue Passwort auf der eigenen Seite von Keycloak, wo Passwortrichtlinie und Brute-Force-Schutz greifen. Die Anwendung sieht, speichert oder überträgt das Passwort nie.

curl -s -X PUT "https://auth.example.com/admin/realms/acme/users/$USER_ID/execute-actions-email?lifespan=3600" \ -H "Authorization: Bearer $ADMIN_TOKEN" -H "Content-Type: application/json" \ -d '["UPDATE_PASSWORD"]' -o /dev/null -w "%{http_code}\n"204# der Benutzer hat jetzt einen einstündigen Einmal-Link im Postfach

Derselbe Mechanismus kann CONFIGURE_TOTP (Faktor neu einrichten), VERIFY_EMAIL (erneut verifizieren) oder UPDATE_PROFILE anfordern. Die Links sind signiert, zweckgebunden und kurzlebig.

Minimale Rechte für den Aufrufer

Der Dienst, der diese E-Mails auslöst, nutzt einen dedizierten Service-Account-Client mit nur den benötigten Realm-Management-Rollen (view-users, manage-users), nie mit einer vollständigen Administrator-Berechtigung.

Härtung von Sitzung und Sperre

Die Sicherung ist nur so stark wie die Sitzung, die darauf folgt:

  • Kurzlebige Access-Tokens (Minuten), mit gleitendem Idle-Timeout und absolutem Sitzungsmaximum.
  • Rotation des Refresh-Tokens, das in einem HttpOnly-Cookie liegt (siehe OIDC & Token-Sicherheit).
  • Brute-Force-Erkennung sperrt ein Konto nach wiederholten Fehlversuchen vorübergehend, sodass ein zweiter Faktor nicht durch Raten ausgehebelt werden kann.
  • Passwortrichtlinie (Länge, Historie, Sperrliste), von Keycloak an einer einzigen Stelle ausgewertet.

Konfiguration prüfen

curl -s https://auth.example.com/realms/acme/.well-known/openid-configuration | jq '.acr_values_supported'[
"0",
"1",
"2"
]
curl -s -H "Authorization: Bearer $TOKEN" https://app.example.com/api/tenants/t-1 -X DELETE -o /dev/null -w "%{http_code}\n"401# ein Stufe-1-Token wird an einer Stufe-2-Route abgewiesen, bis der Benutzer MFA abschließt

MFA sicher einführen

Beginnen Sie mit den administrativen Rollen, prüfen Sie, dass Wiederherstellungscodes und E-Mail-Links durchgängig funktionieren, und weiten Sie die Richtlinie erst dann aus. Da die Durchsetzung im IdP liegt, erfordert die Ausweitung nie ein Deployment einer Anwendung.