Zum Inhalt

Keycloak als Identity-Middleware

Wittgenstein implementiert weder Login noch Passwortspeicherung noch Föderation selbst. Die Plattform stellt Keycloak zwischen jeden Benutzer, jede Anwendung und jede vorgelagerte Identitätsquelle und behandelt es als Identity-Middleware: Anwendungen sprechen ein einziges Protokoll (OIDC) mit einer einzigen Stelle, und Keycloak passt sich an das an, was jede Organisation bereits nutzt.

flowchart LR
    subgraph Sources["Identitätsquellen"]
        G[Social- / Workspace-IdP]
        E[Enterprise-IdP OIDC oder SAML]
        L[LDAP / Active Directory]
        P[Lokale Benutzer + MFA]
    end

    KC(["Keycloak<br/>ein Realm pro Mandant"])

    subgraph Consumers["Konsumenten"]
        A[Web-Anwendungen<br/>OIDC + PKCE]
        S[Dienste & Agenten<br/>Client Credentials]
        T[Werkzeuge<br/>SAML / OIDC]
    end

    G --> KC
    E --> KC
    L --> KC
    P --> KC
    KC --> A
    KC --> S
    KC --> T
    KC -. JWKS .-> GW[AgentGateway<br/>+ extProc]
    GW --> A

Warum Middleware und keine Bibliothek

Anforderung Übernimmt Keycloak Auswirkung auf die Anwendung
„Unsere Benutzer melden sich mit dem Unternehmens-IdP an“ Identity Brokering (OIDC / SAML) Keine — die App erhält weiterhin ein Keycloak-Token
„Wir haben bereits ein Verzeichnis“ LDAP- / Active-Directory-Föderation Keine
„Anmeldung mit einem Workspace-Konto“ Social-/Workspace-IdP, optional auf eine Domain beschränkt Keine
„Zweiter Faktor für Administratoren“ Authentifizierungs-Flows, MFA Keine
„Verschiedene Kunden dürfen die Benutzer der anderen nie sehen“ Ein Realm pro Mandant App auf einen anderen authority zeigen lassen
„Auch Maschinen und Agenten brauchen Tokens“ Service-Account-Clients (client_credentials) Derselbe JWT-Prüfpfad
„Standardwerkzeuge brauchen SSO“ SAML- und OIDC-Clients Einmal im Realm konfiguriert

Der Anwendungscode ist in allen Zeilen identisch. Genau das erlaubt es einer Plattform, Kunden mit sehr unterschiedlichen Identitätslandschaften zu bedienen, ohne etwas zu forken.

Mandantenmodell

  • Ein Realm pro Mandant, wenn Isolation von Benutzern, Richtlinien und Branding erforderlich ist. Jeder Realm hat eigene Benutzer, IdPs, MFA-Richtlinie, Token-Laufzeiten und Signaturschlüssel.
  • Ein Client pro Anwendung innerhalb des Realms, mit möglichst wenigen Redirect-URIs und Scopes.
  • Rollen und Gruppen steuern die Autorisierung. Realm-Rollen kommen in realm_access.roles, Gruppen im Claim groups; wittgenstein-oidc-backend stellt sie über get_roles und get_groups bereit, und @has_role([...]) erzwingt sie pro Route.
from wittgenstein_oidc_backend import get_current_payload, get_groups, get_roles

payload = get_current_payload()
roles = get_roles(payload)      # ["manager", ...]
groups = get_groups(payload)    # ["/customers/acme", ...]

Fachliche Bedeutung bleibt in Ihrem Dienst

Die Bibliothek extrahiert Claims generisch. Einen Gruppennamen zu interpretieren (z. B. auf ein Kundenkonto abzubilden) ist Geschäftslogik Ihrer Anwendung, nicht der Plattform.

Client-Typen und Grants

Client Typ Grant Hinweise
Browser-Anwendung Vertraulich über das Backend (oder öffentlich + PKCE) Authorization Code + PKCE Das Backend hält das Secret; siehe OIDC & Token-Sicherheit
Backend / Agent, der einen anderen Dienst aufruft Vertraulich client_credentials Service-Account mit eng begrenzten Rollen
Admin-Automatisierung Vertraulich client_credentials Nur die benötigten Realm-Management-Rollen
Drittanbieter-Werkzeug SAML oder OIDC Je nach Werkzeug Einmal pro Realm registriert

Direct Access Grants (Password-Grant) sind bei Browser-Clients deaktiviert, sodass Zugangsdaten nur auf den eigenen Seiten von Keycloak eingegeben werden können, wo MFA und Brute-Force-Schutz greifen.

Identitäten in Ihre Anwendung abbilden

  1. Claims — Protocol Mapper fügen dem Token die Rollen, Gruppen und Attribute hinzu, die Ihre Dienste benötigen.
  2. JIT-Provisionierung — beim ersten Login legt jit_provision_user den lokalen Benutzer an, verknüpft die Keycloak-sub und synchronisiert die Rollen bei jedem weiteren Login.
  3. Gateway-Header — hinter AgentGateway validiert der extProc-Sidecar das JWT gegen das JWKS und setzt x-user-id und x-user-name, sodass Dienste, die nur wissen müssen „wer ruft auf“, nie ein Token parsen müssen.

Automatisierte, wiederholbare Realms

Realms werden durch idempotente Skripte aufgesetzt, nicht von Hand zusammengeklickt: Ein erneuter Lauf legt Fehlendes an und aktualisiert Abweichendes (Identity Provider werden per Upsert behandelt, nicht nur angelegt). Ein neuer Mandanten-Realm ist damit eine geprüfte Änderung und kein manuelles Verfahren (die folgende Ausgabe ist gekürzt).

./setup-keycloak.sh[keycloak] realm 'acme' ... created
[keycloak] identity provider 'workspace' ... created
[keycloak] client 'acme-app' ... created (confidential, PKCE S256)
[keycloak] client 'acme-service' ... created (service account)
[keycloak] realm role 'manager' ... created
./setup-keycloak.sh[keycloak] realm 'acme' ... exists, skipped
[keycloak] identity provider 'workspace' ... updated
[keycloak] client 'acme-app' ... exists, skipped

Betrieb von Keycloak

  • TLS — entweder von AgentGateway terminiert (der Identity Provider läuft dahinter im Edge-Proxy-Modus) oder von Keycloak selbst auf einem dedizierten Host, mit automatisch ausgestellten und erneuerten Zertifikaten.
  • Speicher — eine dedizierte PostgreSQL-Datenbank; nichts sonst teilt sie sich.
  • Discovery — jeder Realm veröffentlicht /.well-known/openid-configuration; in Anwendungen ist kein Endpunkt fest verdrahtet.
curl -s https://auth.example.com/realms/acme/.well-known/openid-configuration | jq '{issuer, jwks_uri, token_endpoint}'{
"issuer": "https://auth.example.com/realms/acme",
"jwks_uri": "https://auth.example.com/realms/acme/protocol/openid-connect/certs",
"token_endpoint": "https://auth.example.com/realms/acme/protocol/openid-connect/token"
}
curl -s -X POST https://auth.example.com/realms/acme/protocol/openid-connect/token \ -d grant_type=client_credentials -d client_id=acme-service -d client_secret="$SECRET" | jq 'keys'[
"access_token",
"expires_in",
"token_type"
]

Einen neuen Mandanten hinzufügen

Neuer Realm → dessen IdP anbinden (oder lokale Benutzer mit MFA nutzen) → Anwendungs-Client anlegen → der App den neuen authority geben. Keine Änderung am Anwendungscode.