Journal

SAML pour un client entreprise, en un après-midi

Un client d'entreprise veut se connecter via Entra ID ou Okta. Ce que cela demande avec EAuth, ce que fait son service informatique, et ce qui manque encore.

Le premier client d'entreprise qui dit « nos collaborateurs se connectent via Entra ID », c'est en général le moment où un petit produit découvre ce que coûte un logiciel d'entreprise à construire. SAML est ancien, verbeux et plein de façons de se tromper, et le service informatique du client le testera contre un seul fournisseur, le sien. Avec EAuth, l'ensemble tient sur une page de la console. Cet article la parcourt, y compris les parties qu'il ne nous appartient pas d'accélérer.

D'abord les organisations

Dans EAuth, le single sign-on appartient à une organisation, pas à votre application dans son ensemble. Une organisation, c'est un client : un groupe de comptes de votre application avec un rôle chacun, un domaine e-mail que le client a vérifié et, s'il le souhaite, une connexion à son fournisseur d'identité.

Deux choses viennent donc avant SAML. Cochez Enable organisations dans les réglages de l'application, puis créez l'organisation du client et vérifiez son domaine avec l'enregistrement TXT qu'affiche la console. Le domaine décide qui est envoyé vers le fournisseur, et il empêche le fournisseur d'un client de se porter garant des adresses d'un autre client. Sans domaine vérifié, le single sign-on ne peut pas être activé.

Ce que vous faites

Ouvrez l'organisation, choisissez Set up single sign-on, et la console affiche le côté EAuth de la connexion : un entity ID, une reply URL, et des métadonnées à télécharger qui contiennent les deux ainsi que le certificat de cette connexion. Chaque connexion a sa propre paire de clés RSA de 3072 bits, si bien que la configuration d'un client ne touche jamais celle d'un autre.

L'administrateur du client crée chez son fournisseur une application avec ces deux valeurs, ou y charge les métadonnées, et vous renvoie ses métadonnées de fédération. Collez-les dans la console, ou saisissez à la main l'entity ID, l'URL de connexion et le certificat de signature. Décidez deux choses : si les personnes deviennent membres dès leur première connexion, et si les membres ne peuvent se connecter à l'organisation que par le fournisseur. Puis activez.

C'est toute votre part. Prévoyez un après-midi, et attendez-vous à en passer l'essentiel sur l'échange de mails au milieu et sur la recherche, par l'administrateur du client, des bons réglages d'attributs.

Le fournisseur doit accepter les requêtes de connexion par redirection HTTP, comme le font Entra ID, Okta et Google Workspace, et envoyer l'adresse comme NameID ou comme attribut e-mail. Les noms d'attributs par défaut sont ceux qu'envoient Entra ID et Okta ; quand un fournisseur diffère, vous définissez les noms dans la console. Un fournisseur qui ne fait que la connexion initiée par l'IdP, où il poste une réponse que personne n'a demandée, n'est pas pris en charge, et c'est voulu. La section sur les contrôles explique pourquoi.

Ce que voient les collaborateurs du client

Quand votre application nomme l'organisation dans la requête d'autorisation, avec le paramètre organization et son id ou son slug, une personne non connectée est envoyée directement vers son fournisseur sans rien saisir. C'est ainsi que fonctionne un bouton « Se connecter avec le SSO » dans votre produit.

Sinon, la page de connexion d'EAuth a un bouton Use single sign-on : la personne tape son adresse professionnelle et est envoyée vers le fournisseur de son organisation. Envoyer le formulaire avec une adresse du domaine et sans mot de passe produit le même effet. S'inscrire avec une telle adresse mène aussi au fournisseur, puisque c'est lui qui crée le compte.

La première connexion par le fournisseur crée le compte dans votre application, déjà confirmé et sans mot de passe. Cela compte depuis le 3 octobre, date à laquelle EAuth a cessé de donner à toute application un compte dont personne n'a confirmé l'adresse : pour ces comptes, la parole du fournisseur vaut confirmation. Si l'adhésion à la première connexion est activée, le compte rejoint aussi l'organisation avec le rôle par défaut. Si elle est désactivée, il ne rejoint rien tant que quelqu'un ne l'invite pas, et votre application reçoit access_denied pour l'organisation.

Un compte qui existait déjà avec cette adresse garde son id et ses adhésions. Si son propriétaire avait confirmé l'adresse, il est lié tel quel. Si personne ne l'avait fait, il a pu être créé par quelqu'un d'autre que la personne, et la parole du fournisseur l'emporte : son mot de passe, ses passkeys et son second facteur disparaissent, ses sessions prennent fin, et votre webhook reçoit session.revoked avec le motif.

