Aller au contenu

Connexion unique

La connexion unique permet à un fournisseur d’identité (IdP) externe de décider qui peut se connecter à votre serveur Control Center. Deux protocoles sont pris en charge — SAML 2.0 et OpenID Connect — et tous deux aboutissent au même endroit : un identifiant d’appareil fraîchement émis, indiscernable d’une invitation ou d’un appairage par QR partout en aval.

Les connexions SAML passent par le navigateur. L’IdP n’ouvre jamais de connexion vers votre serveur ; c’est le navigateur de l’utilisateur qui transporte tout :

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

Parce que c’est le navigateur qui fait le coursier, un serveur derrière un NAT fonctionne parfaitement pour les connexions : localhost, réseau local, Tailscale, VPN ou tunnel font tous l’affaire — seul le navigateur de l’utilisateur doit pouvoir joindre l’URL ACS. Le relais WebSocket utilisé par le contrôle à distance depuis un téléphone ne peut pas transporter l’aller-retour HTTP, et c’est pourquoi les points d’entrée SSO ciblent toujours l’origine HTTP du serveur.

OpenID Connect suit la même forme avec un aller-retour code d’autorisation + PKCE ; les claims sont lus sur le point de terminaison de jeton de l’émetteur via TLS, si bien que le navigateur ne fournit jamais la moindre donnée d’identité.

Control Center répartit délibérément les responsabilités :

Élément Emplacement Responsabilité
cc_saml (natif) packages/cc_natives/native/saml/ Vérification XML-DSig, canonicalisation, validation du profil SAML — déléguées à la crate saml en Rust pur épinglée derrière une ABI C sans état
SamlService / OidcService cc_server_core état de connexion (suivi des requêtes en attente, déduplication des rejeux), réglages de la politique de validation, l’appel de provisionnement partagé
SsoProvisioner cc_server_core trouver ou créer l’utilisateur, épingler le sujet du fournisseur, accorder les adhésions selon la politique d’adhésion automatique
ScimService cc_server_core l’interface de push SCIM 2.0 (cycle de vie provisionnement/déprovisionnement)
SsoSettingsService cc_server_core les lignes de connexion enregistrées, la réapplication à chaud, le jeton SCIM

Il n’existe délibérément aucun repli cryptographique SAML en pur Dart : le XML-DSig et la canonicalisation écrits à la main sont précisément là où vivent les vulnérabilités de contournement de signature, si bien qu’une bibliothèque cc_saml manquante fait échouer à la fois la connexion SAML et le démarrage du serveur. Le natif est sans état par conception — l’état inter-requêtes réside dans les services Dart, à usage unique et de courte durée.

Dans le natif vérifié et dans le service, dans l’ordre :

  1. la signature de l’assertion (toujours exigée ; celle de l’enveloppe de réponse est facultative selon la configuration) — structurellement résistante au contournement de signature, car la charge utile validée est liée à l’élément vérifié,
  2. l’émetteur correspond à l’IdP configuré,
  3. l’audience désigne ce fournisseur de service,
  4. la destination est bien notre URL ACS,
  5. InResponseTo correspond à une AuthnRequest en attente que nous avons émise (les réponses non sollicitées, initiées par l’IdP, sont désactivées par défaut),
  6. les fenêtres de durée de vie tiennent dans le décalage d’horloge toléré (90 s par défaut) et
  7. l’identifiant d’assertion n’a jamais été vu auparavant (cache anti-rejeu).

Les échecs reviennent sous forme de codes typés — signature, expired, audience, destination, issuer, request-match — et les points de terminaison répondent par des erreurs génériques, de sorte qu’un attaquant n’apprend rien sur la raison de l’échec d’une falsification.

La première connexion SSO épingle sur le compte le sujet immuable du fournisseur (issuer, plus le NameID SAML / le sub OIDC, ou l’externalId SCIM). À partir de là, c’est le sujet — et non l’adresse e-mail — qui identifie la connexion. Si quelqu’un d’autre présente ensuite la même adresse e-mail depuis le même IdP ou un autre, le compte épinglé le refuse. C’est ce qui empêche une adresse e-mail recyclée chez un fournisseur de se transformer en prise de contrôle de compte.

Le provisionnement JIT a lieu à la connexion (SsoProvisioner). SCIM est une surface de push distincte (ScimService) qui épingle le même sujet sur la ligne utilisateur ; elle n’accorde pas d’adhésion à un espace de travail. Le déprovisionnement — active: false ou DELETE via SCIM — est refusé si le compte est propriétaire d’un espace de travail (transférez d’abord). Sinon, il révoque d’abord chaque identifiant d’appareil (les sessions tombent en quelques secondes via la surveillance des appareils), retire toutes les adhésions aux espaces de travail et marque le compte comme désactivé. La ligne utilisateur survit : l’attribution est permanente. La réactivation restaure la connexion mais ne réaccorde jamais les adhésions.

  • La correspondance groupe→rôle se fait à la connexion, à partir des attributs SAML / claims OIDC — les groupes poussés par SCIM ne sont pas stockés.
  • Le rôle de propriétaire est inaccessible depuis le SSO — ni comme rôle par défaut, ni par une correspondance de groupe.
  • Le relais WebSocket n’est jamais un canal SSO ; les téléphones qui ne joignent le serveur que par le relais ne peuvent pas effectuer d’aller-retour navigateur contre lui.