Comme vous pouvez probablement le deviner, je suis également très intéressé par une solution stable à ce problème, qui n’est pas spécifique au plugin openid-connect. Voyons si nous pouvons trouver une voie à suivre.
Pour rebondir sur le point que vous avez soulevé.
Comment décidez-vous quels groupes ajouter ou supprimer de l’utilisateur ? Et comment cela affecte-t-il l’ajout ou la suppression manuelle d’utilisateurs de groupes dans Discourse lui-même ?
un utilisateur s’authentifie via OIDC avec les groupes ['group1', 'group2']
Dans l’interface utilisateur de Discourse, l’utilisateur est ajouté à group3
plus tard, le même utilisateur s’authentifie via OIDC avec les groupes ['group1']
En examinant cela en tant qu’humain, nous pouvons voir que l’état final devrait être group1, group3 (group2 devrait être supprimé). Mais je ne pense pas qu’il y ait assez d’état suivi pour prendre cette décision de manière programmatique. De même :
un utilisateur s’authentifie via OIDC avec les groupes ['group1', 'group2']
Dans l’interface utilisateur de Discourse, l’utilisateur est retiré de group1
plus tard, le même utilisateur s’authentifie via OIDC avec les groupes ['group1', 'group2']
Que se passe-t-il maintenant ? Un administrateur a explicitement retiré l’utilisateur de group1, mais OIDC vient de le réajouter. C’est une expérience utilisateur très confuse pour l’administrateur. Nous pourrions avoir besoin d’un moyen d’identifier les groupes comme « gérés externement » et de masquer toute l’interface d’ajout/suppression
Je pense qu’il existe plusieurs façons de gérer ce problème (elles ne sont pas exclusives).
Identification des groupes et des attributs de jeton
Dans toute implémentation, l’identification explicite de
quels groupes peuvent voir leur appartenance gérée via l’authentification ; et
quels attributs dans un jeton d’authentification gouvernent l’appartenance aux groupes
devrait être requise, que ce soit via les paramètres du site, les paramètres de groupe ou autrement, et la valeur par défaut devrait être « désactivée ».
Traitement strict ou permissif
Le traitement est « strict » si un utilisateur est supprimé lorsque le groupe est absent de l’attribut de jeton identifié. Le traitement est « permissif » si un utilisateur n’est pas supprimé lorsque le groupe est absent de l’attribut de jeton identifié.
Comme vous le suggérez, vous pourriez désactiver l’ajout/suppression d’appartenance aux groupes via l’administration des groupes si le traitement était « strict ».
Identification de la source d’appartenance
Vous pourriez également stocker la source de l’appartenance au groupe dans la table group_users pour permettre une approche « mixte » au sein d’un groupe, c’est-à-dire qu’un administrateur ne peut pas supprimer les appartenances créées via un jeton d’authentification.
Plus j’y réfléchis, plus je pense que cela devrait être fait via les paramètres de groupe.
Merci @angus d’avoir lancé ce sujet ! Je sais que beaucoup de personnes sont très intéressées par cette fonctionnalité !
Intéressant ! J’avais toujours imaginé que les groupes seraient créés automatiquement lors de l’authentification, et que le nom du groupe correspondrait exactement au nom du groupe chez le fournisseur d’identité. C’est ainsi que fonctionne DiscourseConnect actuellement.
Mais j’aime beaucoup cette option plus explicite ! Cela signifie que les instances Discourse des utilisateurs ne seront pas encombrées par des groupes inutiles provenant de leur fournisseur d’identité, et que les administrateurs pourront personnaliser les noms des groupes à leur guise. Cela offre une grande parité avec notre système d’appartenance basé sur les domaines d’e-mail existant.
Cela semble bien d’un point de vue technique. Ma seule inquiétude est que cela pourrait être difficile à expliquer aux utilisateurs et aux administrateurs. Si nous suivons votre idée de « l’identification explicite » des groupes gérés par l’authentification, je pense que nous pourrions simplement implémenter le traitement « strict » ?
Lorsqu’un groupe est configuré pour être géré par l’authentification, l’ajout ou la suppression manuelle serait masqué derrière un avertissement important, et le comportement serait similaire au mode « strict » que vous avez décrit.
Qu’en pensez-vous ?
Au passage, nous devrions également nous assurer que tous les ajouts ou suppressions automatiques d’utilisateurs soient enregistrés dans le journal du groupe. Cela rendra plus facile pour tout le monde de comprendre ce qui se passe et pourquoi.
Oui, je pense qu’il vaut mieux être explicite, du moins pour la version 1 de cet ensemble de fonctionnalités. Je n’ai encore jamais rencontré de cas d’usage où la création automatique était vraiment nécessaire, c’est-à-dire où le groupe ne pouvait pas simplement être configuré par l’administrateur du site avant tout traitement des revendications.
Peut-être pourrions-nous passer à une création automatique en tant qu’option après avoir mis en place la version explicite ?
En ce qui concerne les paramètres de groupe, cela ressemblerait à ceci :
Section des paramètres : Adhésions (c’est-à-dire la section existante) Titre du groupe de paramètres : Gestion de l’authentification Paramètres :
Service : Liste des services d’authentification. « Tous » serait une option. Ce paramètre fonctionnerait également comme un état « activé / désactivé » pour cet ensemble de fonctionnalités, c’est-à-dire que la valeur par défaut serait « Aucun ».
Revendication : champ de saisie de texte pour identifier la revendication du jeton d’ID. La « description » de cette fonctionnalité (peut-être dans un post ici sur Meta) expliquerait les formats pris en charge, par exemple booléen, chaîne séparée par des virgules, etc.
Mode : voir ci-dessous
Oui, s’il existait un paramètre strict / permissif, nous devrions l’expliquer de manière concise. Je pense que la meilleure approche serait de se concentrer sur l’« ajout » et la « suppression ». Vous pourriez peut-être le décrire ainsi :
Paramètre : « Mode »
Option 1 (permissif) :
Libellé : « Ajouter des membres »
Description : « Autoriser l’ajout de membres à ce groupe lors de l’authentification »
Option 2 (strict) :
Libellé : « Ajouter et supprimer des membres »
Description : « Autoriser l’ajout et la suppression de membres lors de l’authentification. Cela désactivera les contrôles manuels des adhésions. »
La raison pour laquelle je tiens à conserver l’option « permissif » est que, lorsque je travaillais auparavant avec des clients sur ce type de sujet, c’était le cas d’usage le plus courant, à savoir :
Le besoin principal est de permettre l’accès à un groupe en fonction d’un état dans un service externe.
Le besoin de supprimer l’accès en fonction de l’état dans le service externe est plus marginal, c’est-à-dire que les gens perdent l’accès et devraient être supprimés, mais cela est relativement moins important.
Il existe souvent une volonté de conserver la possibilité de contrôler manuellement les adhésions dans Discourse. Lorsque j’ai mis en œuvre l’approche « stricte », cela a été « surprenant » (c’est-à-dire « Pourquoi la personne X a-t-elle perdu son adhésion ? ») malgré les explications et le fonctionnement (technique) tel que prévu.
Oui, c’est important, car les gens se demanderont souvent « pourquoi » quelqu’un a été ajouté ou supprimé, en particulier en mode strict, et nous (c’est-à-dire « Discourse ») n’aurons pas le contrôle sur la véracité des revendications émises par le service d’authentification externe.
Peut-être un nouveau type d’action « Supprimer un utilisateur » et « Ajouter un utilisateur » qui inclut le service d’authentification responsable de l’action, c’est-à-dire « Supprimer un utilisateur ([nom du service]) ».
Une autre « surprise » ici est que nous devrons être clairs : ce n’est pas une solution miracle pour le cas d’usage « je veux que les membres d’un groupe soient basés sur le service externe X », car les personnes ne s’authentifient pas fréquemment, du moins pas selon les besoins du cas d’usage standard de ce type.
Cette gestion de l’authentification est une pièce nécessaire pour traiter ce type de cas d’usage, mais pour le prendre correctement en charge, vous devez également configurer des intégrations basées sur des événements, par exemple un récepteur de webhooks (nous disposons d’un plugin de récepteur de webhooks privé conçu pour la gestion des groupes, que j’espère rendre open source sous peu).
Juste pour comparer les fonctionnalités, je voulais partager ce que nous faisons au sein du Global Legal Empowerment Network, qui utilise WordPress et le plugin Discourse pour WordPress. Nous l’avons configuré de manière à ce que chaque mise à jour d’un utilisateur dans WordPress se répercute dans Discourse. Cela inclut certains groupes spéciaux (par exemple, membre principal, contributeur de ressources, etc.) ainsi que les détails du profil. Nous avons ajouté un champ utilisateur masqué pour « dernière mise à jour », ce qui a aidé au dépannage et à la vérification du bon fonctionnement.
Nous verrouillons ces groupes gérés à distance afin que les utilisateurs ne puissent ni les rejoindre ni les quitter dans Discourse, mais nous n’avons pas jugé nécessaire d’empêcher la gestion des adhésions aux groupes par le personnel. J’aime bien l’aspect de ce que vous essayez ci-dessus, mais c’est un peu au-dessus de mes compétences, honnêtement.
Nous avons certains clients de Discourse for Teams qui utilisent activement Okta pour gérer l’accès à toutes leurs applications d’entreprise. Ils y définissent également des rôles qui sont ensuite pris en charge, par exemple pour accorder certains niveaux d’accès à Microsoft, Tableau, etc. Okta est aussi un annuaire, ils gèrent donc les avatars, la biographie, la localisation et d’autres informations de profil. Ils aimeraient voir ces informations de profil mises à jour dans Teams, même en dehors d’Okta.
Nous devrions essayer de maintenir les éléments super techniques hors des paramètres de groupe si possible. Le fait de devoir lier à une documentation sur la syntaxe signifie probablement qu’il faudrait les simplifier ou les déplacer vers les paramètres du site administrateur. Je pense que nous devrions laisser cette partie à implémenter par chaque plugin d’authentification, car cela peut varier considérablement.
En prenant OIDC comme exemple, ce plugin ajouterait un nouveau paramètre de site openid_connect_roles_claim. Si on utilise Okta, l’administrateur le configurerait comme groups. Je pense que le tableau de chaînes est un format assez standard pour OIDC, mais nous pourrions explorer des options plus complexes ici si vraiment nécessaire.
Pour recevoir ces informations, Auth::Result se verrait attribuer un nouvel attribut roles, qui accepte un simple tableau de chaînes. Le noyau prendra ensuite cela (dans Auth::Result#apply_user_attributes!) et le chargera dans une nouvelle table UserAssociatedRoles avec les colonnes (provider_name, user_id, role). Cette table UserAssociatedRoles nous donne l’« identification de la source d’appartenance » que vous avez mentionnée dans le premier message.
J’imagine les paramètres de groupe ressemblant à ceci :
Les noms de rôle seraient préfixés par le provider_name. La saisie automatique serait basée sur toutes les valeurs existantes dans la table UserAssociatedRoles, mais nous accepterions également des valeurs non saisies automatiquement au cas où le rôle n’aurait pas encore été rencontré par Discourse.
La beauté d’avoir une table complète user_associated_roles dans la base de données est que les administrateurs peuvent faire ce qu’ils veulent avec les groupes Discourse, et les appartenances seront mises à jour instantanément sans que les utilisateurs n’aient à se reconnecter.
C’est juste. Je pense qu’il serait préférable de garder cela aussi simple que possible, du moins pour la version 1, donc je préférerais ne pas avoir plusieurs « modes ». Et si nous avions ce comportement par défaut :
Ajouter des membres lorsqu’ils ont un rôle correspondant provenant du fournisseur d’authentification
Supprimer des membres lorsque ce rôle disparaît
Permettre l’ajout/suppression dans Discourse également
Si un administrateur tente de supprimer un utilisateur qui a été ajouté via un fournisseur d’authentification, afficher un avertissement : « cet utilisateur pourrait être ajouté à nouveau lors de sa prochaine connexion »
Je pense que nous devrions essayer d’être sécurisés par défaut ici, et supprimer les membres lorsqu’ils perdent le rôle chez le fournisseur d’identité. Avec les améliorations du journal d’appartenance, j’espère que cela sera moins confus que le statu quo actuel.
Pour les sites qui ne veulent vraiment pas cela, nous pourrions avoir un paramètre de site comme remove_group_membership_when_auth_role_lost (vrai par défaut).
Oui, tout à fait. Nous avons aussi ce problème avec d’autres métadonnées utilisateur comme le nom, l’avatar, etc. Je pense qu’à un moment prochain, nous devrons examiner la création d’un équivalent de sync_sso pour les autres plugins d’authentification. Cela pourrait transmettre toutes les informations normalement transmises via OIDC / SAML / etc., y compris ces nouvelles informations de « rôle ». Probablement un projet séparé cependant.
Comment cela vous semble-t-il tout cela @angus ? C’est un peu moins flexible que des modes strict/péremptoire séparés, mais je pense qu’avoir un seul mode rendra le dépannage / la documentation / le support beaucoup plus faciles.
Avec ce plan, voici une vue d’ensemble de ce à quoi cela ressemble du point de vue d’un administrateur :
Dans Discourse, définissez openid_connect_roles_claim sur groups
Configuration d’un nouveau groupe
Créez un groupe Discourse comme d’habitude. Configurez le nom / nom complet / insigne / etc. comme vous le souhaitez
Allez dans les préférences « appartenance », placez votre curseur dans le menu déroulant des rôles SSO, et choisissez un rôle dans la liste. Si Discourse n’a pas encore vu quelqu’un se connecter avec ce rôle, vous devrez le saisir manuellement
Vous pouvez spécifier plusieurs rôles pour un seul groupe Discourse. Par exemple, un groupe « équipe » dans Discourse pourrait être une combinaison de oidc:employees et oidc:contractors
Appuyez sur enregistrer, et l’appartenance au groupe sera instantanément mise à jour avec les informations de rôle que Discourse a mises en cache lors des connexions précédentes.
Lors des futures connexions, tous les changements de rôles chez le fournisseur d’identité seront reflétés dans le groupe Discourse
Vous pouvez toujours ajouter/supprimer des utilisateurs au groupe via l’interface native de Discourse
Si vous essayez de supprimer un utilisateur qui a été ajouté via un fournisseur d’identité, un avertissement s’affichera indiquant que l’utilisateur pourrait être réajouté lors de la prochaine connexion
Si vous décidez plus tard que vous ne voulez plus que ce rôle IDP soit associé au groupe, vous pouvez le supprimer, et tous les utilisateurs qui étaient membres via ce rôle seront supprimés
hm oui, cependant il existe des cas où la prise en charge d’une revendication booléenne a du sens. Mais oui, je suis d’accord, gardons cela simple pour l’instant en ne prenant en charge que le tableau de chaînes.
En le faisant authentificateur par authentificateur, je suppose que nous aurions également une méthode group_sync_enabled? dans Auth::Authenticator qui serait redéfinie dans les authentificateurs individuels, et dans les authentificateurs génériques sur la base de l’existence d’une valeur dans le paramètre pertinent.
Cela soulève en fait une question intéressante. Cela pourrait également être utilisé par les authentificateurs de service spécifiques, tels que facebook (“groups”?), google (“groups”?) et discord (“roles”). Je ne sais pas si leurs jetons d’identité contiendraient de telles informations, mais cela vaut peut-être la peine d’être envisagé d’un point de vue de conception technique (c’est-à-dire la possibilité d’ajouter cela plus tard). Pas une préoccupation principale pour la v1, je ne pense pas.
Ces mécanismes semblent corrects, cependant je me demande pourquoi nous sommes passés à l’utilisation de « rôles » au lieu de « groupes » ici. Pas un gros problème, mais je veux m’assurer qu’il n’y a rien que je rate.
D’un point de vue UX, je vois une confusion pouvant découler de cette utilisation de la nomenclature (c’est-à-dire « rôles » et « groupes » en même temps), même si elle est fondée sur une distinction technique utile. Confusion possible des développeurs également s’ils abordent cela sans ce contexte de fond.
J’aime la table séparée, cependant peut-être devrions-nous avoir une association de clé étrangère avec user_associated_accounts au lieu de provider_name ? Cela pourrait être utile dans des opérations comme le nettoyage. Je suppose que la réponse à cette question dépend en partie de la mesure dans laquelle nous voulons que l’association groupe/rôle soit interconnectée avec le compte associé d’un point de vue produit. Y aurait-il un inconvénient à lier les deux maintenant ?
** Edit, je suppose que nous avons déjà l’association via le user_id.
Cela semble correct, cependant je pense juste que nous avons également ces informations par fournisseur en vertu du paramètre de site que vous avez proposé, ce qui couvrirait également le cas où le rôle n’aurait pas encore été vu. Peut-être y a-t-il un moyen d’utiliser Discourse.authenticators ici, c’est-à-dire une liste en mémoire des fournisseurs et de leurs revendications ?
Je pense que c’est un bon compromis. Surtout si nous avons également le paramètre de site mentionné ci-dessous.
Sur la base d’un plugin (c’est-à-dire d’un authentificateur) ? Si c’est le cas, je suis partant. Cela devrait répondre à ce besoin pour la v1.
Bon résumé du flux utilisateur En attendant les suggestions relativement mineures que j’ai faites, je suis d’accord avec la direction.
Oui, tout à fait. Je pensais particulièrement à Google, car les gens aiment l’utiliser pour leurs organisations GSuite, qui, je suppose, ont une notion de groupes.
Je supposais essayer d’éviter toute confusion entre « groupes du fournisseur d’identité » et « groupes Discourse »… mais il est possible que j’aie simplement rendu les choses plus confuses en changeant le nom. Je suis tout à fait d’accord pour rester sur « groupes » ici.
Mon raisonnement était que nous prenons encore en charge les fournisseurs d’authentification non gérés, donc il pourrait ne pas y avoir d’enregistrement user_associated_account. Nous pouvons toujours faire un jointure via l’user_id comme vous l’avez dit
Je ne suis pas sûr que le paramètre de site aide ici. Si nous parlons d’un tableau de chaînes représentant des « rôles », le paramètre de site ne spécifierait pas quels sont réellement les rôles. Il ne ferait que spécifier comment obtenir le tableau. Est-ce que cela a du sens ?
Oui, vous avez raison. Je pensais encore à une implémentation précédente où j’avais inclus des groupes spécifiques dans le paramètre lui-même, mais nous ne faisons pas cela ici, ce qui n’est pas un problème.
Je pense qu’il y a suffisamment d’informations ici pour commencer à travailler sur la PR, que je lancerai plus tard cette semaine, sauf si vous avez encore des préoccupations ? Je mettrai à jour ce fil si quelque chose surgit en cours de route.
Ok, j’ai enfin une version en cours de développement à partager ici
Quelques remarques.
J’ai choisi le cas légèrement plus complexe des groupes Google Apps hd pour la première implémentation, car cela aide à réfléchir aux différentes permutations possibles, par exemple la nécessité de prendre en compte les groupes spécifiques à un domaine provenant d’un fournisseur.
Pour implémenter ce cas d’usage, j’ai également dû introduire un nouveau concept d’« autorisation secondaire » au moment de l’authentification, afin de permettre une autorisation incrémentale. J’ai envisagé plusieurs façons de demander des autorisations de groupe spécifiques pour un utilisateur (c’est-à-dire s’il s’authentifie avec un hd), et cela semblait être la solution la plus faisable. Je reconnais que cela représente peut-être un changement plus important que prévu de ce côté-là, mais il vaut peut-être la peine d’en discuter.
Notez que pour implémenter le cas des groupes Google hd, vous devez accorder aux membres non administrateurs de vos groupes Google Apps hd une autorité d’administrateur déléguée afin de pouvoir lister leurs groupes (via l’API du répertoire administrateur). Il existe en réalité un rôle d’administrateur préconstruit « bêta » appelé « Lecteur de groupes » qui fonctionne bien pour cela. Voir Prebuilt administrator roles | User management | Google Workspace Help
L’implémentation Google fonctionne. Si vous la configurez puis vous authentifiez avec un hd, vos groupes Google hd seront disponibles dans le paramètre d’appartenance automatique aux groupes ; vous serez ajouté à ce groupe Discourse si ce groupe hd est sélectionné, retiré s’il est supprimé (avec ces deux actions enregistrées avec une certaine précision), et les utilisateurs ultérieurs de ce groupe Google hd qui s’authentifieront seront ajoutés immédiatement.
Les détails devraient être évidents à partir du code et des tests. Vous remarquerez également que j’ai fini par ajouter trois nouvelles tables. J’ai essayé quelques solutions plus « légères », mais elles se sont toutes révélées plus compliquées et inefficaces lorsqu’il s’agissait de gérer les mises à jour des groupes associés à un utilisateur et des groupes associés à un groupe. Il est difficile d’éviter de créer de nouvelles tables pour chacun. Je suis ouvert aux idées concernant la modélisation des données, et plus généralement.
Quelques tâches techniques restantes (en dehors des questions conceptuelles/produit soulevées ci-dessus). Des suggestions sont également les bienvenues à ce sujet :
Peut-être sérialiser le label des associated_groups (au lieu de le modéliser côté client).
Ajouter les tests manquants et les tests QUnit.
Peut-être déplacer la création/destruction de user_associated_group / group_associated_group vers un travail en arrière-plan, car avec un grand nombre d’entrées, cela pourrait être lent.
Je suis légèrement réticent à l’idée de cette « double autorisation », ainsi qu’à celle de la colonne provider_domain. Pourrais-tu préciser pourquoi elles sont nécessaires ? Elles semblent assez spécifiques à Google… Y a-t-il une raison pour laquelle nous ne pouvons pas demander la portée admin.directory.group.readonly lors de la première demande d’authentification ? Et peut-être simplement préfixer le nom du groupe par le domaine ? (ou exclure le domaine entièrement, car je suppose que les gens n’utiliseront cela qu’avec un seul « domaine hébergé » Google ?)
Oui, tout à fait d’accord pour les trois tables ici — cela rend les choses plus claires.
D’accord
Nous devons être prudents ici. Tous les membres des groupes doivent être assignés avant que l’utilisateur ne charge le site pour la première fois. Sinon, ils ne verront pas les éléments spécifiques aux groupes lors de leur première connexion (par exemple, les catégories sécurisées). Je pense donc que les modifications des enregistrements user_associated_group doivent être traitées de manière synchrone.
Cependant, pour les modifications des enregistrements group_associated_group, je pense que tu as raison. Ces changements peuvent affecter des milliers d’utilisateurs, ils devront donc être traités en lots. Je commencerais par le faire de manière synchrone, avec un indicateur de chargement dans l’interface utilisateur. Ainsi, les administrateurs pourront clairement voir quand cela est en cours/terminé.
Si nous constatons que cela approche des 30 secondes (le délai d’attente des requêtes Unicorn), nous devrons peut-être envisager une tâche en arrière-plan.
Nous pourrions également devoir réfléchir à l’ajout d’un verrouillage DistributedMutex. Par exemple, si un utilisateur se connecte pendant qu’une modification de group_associated_group est en cours de traitement, que se passe-t-il ? Je suis ouvert à discuter de ce genre de sujets sur GitHub une fois que l’architecture globale sera finalisée.
Pour mon cas d’usage, je serais tout à fait satisfait que le comportement de Discourse Connect soit implémenté pour d’autres fournisseurs d’authentification.
La raison pour laquelle vous ne pouvez pas demander les autorisations de groupe lors de la première demande est que vous ne savez pas qui se connecte, ni ce qu’il est prêt à partager. Vous pourriez restreindre la correspondance des groupes associés à Google aux personnes utilisant le paramètre google_oauth2_hd, mais cela limiterait considérablement la portée de la fonctionnalité. Il est relativement courant d’avoir une équipe qui utilise Google Apps en plus d’utilisateurs « publics » qui souhaitent également utiliser l’authentification Google.
*edit Je devrais préciser : si vous demandez une portée de groupes et que l’utilisateur ne peut pas l’accorder (par exemple, son HD n’a pas délégué l’autorité aux utilisateurs non administrateurs comme décrit ci-dessus), alors l’authentification échouera. Vous ne pouvez pas demander des portées optionnelles en même temps que des portées obligatoires.
De plus, cette approche, c’est-à-dire demander une portée de groupes dès le départ comme pratique standard lors de la mise en œuvre dans les différentes méthodes d’authentification, serait sans doute plus spécifique à Google que l’alternative, car vous n’avez pas toujours l’équivalent du système de domaine hébergé pour restreindre la connexion. Par exemple, je peux me tromper, mais je ne pense pas qu’il existe un moyen de restreindre la connexion GitHub OAuth2 à une organisation GitHub spécifique.
Autrement dit, dans de nombreux cas, vous seriez confronté au choix de demander à tous ceux qui utilisent cette méthode d’authentification d’accorder la portée groups pertinente, ou de ne pas utiliser la fonctionnalité. Cette approche peut fonctionner dans certains contextes, mais pas dans beaucoup d’autres. Cette approche d’autorisation incrémentale offre plus de flexibilité aux différentes méthodes d’authentification pour mettre en œuvre la fonctionnalité.
Il est vrai que Google a été un fervent défenseur de l’autorisation incrémentale dans l’espace OAuth2 ; par exemple, tous les documents de travail sur le sujet ont été rédigés par un employé de Google.
Cependant, le concept n’est pas spécifique à Google (les autres ne l’appellent pas nécessairement « autorisation incrémentale »). Il est relativement courant sous différentes formes dans les applications mobiles et est adopté dans OAuth2 par d’autres fournisseurs. Par exemple, voici la documentation de Facebook sur le même sujet.
Vous connaissez probablement déjà ce domaine, mais il est considéré comme une « bonne pratique » de :
informer les utilisateurs que vous allez demander plus d’autorisations que les informations de base standard ;
et peut-être expliquer pourquoi.
Si l’utilisateur clique sur « S’inscrire avec Facebook » dans un formulaire de connexion Discourse, puis qu’en plus de son adresse e-mail, il lui est également demandé l’accès à ses groupes Facebook, il pourrait abandonner. Facebook le formule ainsi :
En règle générale, plus une application demande d’autorisations, moins il est probable que les personnes utilisent Facebook pour se connecter à votre application. En fait, nos recherches montrent que les applications qui demandent plus de quatre autorisations connaissent une baisse significative du nombre de connexions terminées.
Cela soulève la question de savoir s’il est judicieux de demander des portées supplémentaires au moment de l’authentification, et en gros, j’ai conclu que nous n’avons vraiment pas d’autres bonnes options. Il y a le besoin d’avoir un accès immédiat aux groupes dans certains scénarios (comme vous l’avez suggéré), et il y a aussi la réalité que sans cette demande préalable, de nombreux utilisateurs ne prendraient pas une étape supplémentaire pour autoriser leurs groupes sur un service, par exemple dans leur profil ou sur la page des groupes.
Cela doit être fait au moment de l’authentification, ce qui nous ramène au problème mentionné ci-dessus et à la raison pour laquelle j’ai mis en place un système de « seconde autorisation ». Il est en effet conçu pour être un « système » léger, dans la mesure où il est relativement facile pour un autre service, disons Facebook ou GitHub, de mettre en œuvre une demande de seconde autorisation pour obtenir l’accès aux groupes de l’utilisateur après qu’il se soit authentifié et, éventuellement, après avoir passé un certain test lié à ses informations de base.
Chaque fournisseur doit simplement :
Renvoyer un résultat avec secondary_authorization_url
Utiliser le paramètre state pour détecter à quelle demande d’autorisation l’utilisateur en est
Fournir une omniauth_secondary_authorization_description pour users/omniauth_callbacks/secondary_authorization.html.erb. Par exemple, voici celle pour Google, que l’utilisateur voit avant de confirmer la redirection de la seconde autorisation :
Comme vous vous êtes connecté avec une adresse e-mail %{domain}, nous devons demander la permission de voir vos groupes %{domain}.
Aucune de ces parties n’est spécifique à Google.
Ce que je souhaiterais faire ici, c’est permettre à l’utilisateur de dire « non » à la demande secondaire et de s’authentifier quand même. Dans le scénario Google Apps HD, ce n’est pas vraiment un problème car si son compte fait partie d’un domaine hébergé, il est peu probable qu’il veuille ou soit en mesure de dire non. Cependant, il faut l’accommoder pour couvrir toute la gamme des scénarios d’authentification, je pense.
Enfin, il convient également de noter que la seconde autorisation n’est pas nécessaire pour que associated_groups fonctionne. Un fournisseur d’authentification peut simplement demander la portée des groupes dès le départ, puis ajouter les groupes au résultat de l’authentification après avoir reçu la première réponse. En effet, nous devrions probablement intégrer cela comme une option aux plugins de base oauth2 et openid connect.
Domaine du fournisseur
Je pense qu’il faut une forme d’identifiant secondaire dans la table associated_groups qui soit lisible par l’administrateur du site. Il existe de nombreux scénarios où un nom de groupe seul peut ne pas suffire. Il y a la possibilité de conflits de noms entre les concepts équivalents de chaque service, par exemple :
gestion de groupes Google multi-domaines (vous pouvez avoir plusieurs domaines dans un seul espace de travail)
gestion de groupes GitHub multi-organisations
gestion de rôles Discord multi-serveurs
etc.
Nous pourrions changer domain en namespace peut-être. Nous pourrions l’inclure dans le name du groupe, mais cela nous donnerait-il des avantages ? Il pourrait être utile de requêter par le « domaine » ou le « namespace » à un moment donné. Oui, peut-être que namespace serait mieux que domain.
La raison pour laquelle il doit être « lisible par l’administrateur » est qu’il est utilisé dans l’étiquette visible par l’administrateur dans l’interface des groupes, en partie à des fins de désambiguïsation.
Je réfléchis à savoir s’il est logique d’essayer de stocker également un provider_id ici (s’il existe). Cela pourrait être utile plus tard.
Oui, je suis d’accord avec tout cela, et merci pour les conseils. Je vais essayer cette partie et nous pourrons en discuter davantage sur la PR.
@david Je viens de pousser quelques mises à jour sur ce point, notamment :
DistributedMutxes et in_batches dans group_associated_group
Tests d’acceptation (nous avions déjà rspec)
Il faudra sans doute encore quelques améliorations, mais cela fonctionne actuellement conformément aux spécifications et tous les tests sont validés. Faites un essai, faites-moi part de votre avis et des modifications que vous souhaiteriez.
Bonjour @angus ! Je me demande si vous avez fait de nouveaux progrès sur ce sujet ? Je suis très intéressé par le comportement « strict » simple, tel que je le comprends, et comme nous contrôlons notre fournisseur Oauth2/OpenID Connect, je ne m’inquiète pas du cas de « l’autorisation secondaire ». Y a-t-il une chance qu’une solution de ce type puisse être intégrée plus tôt ?
Si cela peut aider, notre environnement est documenté ici : Infrastructure/Authentication - Fedora Project Wiki, et j’ai configuré Discourse pour demander les scopes oauth2 suivants : openid profile email https://id.fedoraproject.org/scope/groups.
En gros, tout ce que je veux, c’est :
Laisser les niveaux de confiance et les groupes du personnel tels quels
Ajouter l’utilisateur à tous les groupes Discourse existants présents dans la liste fournie par le SSO, s’il n’y est pas déjà
Retirer l’utilisateur de tous les groupes dans lesquels il se trouve mais qui ne figurent pas dans la liste
J’avoue volontiers que je ne comprends pas toutes les subtilités… y a-t-il une complication que je ne saisis pas ?
Super — merci beaucoup. Je ne veux pas insister, mais le support de Discourse a suggéré que poster dans ce sujet serait le meilleur moyen de connaître l’état actuel des choses.
Je suis enthousiaste à l’idée de votre travail là-dessus, car il y a tant de choses que nous pourrons faire avec les sites Fedora Discourse une fois que nous aurons cela, et que nous ne pouvons tout simplement pas faire pour le moment !