Avec only through the provider activé, mots de passe et passkeys ne comptent plus pour l'organisation. Un mot de passe tapé pour une adresse du domaine n'est même pas examiné, les refresh tokens de l'organisation prennent fin dès que le réglage s'applique, et un token issu d'un autre type de connexion est refusé quand il est utilisé.

Ce que nous contrôlons à chaque réponse

C'est la partie qui prend du temps à construire, et la raison de la prendre chez quelqu'un qui l'a déjà fait. La vérification des signatures XML est le travail d'une bibliothèque maintenue : des attaques par signature wrapping ont cassé les implémentations de grands éditeurs, parce que la canonicalisation XML est subtile d'une manière qui passe tous les tests qu'écrit un développeur. La bibliothèque vérifie la signature avec le certificat que vous avez collé, jamais avec un certificat arrivé dans la réponse, et contrôle la destination, l'émetteur, les horaires et le destinataire.

Par-dessus, EAuth lie chaque requête au navigateur qui l'a lancée, par un cookie que seul ce navigateur détient : une réponse obtenue ailleurs et postée depuis un autre navigateur ne connecte personne. Une requête reste ouverte dix minutes et reçoit une seule réponse, et chaque id d'assertion est mémorisé plus longtemps que l'assertion ne pourrait être acceptée : une réponse interceptée et postée une seconde fois est refusée. L'audience doit être l'entity ID de cette connexion, et l'adresse affirmée doit appartenir au domaine vérifié de l'organisation.

Quand une réponse est refusée, la personne le voit, et la page de l'organisation dans la console en donne la raison : une signature qui ne correspond pas, une audience ou une reply URL mal configurée chez le fournisseur, une adresse hors du domaine, un fournisseur qui n'a pas réauthentifié la personne alors qu'on le lui demandait. C'est là qu'il faut regarder pendant que le service informatique du client met tout en place, et cela évite l'aller-retour où chacun écrit à l'autre « ça ne marche pas ».

Retirer des personnes, et connexions fraîches

Quand le client retire quelqu'un chez son fournisseur, cette personne ne peut plus se connecter par lui. EAuth n'est toutefois pas informé du retrait. Les refresh tokens que votre application détient déjà pour elle restent valables jusqu'à leur expiration, soit jusqu'à 30 jours, sauf si le membre est retiré ou suspendu dans la console ou si l'organisation est suspendue. Ces actions mettent fin aussitôt aux refresh tokens de l'organisation, et les access tokens déjà émis expirent dans les 15 minutes.

Une application qui veut une connexion fraîche peut le dire. Avec prompt=login, le fournisseur est prié d'authentifier à nouveau la personne (ForceAuthn), et une réponse qui porte une connexion antérieure est refusée. Avec max_age, la session propre du fournisseur compte si elle est assez récente, et sinon, le fournisseur est sollicité à nouveau. Le auth_time de l'ID token est le moment où, selon le fournisseur, la personne s'est authentifiée, et la session EAuth se termine au plus tard quand le fournisseur dit qu'elle doit se terminer.

Ce qu'il ne fait pas

Il n'y a pas de provisioning SCIM : les comptes apparaissent à la première connexion, ne sont pas créés à l'avance et ne sont pas supprimés quand le fournisseur retire quelqu'un. C'est la lacune de la section précédente, et la raison pour laquelle le bouton de retrait de la console compte. Les claims de groupe du fournisseur ne sont pas convertis en rôles ; les rôles dans l'organisation se définissent dans la console. Le binding artifact n'est pas pris en charge.

Un mot sur les tests. Nos tests de bout en bout tournent contre un fournisseur d'identité construit sur la même bibliothèque SAML, qui signe ses réponses comme le font Entra ID et Okta. Cela éprouve le protocole et nos refus. Ce n'est pas la même chose qu'une session dans la console d'administration de chaque éditeur, et c'est à la première connexion avec un fournisseur donné que ses réglages particuliers se révèlent. Le motif de refus sur la page de l'organisation est là pour cet après-midi-là.

Sources

  1. Assertions and Protocols for the OASIS Security Assertion Markup Language (SAML) V2.0, OASIS, consulté le
  2. SAML Security Cheat Sheet, OWASP, consulté le
  3. On Breaking SAML: Be Whoever You Want to Be, USENIX Security 2012, consulté le
  4. Single sign-on SAML protocol, Microsoft, consulté le
  5. Enterprise SSO with SAML, EAuth documentation, Elchi Studios, consulté le

Écrit par Samuel Krauss, Founder. Classé sous eauth, saml, sso, organisations.

Traduit de l’anglais. Lire l’original anglais

Tous les articles Cet article en Markdown Flux Atom