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 Claimgroups;wittgenstein-oidc-backendstellt sie überget_rolesundget_groupsbereit, 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
- Claims — Protocol Mapper fügen dem Token die Rollen, Gruppen und Attribute hinzu, die Ihre Dienste benötigen.
- JIT-Provisionierung — beim ersten Login legt
jit_provision_userden lokalen Benutzer an, verknüpft die Keycloak-subund synchronisiert die Rollen bei jedem weiteren Login. - Gateway-Header — hinter AgentGateway validiert der
extProc-Sidecar das JWT gegen das JWKS und setztx-user-idundx-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).
[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.
"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.