TL;DR
Die Implementierung von Enterprise Single Sign-On erfordert eine fundierte Entscheidung zwischen SAML 2.0 und OpenID Connect, wobei jedes Protokoll spezifische Stärken und Anwendungsfälle aufweist. SAML 2.0, entwickelt 2005, eignet sich mit seinem XML-Format und breiter App-Kompatibilität besonders für Legacy-Enterprise-Anwendungen wie SAP oder Salesforce. OpenID Connect (OIDC), seit 2014 auf OAuth 2.0 basierend, nutzt JSON/JWT und ist ideal für moderne Web- und Mobile-Apps sowie APIs. Der Artikel beleuchtet detailliert den SP-initiierten SAML-Ablauf und den empfohlenen OIDC Authorization Code Flow mit PKCE, inklusive der Struktur von JWT-Tokens. Zudem werden kritische Sicherheitsprobleme wie XML-Signature-Wrapping bei SAML und deren Schutzmaßnahmen aufgezeigt.
Diese Zusammenfassung wurde KI-gestützt erstellt (EU AI Act Art. 50).
Inhaltsverzeichnis (7 Abschnitte)
Single Sign-On ist heute Standard in Unternehmensumgebungen - aber die Implementierung birgt zahlreiche Fallstricke. Ob SAML oder OIDC, Keycloak oder Azure AD: jede Entscheidung hat Konsequenzen für Sicherheit, Usability und Wartbarkeit. Dieser Guide zeigt die vollständige SSO-Implementierung.
SAML 2.0 vs. OpenID Connect: Die Wahl
Welches Protokoll für welchen Use Case?
SAML 2.0 (Security Assertion Markup Language):
- Entwickelt: 2005 (für Browser-basierte Enterprise-Apps)
- Format: XML (komplex, aber vollständig)
- Stärken: Enterprise-Standard, breite App-Kompatibilität
- Schwächen: kein nativer Mobile-Support, XML-Overhead
- Typische Use Cases: SharePoint, SAP, Salesforce, ServiceNow, Legacy Enterprise Applications, Behörden und stark regulierte Branchen
OpenID Connect (OIDC):
- Entwickelt: 2014 (über OAuth 2.0 aufgebaut)
- Format: JSON/JWT (einfach, flexibel)
- Stärken: Modern, Mobile-freundlich, APIs, SPA
- Schwächen: Jüngere Apps fehlen teils noch
- Typische Use Cases: Web-Apps (React, Vue, Angular), Mobile Apps (iOS, Android), APIs und Microservices, Google/GitHub/Microsoft Auth
Entscheidungsmatrix
| App-Typ | SAML | OIDC |
|---|---|---|
| Browser-SPA | nein | ja |
| Mobile App | nein | ja |
| Legacy Enterprise | ja | selten |
| Microservices | nein | ja |
| Office 365 | beide | beide (OIDC bevorzugt) |
| SAP/Oracle | ja | nein |
SAML 2.0 im Detail
SAML-Ablauf (SP-initiiert - Standard)
Akteure:
- User: Benutzer (Browser)
- SP: Service Provider = App (z.B. Salesforce)
- IdP: Identity Provider = Auth-Server (z.B. Keycloak, AD FS)
SP-initiierter Flow:
- User ruft salesforce.com/protected auf
- Salesforce erkennt kein gültiges SAML-Session und leitet zum IdP weiter; die Redirect-URL enthält den SAMLRequest als base64-encodiertes XML sowie einen RelayState
- Der IdP zeigt die Login-Seite (sofern kein Session besteht)
- User gibt Username, Passwort und MFA ein
- IdP erstellt eine SAML Assertion, signiert mit seinem Private Key:
<saml:Assertion>
<saml:AttributeStatement>
<saml:Attribute Name="email">
<saml:AttributeValue>user@company.com</saml:AttributeValue>
</saml:Attribute>
<saml:Attribute Name="groups">
<saml:AttributeValue>Salesforce-Admins</saml:AttributeValue>
</saml:Attribute>
</saml:AttributeStatement>
<saml:AuthnStatement SessionIndex="_abc123">
<saml:AuthnContext>
<saml:AuthnContextClassRef>
urn:oasis:names:tc:SAML:2.0:ac:classes:TimeSyncToken
</saml:AuthnContextClassRef>
</saml:AuthnContext>
</saml:AuthnStatement>
</saml:Assertion>
- IdP sendet die SAMLResponse per POST an die ACS-URL des SP
- SP validiert die Signatur mit dem IdP-Zertifikat und erstellt eine lokale Session
Wichtige SAML-Parameter
| Parameter | Beschreibung |
|---|---|
| NameID Format | emailAddress (empfohlen), persistent, transient |
| ACS-URL | Assertion Consumer Service URL des SP |
| Entity ID | Eindeutige SP-Identifikation |
| RelayState | Ursprüngliche URL nach Auth |
| NotBefore/After | Zeitfenster der Assertion (max. 5 Min.!) |
SAML-Sicherheitsprobleme
1. XML-Signature-Wrapping (XSW): Ein Angreifer wickelt eine bösartige Assertion um eine legitime. Der SP validiert die legitime, nutzt aber die bösartige. Schutz: xpath-basierte Signatur-Validierung und Zertifikat-Pinning.
2. Assertion-Replay: Eine gültige Assertion kann mehrfach eingereicht werden. Schutz: InResponseTo-Prüfung und Assertion-ID-Cache mit einer Stunde Gültigkeit.
OpenID Connect (OIDC) und OAuth 2.0
Authorization Code Flow mit PKCE (Empfehlung 2024)
Akteure:
- User: Endnutzer
- Client: Web-App / Mobile App
- Authorization Server: IdP (Keycloak, Auth0, etc.)
- Resource Server: API (mit Access Token geschützt)
Der Ablauf beginnt damit, dass der Client einen zufälligen code_verifier (43-128 Zeichen, URL-safe) generiert und daraus per SHA256 den code_challenge ableitet. Der Authorization Request enthält dann neben response_type=code, client_id und redirect_uri auch scope=openid profile email groups, einen CSRF-Schutz-state-Parameter sowie den PKCE-code_challenge.
Nach der Authentifizierung des Users leitet der IdP mit einem kurzlebigen Authorization Code zurück zur App. Die App tauscht diesen Code gegen Tokens ein - ausschließlich Server-zu-Server, mit code_verifier zur PKCE-Verifikation. Die Antwort enthält:
access_token: JWT, gültig ca. 5 Minuten, für API-Anfragenid_token: signiertes JWT mit User-Informationenrefresh_token: für Token-Erneuerung ohne erneuten Login
JWT-Aufbau (Access Token)
{
"alg": "RS256", "typ": "JWT", "kid": "key-id-1"
}
{
"iss": "https://sso.company.com/realms/company",
"sub": "user-uuid-123",
"aud": ["my-app", "account"],
"exp": 1709550000,
"iat": 1709549700,
"email": "user@company.com",
"groups": ["admins", "developers"],
"preferred_username": "john.doe"
}
Die Signatur erfolgt mit RS256 und dem Private Key des IdP.
JWT-Validierung (Server-Side)
import jwt
# JWKS-Endpoint: public keys des IdP
jwks_uri = "https://sso.company.com/realms/company/protocol/openid-connect/certs"
jwks_client = jwt.PyJWKClient(jwks_uri)
signing_key = jwks_client.get_signing_key_from_jwt(token)
payload = jwt.decode(
token,
signing_key.key,
algorithms=["RS256"],
audience="my-app",
issuer="https://sso.company.com/realms/company"
)
# → Automatisch: exp, iat, iss, aud geprüft!
Keycloak als Open-Source Identity Provider
Installation (Docker)
# docker-compose.yml:
services:
keycloak:
image: quay.io/keycloak/keycloak:24.0.2
command: start-dev --import-realm
environment:
KEYCLOAK_ADMIN: admin
KEYCLOAK_ADMIN_PASSWORD: ${KC_ADMIN_PASSWORD}
KC_DB: postgres
KC_DB_URL: jdbc:postgresql://postgres:5432/keycloak
KC_DB_USERNAME: keycloak
KC_DB_PASSWORD: ${KC_DB_PASSWORD}
KC_HOSTNAME: sso.company.com
KC_PROXY: edge # Hinter Nginx/Traefik!
volumes:
- ./realm-export.json:/opt/keycloak/data/import/realm.json
Realm-Konzept
Keycloak trennt Mandanten über Realms. Für ein typisches Unternehmens-Setup empfiehlt sich ein Realm "company" für alle internen Anwendungen und ein separates Realm "customers" für externes Kunden-SSO. Das Realm "master" darf ausschließlich für die Keycloak-Administration selbst genutzt werden.
Client-Konfiguration (OIDC-Web-App)
Ein vertraulicher OIDC-Client wird mit folgenden Einstellungen angelegt:
- Client ID: my-app
- Client Protocol: openid-connect
- Access Type: confidential (mit Client Secret)
- Valid Redirect URIs: https://app.company.com/*
- Web Origins: https://app.company.com
- Standard Flow: ON (Authorization Code)
- Direct Access: OFF (kein Username+Password direkt)
Das Client Secret ist unter Clients > my-app > Credentials abrufbar.
Rollen und Gruppen
Keycloak unterscheidet zwischen Realm-Rollen, die für alle Clients gelten (z. B. admin, user, developer, viewer), und Client-Rollen, die app-spezifisch sind (z. B. my-app: admin, user, read-only).
Composite Roles bündeln mehrere Rollen: Die Rolle "developer" kann beispielsweise die Rollen user, deployer und read-only enthalten. Gruppen vereinfachen die Zuweisung: Die Gruppe "Engineering" erhält die Realm-Rolle "developer", womit alle Mitglieder automatisch die entsprechenden Rechte erhalten.
Attribute Mapper (Custom Claims in Token)
Über Mapper werden zusätzliche Claims in den Token geschrieben. Für Gruppen-Mitgliedschaften wird ein Mapper vom Typ "Group Membership" auf den Token Claim groups konfiguriert, optional mit vollem Pfad (z. B. /Engineering/Backend). Für Abteilungszugehörigkeiten wird ein "User Attribute" Mapper verwendet, der das Custom-Feld department aus dem User-Profil in den Token überträgt.
Active Directory Integration
LDAP/AD-Integration in Keycloak
Die LDAP-Federation wird unter User Federation > Add Provider > ldap konfiguriert. Die wichtigsten Parameter:
- Vendor: Active Directory
- Connection URL: ldaps://dc01.company.local:636
- Bind DN: CN=keycloak-svc,OU=ServiceAccounts,DC=company,DC=local
- User DN: OU=Users,DC=company,DC=local
- User Object Classes: person, organizationalPerson, user
- Import Users: ON (oder Read-Only Sync)
Der LDAP-Filter (&(objectClass=user)(!(userAccountControl:1.2.840.113556.1.4.803:=2))) schränkt die Synchronisation auf aktive Accounts ein.
Gruppen-Sync
Der group-ldap-mapper synchronisiert AD-Gruppen in Keycloak. Konfiguriert wird er mit dem LDAP Groups DN (z. B. OU=SecurityGroups,DC=company,DC=local), Group Object Class group und dem Membership-Attribut member. AD-Gruppen lassen sich dann über den Group Roles Mapper direkt auf Keycloak-Rollen abbilden (z. B. AD-Gruppe "SSO-Admins" → Keycloak-Rolle "admin").
Sync-Strategie: Ein Full Sync alle 4 Stunden und ein Changed Users Sync alle 15 Minuten sind ein praxistauglicher Kompromiss. Der Sync lässt sich über die Admin CLI auch manuell auslösen:
kcadm.sh trigger-ldap-full-sync --realm company \
--federation-id ldap-provider-id
Kerberos/SPNEGO (Seamless SSO für Windows-Clients): Wenn Keycloak mit Kerberos konfiguriert ist, können Windows-Nutzer im Firmennetz ohne manuellen Login auf Anwendungen zugreifen - der Browser sendet automatisch das Kerberos-Ticket. Funktioniert nativ in Chrome und Edge; Firefox benötigt eine zusätzliche Konfiguration.
MFA und Adaptive Authentication
TOTP einrichten
TOTP wird unter Authentication > Policies > OTP Policy konfiguriert. Als Algorithmus empfiehlt sich SHA256, als Periode 30 Sekunden. Um MFA im Browser-Flow zu erzwingen, wird im Authentication > Flows > Browser-Flow die OTP Form als REQUIRED hinzugefügt.
FIDO2/WebAuthn
Ab Keycloak 21 ist WebAuthn nativ unterstützt. Die Konfiguration unter Authentication > Policies > WebAuthn Policy umfasst den Relying Party Entity Name (z. B. company.com), Attestation Conveyance (direct), Authenticator Attachment (platform für geräteeigene Authenticatoren oder cross-platform für externe Hardware-Keys) und User Verification Requirement (required).
Adaptive Authentication (Risikobasiert)
Risikobasierte Regeln reagieren auf Kontext-Faktoren:
- Neues Gerät und neuer Standort: MFA erzwingen
- Login aus bekannter VPN-IP-Range: kein MFA nötig (trusted network)
- Mehr als 3 fehlgeschlagene Logins: CAPTCHA
- Login außerhalb der Geschäftszeiten: MFA und Alert
Keycloak unterstützt Conditional Flows: Ein "Condition - User Configured"-Element prüft ob der User bereits TOTP eingerichtet hat. Falls ja, wird TOTP angefordert; falls nein, wird ein Enrollment-Flow gestartet.
SSO-Migration ohne User-Disruption
Phasen-Migration (Zero-Downtime)
Phase 1: IdP-Deployment (keine User-Auswirkung)
Keycloak wird installiert und konfiguriert, der AD/LDAP-Sync eingerichtet und eine Pilot-Gruppe (typischerweise das IT-Team mit ~10 Usern) auf Test-Apps losgeschickt.
Phase 2: Neue Apps direkt mit SSO
Alle neuen Anwendungen werden ab sofort ausschließlich mit SSO integriert - kein eigenes Login-Formular mehr im Code.
Phase 3: Bestehende Apps migrieren
Just-In-Time Provisioning minimiert den Migrationsaufwand: Beim ersten Login via SSO prüft die App ob ein Konto mit der E-Mail-Adresse aus dem Token existiert. Falls ja, wird das Konto mit der SSO-Identität verknüpft; falls nein, wird ein neues Konto angelegt. Der User merkt davon kaum etwas.
def handle_oidc_callback(user_info):
email = user_info['email']
user = User.find_by_email(email)
if not user:
user = User.create(email=email, name=user_info['name'])
# SSO-Sub-ID speichern für zukünftige Logins:
user.sso_sub = user_info['sub']
user.save()
session['user_id'] = user.id
Phase 4: Passwort-Logins deaktivieren
Nach 90 Tagen werden lokale Passwörter deaktiviert. Für Service-Accounts und Notfälle bleibt ein dokumentierter Ausnahmeprozess bestehen.
Rollback-Plan
Ein Feature-Flag SSO_ENABLED = false schaltet bei Problemen auf lokalen Login zurück. Nach erfolgreicher Migration über mehrere Wochen wird das Flag entfernt.
Nächster Schritt
Unsere zertifizierten Sicherheitsexperten beraten Sie zu den Themen aus diesem Artikel — unverbindlich und kostenlos.
Kostenlos · 30 Minuten · Unverbindlich
