Zum Inhalt springen

Single Sign-on

Single Sign-on lässt einen externen Identitätsanbieter (IdP) entscheiden, wer sich an deinem Control Center-Server anmelden darf. Zwei Protokolle werden unterstützt — SAML 2.0 und OpenID Connect — und beide landen an derselben Stelle: frisch ausgestellte Geräteanmeldedaten, die sich danach nirgends von einer Einladung oder einer QR-Kopplung unterscheiden lassen.

SAML-Anmeldungen sind über den Browser vermittelt. Der IdP öffnet nie selbst eine Verbindung zu deinem Server; der Browser der anmeldenden Person trägt alles:

browser ──> /saml/login ──> IdP ──POST SAMLResponse──> /saml/acs
│ verify
▼
provision user (maybe)
│
▼
browser handoff: device PSK in the URL
fragment (never a query parameter)

Weil der Browser der Bote ist, funktioniert ein Server hinter NAT für Anmeldungen problemlos: localhost, LAN, Tailscale, VPN oder ein Tunnel reichen alle — nur der Browser der Person muss die ACS-URL erreichen. Das WebSocket-Relay der Telefon-Fernsteuerung kann den HTTP-Roundtrip nicht tragen. Deshalb zielen die SSO-Einstiegspunkte immer auf den HTTP-Origin des Servers.

OpenID Connect folgt derselben Form, mit einem Roundtrip aus Authorization Code und PKCE. Die Claims liest der Server über TLS vom Token-Endpunkt des Issuers, der Browser liefert also überhaupt keine Identitätsdaten.

Control Center teilt die Verantwortung bewusst auf:

Baustein Ort Verantwortet
Native-Bibliothek cc_saml packages/cc_natives/native/saml/ Prüfung von XML-DSig, Kanonisierung, Validierung des SAML-Profils — delegiert an die gepinnte Crate saml in reinem Rust hinter einem zustandslosen C-ABI
SamlService / OidcService cc_server_core Anmeldezustand (Tracker für offene Requests, Replay-Deduplizierung), Stellschrauben der Validierungsrichtlinie, der gemeinsame Provisionierungsaufruf
SsoProvisioner cc_server_core den User finden oder anlegen, das Subject des Anbieters festschreiben, Mitgliedschaften nach der Auto-Member-Richtlinie vergeben
ScimService cc_server_core die Push-Oberfläche von SCIM 2.0 (Lebenszyklus von Provisionierung und Deprovisionierung)
SsoSettingsService cc_server_core die gespeicherten Verbindungszeilen, das sofortige erneute Anwenden, das SCIM-Token

Absichtlich gibt es keinen SAML-Krypto-Fallback in reinem Dart: von Hand geschriebene XML-DSig- und Kanonisierungslogik ist genau der Ort, an dem Signature-Wrapping-Lücken sitzen. Fehlt die Bibliothek cc_saml, lehnt der Server sowohl die SAML-Anmeldung als auch den Start ab. Das Native ist absichtlich zustandslos — Zustand über Requests hinweg liegt in den Dart-Services, einmal verwendbar und kurzlebig.

Im geprüften Native und im Service, in dieser Reihenfolge:

  1. die Signatur der Assertion (immer erforderlich; die des Antwort-Umschlags ist je nach Konfiguration optional) — strukturell gegen Signature Wrapping geschützt, weil die geprüfte Nutzlast an das verifizierte Element gebunden ist,
  2. der Issuer dem konfigurierten IdP entspricht,
  3. die Audience diesen Service Provider nennt,
  4. die Destination unsere ACS-URL ist,
  5. InResponseTo zu einem offenen AuthnRequest passt, den wir ausgestellt haben (unaufgeforderte, vom IdP initiierte Antworten sind standardmäßig aus),
  6. die Gültigkeitsfenster innerhalb der Zeitabweichung halten (Vorgabe 90 s) und
  7. die Assertion-ID noch nie gesehen wurde (Replay-Cache).

Fehler kommen als typisierte Codes zurück — signature, expired, audience, destination, issuer, request-match — und die Endpunkte antworten mit allgemeinen Fehlern. Ein Angreifer erfährt also nichts darüber, warum eine Fälschung gescheitert ist.

Die erste SSO-Anmeldung schreibt das unveränderliche Subject des Anbieters (issuer plus SAML-NameID / OIDC-sub oder SCIM-externalId) am Konto fest. Danach identifiziert das Subject die Anmeldung, nicht die E-Mail. Legt später jemand dieselbe E-Mail vom selben oder einem anderen IdP vor, lehnt das festgeschriebene Konto ab. Genau das verhindert, dass eine beim Anbieter wiedervergebene E-Mail zur Kontoübernahme wird.

Die JIT-Provisionierung passiert bei der Anmeldung (SsoProvisioner). SCIM ist eine eigene Push-Oberfläche (ScimService), die dasselbe Subject an der User-Zeile festschreibt; Mitgliedschaften im Arbeitsbereich vergibt sie nicht. Die Deprovisionierung — SCIM active: false oder DELETE — lehnt ab, wenn das Konto Eigentümer eines Arbeitsbereichs ist (zuerst übertragen). Sonst widerruft sie zuerst alle Geräteanmeldedaten (Sitzungen fallen innerhalb von Sekunden über die Geräteüberwachung weg), entfernt jede Mitgliedschaft im Arbeitsbereich und stempelt das Konto als deaktiviert. Die User-Zeile bleibt: die Zuordnung ist dauerhaft. Eine Reaktivierung stellt die Anmeldung wieder her, vergibt Mitgliedschaften aber nie erneut.

  • Die Zuordnung Gruppe→Rolle passiert bei der Anmeldung aus den SAML-Attributen bzw. den OIDC-Claims — per Push gelieferte SCIM-Gruppen werden nicht gespeichert.
  • Die Rolle Eigentümer ist über SSO nicht erreichbar — weder als Standardrolle noch über eine Gruppenzuordnung.
  • Das WebSocket-Relay ist nie ein SSO-Kanal. Telefone, die den Server nur über das Relay erreichen, können darüber keinen Browser-Roundtrip zu Ende bringen.