Le cahier des charges imposé par les clients entreprise
Tout a commencé par un appel d'offres qu'on n'a pas perdu, mais qu'on aurait pu perdre sur un mot : SSO. Le client final, un acteur réglementé français avec 4 000 collaborateurs, voulait activer l'authentification fédérée avant la signature. Trois protocoles ont été cités noir sur blanc dans les annexes sécurité : SAML 2.0, OIDC, et SCIM pour le provisioning. Aucun n'était optionnel.
SAML 2.0 reste la référence dans le Fortune 500 en 2026. Azure AD, Okta et Google Workspace sont les trois IdP qu'on rencontre dans 90 % de nos dossiers B2B, et ils exposent tous un endpoint SAML historique. Refuser SAML, c'est se fermer la moitié des comptes entreprise. OIDC est la version moderne : stateless, basé sur JSON, plus simple à intégrer côté navigateur, et attendu par les directions info qui ont migré sur des IdP récents.
SCIM a été le vrai point de blocage. Les RSSI demandent désormais un cycle de vie automatisé des utilisateurs : à l'arrivée d'un collaborateur, le compte se crée tout seul ; à son départ, il se désactive dans la foulée. Le endpoint `/Users` de SCIM 2.0 est devenu un standard de fait. À cela s'ajoutent l'application d'une politique MFA sur les comptes sensibles, et la production de journaux d'audit exportables (login, échec, changement de rôle) pour les contrôles internes du client.
Résumé du cahier des charges, validé avec le client : SAML + OIDC en entrée, SCIM 2.0 pour le provisioning, MFA configurable par organisation, et un flux d'audit log sécurisé. Aucune solution grand public ne coche ces quatre cases en mode freemium. Il fallait choisir un fournisseur dédié, ou accepter de construire l'ensemble soi-même.
La matrice d'évaluation que nous avons utilisée
Cinq fournisseurs ont été short-listés. Chacun a été noté sur six critères : couverture protocolaire (SAML, OIDC, SCIM), coût mensuel pour 50 organisations et 5 000 utilisateurs, qualité de la documentation développeur, charge de maintenance estimée, facilité d'intégration dans une stack Next.js 15, et profondeur des journaux d'audit. Les chiffres ci-dessous ont été relevés entre janvier et mars 2026, directement sur les sites publics de pricing.
Auth0 (Okta) reste la référence en matière de maturité : règles et Actions permettent tout, depuis l'enrichissement de token jusqu'au Multi-Factor Auth contextuel. Le revers : le SSO enterprise (SAML + OIDC) n'est disponible qu'à partir du plan B2B Enterprise, autour de 2 300 $/mois pour notre volumétrie, hors MFA premium. La courbe d'apprentissage des Actions est réelle, et le debugging en production reste un sport.
WorkOS positionne SSO + Directory Sync comme un service à part entière. Le pricing est lisible : 125 $/mois de base, puis un coût par utilisateur MAU qui reste maîtrisé. L'API est claire, la documentation orientée développeur, et l'adaptateur côté Next.js tient en une centaine de lignes. SCIM est supporté nativement via Directory Sync.
Supabase Auth, avec son add-on SSO, est tentant pour une équipe déjà sur Supabase : tout reste dans la même console. Mais le SAML SSO n'est disponible que sur le tier Enterprise (à partir de 2 500 $/mois sur devis), et SCIM n'est pas encore first-class. Pour un B2B SaaS pur, c'est un frein sérieux. Clerk offre une DX excellente et un système d'organisations bien pensé : le SSO SAML est débloqué sur le plan Premium (catalogue public autour de 0,02 $/MAU supplémentaire), mais la philosophie 'opinionated' devient un obstacle dès qu'on veut sortir du chemin balisé.
Enfin, Keycloak auto-hébergé était l'option libre et complète : zéro coût de licence, support natif de SAML, OIDC et SCIM, et un back-office puissant. Le prix à payer est opérationnel : c'est une stack JVM à maintenir, des mises à jour à suivre, des dumps PostgreSQL à sauvegarder, et une complexité de configuration qui mobilise facilement 20 heures par mois d'un ingénieur. Sur trois ans, le coût caché dépasse largement le pricing de WorkOS.
Trois mois de chantier, mois par mois
Le mois 1 a été consacré au shortlist et au POC. Pour chaque fournisseur, nous avons monté un environnement isolé, branché un Azure AD de test, et déroulé trois parcours : login initié par le fournisseur de service (SP-initiated), login initié par l'IdP (IdP-initiated), et provisioning d'un utilisateur via SCIM. Ce premier filtre a éliminé Supabase Auth (pas de SCIM réellement exploitable dans notre fenêtre) et Clerk (le SSO en Premium ne nous a pas semblé au niveau d'exigence du client final sur les journaux d'audit).
Le mois 2 a été l'intégration réelle dans le produit, avec un seul fournisseur retenu : WorkOS. Nous avons mis en place le callback unique, branché l'adapter côté Next.js 15, et déroulé une campagne de charge. Le test clé : 500 connexions simultanées sur le endpoint `/auth/callback` pour vérifier qu'il n'y avait ni deadlock sur la table des sessions ni dégradation côté WorkOS. Le bottleneck n'a pas été le fournisseur, mais notre propre base PostgreSQL sur l'écriture des `audit_logs`. Nous avons déplacé ces écritures vers une file asynchrone (Redis + worker dédié) avant le mois 3.
Le mois 3 a été la bêta avec deux design partners : un cabinet de conseil français (Azure AD, ~1 200 utilisateurs) et une scale-up industrielle (Okta, ~600 utilisateurs). Leurs retours ont confirmé deux points : le SP-initiated fonctionne sans accroc sur les deux IdP, et l'IdP-initiated demande un traitement spécifique (gestion d'un `RelayState` non vide, et redirection post-authentification vers une page neutre). Cette bêta a aussi révélé que la rotation des certificats de signature SAML côté client n'était pas anticipée par nos opérations — un point que nous avons corrigé avant la mise en production générale.
Les coûts cachés du « enterprise SSO »
Le premier coût caché est l'échange de métadonnées SAML. Sur le papier, c'est un fichier XML à échanger une fois. En pratique, les clients rotent leurs certificats de signature tous les 12 à 24 mois, parfois sans prévenir, et le jour où ça casse, c'est une interruption totale de connexion pour toute l'organisation concernée. Il faut prévoir un runbook, une alerte sur la date d'expiration du certificat distant (visible dans les métadonnées), et un point de contact identifié chez chaque client entreprise.
Le deuxième piège est l'opposition JIT vs SCIM. Le JIT (Just-In-Time provisioning) crée l'utilisateur à la volée lors du premier login SAML/OIDC. C'est simple, élégant, et ça casse le jour où un utilisateur change d'email ou de nom dans l'IdP : le JIT ne met pas à jour, il crée un doublon. SCIM est plus robuste car il synchronise en amont, mais il exige un mapping d'attributs rigoureux (`userName`, `emails`, `displayName`, `active`) qui varie selon les IdP. Pour un produit qui se veut multi-clients entreprise, SCIM est non négociable ; JIT est un bonus acceptable pour les TPE.
Le troisième coût caché concerne les quirks IdP. Azure AD, Okta et Google Workspace prétendent tous implémenter SAML 2.0, mais se comportent différemment sur trois points : le format du `NameId` (email, persistent, unspecified), la gestion du `RelayState` (limité à 80 caractères chez Okta), et l'envoi ou non des attributs utilisateur dans l'assertion. Il faut un tableau de compatibilité par IdP, et une procédure de test dédiée. Nous avons fini par maintenir une matrice interne Azure AD / Okta / Google / OneLogin / JumpCloud, mise à jour à chaque nouveau client.
Dernier point, souvent oublié : la facturation. Les fournisseurs SSO facturent au MAU ou à l'organisation. Un client avec 5 000 utilisateurs dont seulement 800 actifs par mois peut coûter deux fois plus cher que prévu si la granularité est mauvaise. Lire le pricing en détail avant de signer, et projeter sur 24 mois avec une marge de 30 % de croissance utilisateur.
Le choix final pour notre contexte
Notre décision, documentée dans un ADR interne : WorkOS. Trois critères ont fait la différence. D'abord le coût : pour un produit à moins de 500 k€ d'ARR, le pricing WorkOS (125 $/mois de base + per-user) reste prévisible, là où Auth0 Enterprise et Clerk Premium nous auraient fait franchir un pallier de coût significatif dès la dixième organisation. Ensuite, la couverture protocolaire est large : SAML, OIDC et Directory Sync SCIM sont inclus sans add-on séparé. Enfin, la DX (developer experience) était au-dessus du lot : documentation orientée code, SDK TypeScript bien typé, et un cycle de test qui tient en une journée.
Ce choix n'est pas universel. Pour une startup à moins de 100 k€ d'ARR avec un seul client entreprise attendu, Clerk ou même Supabase Auth avec SSO add-on peuvent suffire, et le coût initial sera inférieur. Pour une scale-up au-delà de 2 M€ d'ARR avec des besoins d'audit très lourds et une équipe platform dédiée, Auth0 ou Keycloak peuvent devenir pertinents : Auth0 pour l'écosystème, Keycloak pour la maîtrise totale. À l'inverse, en dessous de 500 k€ d'ARR et pour un B2B SaaS avec plusieurs clients entreprise, WorkOS reste notre recommandation par défaut en 2026.
Nous avons conservé le critère de réversibilité : le contrat WorkOS est résiliable avec un préavis de 30 jours, et les métadonnées SAML restent échangeables manuellement en cas de bascule vers un autre fournisseur. La dépendance reste contenue, et un ADR de migration est déjà esquissé au cas où la volumétrie ferait basculer l'équilibre économique dans 18 à 24 mois.
Leçons d'implémentation : sessions, callbacks et flows
Premier sujet : JWT stateless vs sessions de référence. Le JWT stateless est séduisant car il évite un aller-retour base à chaque requête. Mais en B2B entreprise, le besoin de révocation est réel : un compte compromis chez un client doit pouvoir être désactivé instantanément, pas au prochain refresh token. Nous avons opté pour des sessions de référence stockées en base, vérifiées à chaque requête via un cache Redis (TTL 60 secondes). Surcharge acceptable, et révocation immédiate garantie.
Deuxième sujet : le piège du `redirect_uri`. SAML et OIDC exigent un callback déclaré côté IdP. Avec plusieurs fournisseurs d'identité (Azure AD, Okta, Google), il est tentant de déclarer un `redirect_uri` par IdP (`/auth/callback/azure`, `/auth/callback/okta`, etc.). C'est une erreur. La bonne pratique est un seul `/auth/callback` universel, et un dispatch par le paramètre `state` (OIDC) ou `RelayState` (SAML). Cela simplifie la configuration IdP côté client et centralise la logique de routage côté application.
Troisième sujet : les flows SP-initiated et IdP-initiated. SP-initiated (l'utilisateur clique 'Se connecter avec SSO' sur votre site, vous redirigez vers l'IdP) est le flux par défaut, et le plus simple. IdP-initiated (l'utilisateur clique sur l'icône de votre app dans le portail de l'IdP, l'assertion arrive sans appel préalable) est demandé par une majorité de clients entreprise. Les deux doivent fonctionner. La différence clé : en IdP-initiated, il n'y a pas de `state` préalablement stocké, donc il faut gérer un `RelayState` potentiellement vide et rediriger intelligemment vers la page d'accueil (et non vers l'URL initialement demandée, qui n'existe pas).
Quatrième sujet, transverse : les assertions SAML signées doivent être vérifiées avec rigueur. Vérifier la signature du message ne suffit pas ; il faut aussi vérifier la signature de l'assertion interne, contrôler le `destination`, le `recipient`, la fenêtre de validité (`NotBefore` / `NotOnOrAfter`), et rejeter toute assertion reçue en dehors de sa fenêtre temporelle. Sur ce point, s'appuyer sur une bibliothèque éprouvée (WorkOS gère cela pour nous ; en Keycloak, c'est natif) plutôt que sur un parser maison est non négociable.
Ce que nous referions différemment
Trois changements si nous devions reprendre ce chantier demain. Premièrement, nous aurions lancé la bêta design partners dès le mois 2 plutôt que le mois 3. Les retours sur l'IdP-initiated et sur la rotation des certificats SAML sont arrivés trop tard dans le cycle, et ont consommé deux semaines de réwork. Un design partner accessible dès le POC initial permet de découvrir ces écarts quand le coût de correction est encore faible.
Deuxièmement, nous aurions écrit le tableau de compatibilité IdP avant de choisir le fournisseur, et non après. La matrice Azure AD / Okta / Google / OneLogin / JumpCloud est devenue un actif interne précieux, mais elle a été produite en mode réactif. C'est désormais un livrable obligatoire de tout chantier SSO, avant même le choix technique.
Troisièmement, nous aurions surdimensionné le runbook opérations SSO dès le départ. Aujourd'hui, il couvre la rotation de certificat, la mise à jour d'attributs, l'ajout d'un nouvel IdP, et la procédure de déblocage d'un utilisateur bloqué. Il a été écrit après deux incidents mineurs en production. La prochaine fois, il sera écrit avant la mise en production, validé avec le client, et testé en environnement de pré-prod.