Come puoi probabilmente immaginare, anch’io sono molto interessato a vedere una soluzione stabile a questo problema, che non è specifico del plugin openid-connect. Vediamo se possiamo trovare una strada da seguire.
Per riprendere il punto che hai sollevato.
Come decidi quali gruppi aggiungere o rimuovere dall’utente? E come ciò influisce sull’aggiunta o rimozione manuale degli utenti dai gruppi direttamente in Discourse?
un utente si autentica tramite OIDC con i gruppi ['group1', 'group2']
Nell’interfaccia utente di Discourse, l’utente viene aggiunto a group3
successivamente, lo stesso utente si autentica tramite OIDC con i gruppi ['group1']
Osservando la situazione come esseri umani, possiamo vedere che lo stato finale dovrebbe essere group1, group3 (group2 dovrebbe essere rimosso). Ma non credo ci siano sufficienti dati di stato tracciati per prendere quella decisione in modo programmatico. Allo stesso modo:
un utente si autentica tramite OIDC con i gruppi ['group1', 'group2']
Nell’interfaccia utente di Discourse, l’utente viene rimosso da group1
successivamente, lo stesso utente si autentica tramite OIDC con i gruppi ['group1', 'group2']
Ora cosa dovrebbe succedere? Un amministratore ha esplicitamente rimosso l’utente da group1, ma OIDC li ha appena aggiunti di nuovo. Questa è un’esperienza utente molto confusa per l’amministratore. Potremmo aver bisogno di un modo per identificare i gruppi come ‘gestiti esternamente’ e nascondere tutta l’interfaccia di aggiunta/rimozione
Penso che ci siano alcuni modi in cui potremmo gestire questo problema (non si escludono a vicenda)
Identificazione dei gruppi e degli attributi del token
In qualsiasi implementazione, l’identificazione esplicita di
quali gruppi possono avere la loro appartenenza gestita tramite autenticazione; e
quali attributi in un token di autenticazione governano l’appartenenza ai gruppi
dovrebbe essere obbligatoria, sia che ciò avvenga tramite impostazioni del sito, impostazioni dei gruppi o altro, e l’impostazione predefinita dovrebbe essere “disattivata”.
Gestione rigorosa o permissiva
La gestione è “rigorosa” se un utente viene rimosso quando il gruppo è assente dall’attributo del token identificato. La gestione è “permissiva” se un utente non viene rimosso quando il gruppo è assente dall’attributo del token identificato.
Come suggerisci, potresti disabilitare l’aggiunta o la rimozione dell’appartenenza ai gruppi tramite l’amministratore dei gruppi se la gestione fosse “rigorosa”.
Identificazione della fonte dell’appartenenza
Potresti anche memorizzare la fonte dell’appartenenza al gruppo nella tabella group_users per consentire un approccio “misto” all’interno di un gruppo, ovvero un amministratore non può rimuovere le appartenenze create tramite token di autenticazione.
Più ci penso, più sono convinto che questo dovrebbe essere fatto tramite le impostazioni del gruppo.
Grazie @angus per aver dato il via a questa discussione! So che molte persone sono molto interessate a questa funzionalità!
Interessante! Avevo sempre immaginato che i gruppi venissero creati automaticamente durante l’autenticazione e che il nome del gruppo corrispondesse 1:1 al nome del gruppo sul provider di identità. È così che funziona DiscourseConnect al momento.
Ma in realtà mi piace molto di più questa opzione più esplicita! Significa che le istanze di Discourse delle persone non saranno contaminate da gruppi non necessari provenienti dal loro provider di identità, e che gli amministratori possono personalizzare i nomi dei gruppi a loro piacimento. Ha molta affinità con la nostra attuale membership basata sul dominio email
Questo sembra buono dal punto di vista tecnico. La mia unica preoccupazione è che potrebbe essere difficile da spiegare agli utenti/amministratori. Se seguiamo la tua idea di ‘identificazione esplicita’ dei gruppi gestiti dall’autenticazione, penso che potremmo semplicemente implementare la gestione ‘rigorosa’?
Quando un gruppo è configurato come gestito dall’autenticazione, l’aggiunta/rimozione manuale è nascosta dietro un grande avviso e si comporta come la modalità ‘rigorosa’ che hai descritto.
Che ne pensi?
A parte: dovremmo anche assicurarci che tutte le aggiunte/rimozioni automatiche degli utenti vengano registrate nel log del gruppo. Questo renderà più facile per tutti capire cosa sta succedendo e perché.
Sì, penso che sia meglio essere espliciti, almeno per la versione 1 di questa serie di funzionalità. Non ho ancora incontrato un caso d’uso in cui la creazione automatica fosse realmente necessaria, ovvero in cui il gruppo non potesse semplicemente essere configurato dall’amministratore del sito prima di gestire qualsiasi rivendicazione.
Forse potremmo passare alla creazione automatica come opzione dopo aver implementato la versione esplicita?
Per quanto riguarda le Impostazioni del Gruppo, sarebbe qualcosa del genere:
Sezione Impostazioni: Membri (ovvero la sezione esistente) Titolo del Gruppo di Impostazioni: Gestione Autenticazione Impostazioni:
Servizio: Elenco dei servizi di autenticazione. “Tutti” sarebbe un’opzione. Questa impostazione fungerebbe anche da stato “abilitato/disabilitato” per questa serie di funzionalità. Cioè, l’impostazione predefinita sarebbe “Nessuno”.
Rivendicazione (Claim): campo di testo per identificare la rivendicazione del token ID. La “descrizione” per questa funzionalità (forse in un post qui su Meta) spiegherebbe i formati supportati, ad esempio booleano, stringa delimitata da virgole, ecc.
Modalità: vedi sotto
Sì, se ci fosse un’impostazione rigorosa/permissiva dovremmo spiegarla in modo conciso. Penso che l’approccio migliore sia concentrarsi su “aggiunta” e “rimozione”. Potresti descriverla più o meno così:
Impostazione: “Modalità”
Opzione 1 (permissiva):
Etichetta: “Aggiungi Membri”
Descrizione: “Consenti l’aggiunta di membri a questo gruppo durante l’autenticazione”
Opzione 2 (rigorosa):
Etichetta: “Aggiungi e Rimuovi Membri”
Descrizione: “Consenti l’aggiunta e la rimozione di membri durante l’autenticazione. Questo disabiliterà i controlli manuali dell’iscrizione.”
Il motivo per cui tengo molto a mantenere l’opzione “permissiva” è che, lavorando in passato con clienti su questo tipo di cose, questo è il caso d’uso più comune, ovvero:
La necessità principale è consentire l’accesso a un gruppo in base a uno stato in un servizio esterno
La necessità di rimuovere l’accesso in base allo stato nel servizio esterno è più marginale, ovvero le persone perdono l’accesso e dovrebbero essere rimosse, ma questo è relativamente meno importante
Spesso c’è il desiderio di mantenere la possibilità di controllare manualmente l’iscrizione su Discourse. Quando ho implementato l’approccio “rigoroso”, questo è stato “sorprendente” (ovvero “Perché la persona X ha perso l’iscrizione?”) nonostante le spiegazioni e il funzionamento (tecnico) corretto.
Sì, è importante, poiché spesso ci si chiederà “perché” qualcuno è stato aggiunto o rimosso, specialmente in modalità rigorosa, e noi (cioè “Discourse”) non avremo il controllo sulla veridicità delle rivendicazioni fatte dal servizio di autenticazione esterno.
Forse un nuovo tipo di azione “Rimuovi Utente” e “Aggiungi Utente” che includa il servizio di autenticazione responsabile dell’azione, ovvero “Rimuovi Utente ([nome servizio])”.
Un’altra “trappola” qui è che dovremo chiarire che questa non è una soluzione miracolosa per il caso d’uso “voglio che le appartenenze a un gruppo siano basate sul servizio esterno X”, poiché le persone non si autenticano frequentemente, o almeno non secondo le esigenze del caso d’uso standard di quel tipo.
Questa (gestione dell’autenticazione) è una parte necessaria per gestire quel tipo di caso d’uso, ma per servirlo adeguatamente è anche necessario configurare integrazioni basate su eventi, ad esempio un ricevitore webhook (abbiamo un plugin privato di ricezione webhook progettato per la gestione dei gruppi che spero di rendere open source entro breve tempo).
Questo è un lavoro prezioso che state facendo qui!
Solo a titolo di confronto funzionale, volevo condividere cosa facciamo noi alla Global Legal Empowerment Network, che utilizza WordPress e il plugin Discourse per WordPress. Abbiamo configurato il sistema in modo che ogni volta che un utente viene aggiornato su WordPress, l’aggiornamento venga riflesso anche su Discourse. Questo include alcuni gruppi speciali (ad esempio, membro principale, contribuente alle risorse, ecc.) nonché i dettagli del profilo. Abbiamo aggiunto un campo utente nascosto per “ultima modifica”, che ci ha aiutato nella risoluzione dei problemi e nel verificare che tutto funzionasse correttamente.
Blocciamo quei gruppi gestiti da remoto in modo che gli utenti non possano unirsi o uscire da essi su Discourse, ma non abbiamo visto la necessità di impedire la gestione dell’iscrizione ai gruppi da parte dello staff. Mi piace l’aspetto di ciò che state cercando di fare, ma devo ammettere che va un po’ oltre le mie capacità.
Abbiamo alcuni clienti di Discourse for Teams che utilizzano attivamente Okta per gestire l’accesso a tutte le applicazioni aziendali. Lì configurano anche dei ruoli, che vengono poi supportati, ad esempio, per fornire determinati livelli di accesso a Microsoft Tableau, ecc. Okta funge anche da directory, quindi gestiscono avatar utente, biografia, posizione e altre informazioni di profilo. Vorrebbero vedere queste informazioni sul profilo aggiornate in Teams anche esternamente a Okta.
Dovremmo cercare di mantenere il più possibile le funzionalità super-tecniche fuori dalle impostazioni del gruppo. Se è necessario collegarsi alla documentazione sulla sintassi, probabilmente significa che dovrebbe essere semplificata o relegata alle impostazioni del sito per gli amministratori. Penso che dovremmo lasciare questa parte a ciascun plugin di autenticazione da implementare, poiché può variare notevolmente.
Prendendo OIDC come esempio, quel plugin aggiungerebbe una nuova impostazione del sito openid_connect_roles_claim. Se si utilizza Okta, l’amministratore lo configurerebbe come groups. Penso che un array di stringhe sia un formato piuttosto standard per OIDC, ma potremmo esplorare opzioni più complesse qui se davvero necessario.
Per ricevere queste informazioni, Auth::Result riceverebbe un nuovo attributo roles, che accetta un semplice array di stringhe. Il core prenderà quindi questo (in Auth::Result#apply_user_attributes!) e lo caricherà in una nuova tabella UserAssociatedRoles con le colonne (provider_name, user_id, role). Questa tabella UserAssociatedRoles ci fornisce l’“identificazione della fonte di appartenenza” che hai menzionato nel post originale.
Immagino che le impostazioni del gruppo assomiglino a qualcosa del genere:
I nomi dei ruoli sarebbero preceduti dal provider_name. Il completamento automatico si baserebbe su tutti i valori esistenti nella tabella UserAssociatedRoles, ma accetteremmo anche valori non completati automaticamente nel caso in cui il ruolo non fosse ancora stato visto da Discourse.
La bellezza di avere una tabella completa user_associated_roles nel database è che gli amministratori possono fare ciò che vogliono con i gruppi di Discourse e le appartenenze verranno aggiornate istantaneamente senza che gli utenti debbano accedere nuovamente.
È giusto. Penso che sarebbe meglio mantenere tutto il più semplice possibile, almeno per la v1, quindi preferirei non avere più “modalità”. Che ne dici di avere questo come comportamento predefinito:
Aggiungi membri quando hanno un ruolo corrispondente dal provider di autenticazione
Rimuovi membri quando quel ruolo scompare
Consenti l’aggiunta/rimozione anche in Discourse
Se un amministratore tenta di rimuovere un utente che è stato aggiunto tramite un provider di autenticazione, mostra un avviso: “questo utente potrebbe essere aggiunto di nuovo la prossima volta che accede”
Penso che dovremmo cercare di essere sicuri per impostazione predefinita qui e rimuovere i membri quando perdono il ruolo sul provider di identità. Con i miglioramenti al registro delle appartenenze, spero che dovrebbe essere meno confuso rispetto allo status quo attuale.
Per i siti che non vogliono davvero questo, potremmo avere un’impostazione del sito come remove_group_membership_when_auth_role_lost (predefinito true).
Sì, certamente. Abbiamo anche questo problema con altri metadati utente come nome, avatar, ecc. Penso che a breve dovremo esaminare la possibilità di creare un equivalente di sync_sso per altri plugin di autenticazione. Potrebbe essere passata tutte le informazioni che normalmente vengono passate tramite OIDC / SAML / ecc., comprese queste nuove informazioni sui “ruoli”. Probabilmente un progetto separato, però.
Come ti sembra tutto questo @angus? È leggermente meno flessibile rispetto alle modalità separate rigorose/permissive, ma penso che avere una sola modalità renderà molto più semplice la risoluzione dei problemi / la documentazione / il supporto.
Con quel piano, ecco una visione d’insieme di come appare dal punto di vista di un amministratore:
In Discourse, imposta openid_connect_roles_claim su groups
Configurazione di un nuovo gruppo
Crea un gruppo Discourse come al solito. Configura nome/nome completo/flair/ecc. come preferisci
Vai alle preferenze “appartenenza”, posiziona il cursore nel menu a tendina dei ruoli SSO e scegli un ruolo dal menu. Se Discourse non ha ancora visto qualcuno accedere con quel ruolo, dovrai inserirlo manualmente
Puoi specificare più ruoli per un singolo gruppo Discourse. Ad esempio, un gruppo “team” in Discourse potrebbe essere una combinazione di oidc:employees e oidc:contractors
Premi salva e l’appartenenza al gruppo verrà aggiornata istantaneamente con le informazioni sul ruolo che Discourse ha memorizzato nella cache dai precedenti accessi.
Nei futuri accessi, eventuali modifiche ai ruoli del provider di identità verranno riflesse nel gruppo Discourse
Puoi ancora aggiungere/rimuovere utenti dal gruppo nell’interfaccia nativa di Discourse
Se provi a rimuovere un utente che è stato aggiunto tramite un provider di identità, verrà mostrato un avviso che indica che l’utente potrebbe essere re-aggiunto al prossimo accesso
Se in seguito decidi di non voler associare quel ruolo IDP al gruppo, puoi rimuoverlo e tutti gli utenti che erano membri tramite quel ruolo verranno rimossi
Sì, però ci sono casi in cui ha senso supportare un claim booleano. Ma sì, concordo: teniamolo semplice per ora supportando solo un array di stringhe.
Facendolo autentificatore per autentificatore, immagino avremmo anche un metodo group_sync_enabled? in Auth::Authenticator, che verrebbe sovrascritto nei singoli autentificatori e, negli autentificatori generici, in base all’esistenza di un valore nell’impostazione rilevante.
Questo in realtà solleva una domanda interessante. Potrebbe essere utilizzato anche dagli specifici autentificatori di servizio, come Facebook (“groups”?), Google (“groups”?) e Discord (“roles”). Non sono sicuro che i loro token ID contengano tali informazioni, ma forse vale la pena considerarlo dal punto di vista del design tecnico (cioè la possibilità di aggiungerlo in futuro). Non credo sia una preoccupazione primaria per la v1.
Questi meccanismi sembrano corretti, però sono curioso del motivo per cui siamo passati dall’uso di “groups” a “roles” qui. Non è un grosso problema, ma voglio essere sicuro di non aver perso qualcosa.
Dal punto di vista dell’esperienza utente, vedo che potrebbe sorgere confusione dall’uso di questa nomenclatura (cioè “roles” e “groups” contemporaneamente), anche se è fondata su una distinzione tecnica utile. È anche possibile che ci sia confusione tra gli sviluppatori che si avvicinano a questo senza il contesto di fondo.
Mi piace la tabella separata, ma forse dovremmo avere un’associazione di chiave esterna con user_associated_accounts invece di provider_name? Potrebbe essere utile in operazioni come quelle di pulizia. Immagino che la risposta a questa domanda dipenda in parte da quanto vogliamo che l’associazione gruppo/ruolo sia interconnessa con l’account associato dal punto di vista del prodotto. Ci sarebbero degli svantaggi nel collegare i due ora?
** Modifica, immagino che abbiamo già l’associazione tramite user_id.
Sembra corretto, però sto pensando che abbiamo anche queste informazioni su base provider grazie all’impostazione del sito che hai proposto, che coprirebbe anche il caso in cui il ruolo non sia stato ancora visto. Forse c’è un modo per usare Discourse.authenticators qui, cioè un elenco in memoria dei provider e delle loro affermazioni?
Penso che sia un buon compromesso. Soprattutto se abbiamo anche l’impostazione del sito menzionata di seguito.
Su base plugin (cioè autentificatore)? Se è così, sono d’accordo. Dovrebbe soddisfare questa esigenza per la v1.
Buona sintesi del flusso utente In attesa dei suggerimenti relativamente minori che ho fatto, sono d’accordo con la direzione presa.
Sì, certo. Stavo pensando in particolare a Google, dato che le persone amano utilizzarlo per le loro organizzazioni GSuite, che presumo abbiano un concetto di gruppi.
Credo di aver cercato di evitare qualsiasi confusione tra “Gruppi del Provider di Identità” e “Gruppi di Discourse”… ma è possibile che cambiando il nome abbia solo reso le cose più confuse. Sono molto felice di attenermi a “gruppi” qui.
Il mio ragionamento era che supportiamo ancora provider di autenticazione non gestiti, quindi potrebbe non esserci un record user_associated_account. Possiamo comunque fare il join su di esso tramite user_id come hai detto
Non sono sicuro che l’impostazione del sito sia d’aiuto qui. Se stiamo parlando di un array di stringhe che rappresentano “ruoli”, l’impostazione del sito non specificherebbe quali sono effettivamente i ruoli. Specificherebbe solo come ottenere l’array. Ha senso?
Sì, hai ragione. Stavo ancora pensando a un’implementazione precedente in cui avevo incluso gruppi specifici direttamente nell’impostazione, ma in questo caso non lo stiamo facendo, il che va bene.
Penso che ci siano sufficienti informazioni per iniziare a lavorare sulla PR, che inizierò più avanti questa settimana, a meno che tu non abbia altre preoccupazioni? Aggiornò qui se dovessero sorgere problemi durante il percorso.
Ok, finalmente ho una versione in lavorazione da condividere qui
Alcune note.
Per l’implementazione iniziale ho scelto il caso leggermente più complesso dei gruppi hd di Google Apps, poiché ritengo che aiuti a considerare tutte le possibili combinazioni, ad esempio la necessità di gestire gruppi specifici di dominio provenienti da un provider.
Per implementare questo caso d’uso, ho dovuto introdurre un nuovo concetto di “autorizzazione secondaria” al momento dell’autenticazione, per consentire un’autorizzazione incrementale. Ho valutato diverse modalità per richiedere le autorizzazioni di gruppo specifiche di un utente (ovvero se si sta autenticando con un hd), e questa mi è sembrata la più fattibile. Riconosco che si tratta forse di un cambiamento più significativo del previsto in questo ambito, ma potrebbe valere la pena discuterne.
Si noti che per implementare il caso dei gruppi hd di Google è necessario concedere ai membri non amministratori dei gruppi hd di Google Apps l’autorità di amministratore delegato per elencare i propri gruppi (tramite l’API directory amministrativa). Esiste effettivamente un ruolo predefinito di amministratore “beta” chiamato “Lettore di gruppi” che funziona bene per questo scopo. Vedere Prebuilt administrator roles | User management | Google Workspace Help
L’implementazione di Google funziona. Se la configurate e poi vi autenticate con un hd, i vostri gruppi hd di Google saranno disponibili nelle impostazioni di appartenenza automatica ai gruppi; sarete aggiunti a quel gruppo Discourse se quel gruppo hd viene selezionato, rimossi se viene rimosso (entrambe le azioni vengono registrate con un certo grado di dettaglio) e gli utenti successivi in quel gruppo hd di Google che si autenticano verranno aggiunti immediatamente.
I dettagli dovrebbero essere evidenti dal codice e dai test. Noterete anche che ho finito per aggiungere tre nuove tabelle. Ho tentato alcune soluzioni più “leggere”, ma ciascuna si è rivelata più contorta e inefficiente quando si trattava di gestire gli aggiornamenti dei gruppi associati a un utente e dei gruppi associati a un gruppo. È difficile evitare di creare nuove tabelle per ciascuno. Sono aperto a idee sul fronte della modellazione dei dati, e più in generale.
Alcuni task tecnici rimasti (a parte le domande concettuali/di prodotto sollevate sopra). Anche su questo fronte sono benvenute le suggerimenti:
Forse serializzare l’attributo label di associated_groups (invece di modellarlo sul client).
Aggiungere i test mancanti e i qunit.
Forse spostare la creazione/distruzione di user_associated_group / group_associated_group in un job in background, poiché con un gran numero di elementi potrebbe essere lento.
Sono un po’ esitante riguardo alla questione dell’“autorizzazione secondaria” e anche alla colonna provider_domain. Potresti spiegare meglio perché sono necessari? Sembra che siano piuttosto specifici di Google… c’è un motivo per cui non possiamo richiedere l’ambito admin.directory.group.readonly durante la prima richiesta di autenticazione? E magari anteporre semplicemente il nome del gruppo al dominio? (o escludere completamente il dominio, dato che presumo che le persone lo useranno solo con un singolo dominio ‘hosted’ di Google?)
Sì, sono assolutamente d’accordo con le 3 tabelle qui: mantiene tutto più pulito
Concordo
Dobbiamo fare attenzione qui. Qualsiasi appartenenza a gruppi deve essere assegnata prima che l’utente carichi il sito per la prima volta. Altrimenti, non vedrà le cose specifiche del gruppo al primo accesso (ad esempio, categorie sicure). Quindi penso che le modifiche ai record user_associated_group debbano essere elaborate in modo sincrono.
Ma per le modifiche ai record group_associated_group, penso che tu abbia ragione. Le modifiche lì potrebbero influenzare migliaia di utenti, quindi dovranno essere elaborate in_batches. Penso che inizierei facendolo in modo sincrono, con un indicatore di caricamento nell’interfaccia utente. In questo modo, gli amministratori potranno chiaramente vedere quando è in esecuzione/completato.
Se vediamo che ci si avvicina ai 30 secondi (il timeout della richiesta di unicorn), allora potremmo dover pensare a un job in background.
Potremmo anche dover pensare di aggiungere un blocco DistributedMutex qui. Ad esempio, se un utente accede mentre viene elaborata una modifica a group_associated_group, cosa succede? Sono felice di discutere questo tipo di cose su GitHub una volta che avremo finalizzato l’architettura complessiva.
Per il mio caso d’uso, sarei perfettamente soddisfatto se il comportamento di Discourse Connect venisse implementato anche per altri provider di autenticazione.
Il motivo per cui non puoi richiedere le autorizzazioni per i gruppi nella prima richiesta è che non sai chi sta effettuando l’accesso o cosa è disposto a condividere. Potresti limitare la mappatura dei gruppi associati a Google alle persone che utilizzano l’impostazione google_oauth2_hd, tuttavia ciò limiterebbe notevolmente la portata della funzionalità. È relativamente comune avere un team che utilizza Google Apps insieme a utenti “pubblici” che desiderano anche utilizzare l’autenticazione Google.
*edit Dovrei chiarire che, se richiedi uno scope per i gruppi e l’utente non può concederlo (ad esempio, il suo HD non ha delegato l’autorità agli utenti non amministratori come descritto sopra), l’autenticazione fallirà. Non puoi richiedere scope opzionali insieme a scope obbligatori.
Inoltre, quell’approccio, ovvero richiedere uno scope per i gruppi in anticipo come pratica standard nell’implementazione di questa funzionalità nei vari metodi di autenticazione, sarebbe probabilmente più specifico di Google rispetto all’alternativa, poiché non hai sempre l’equivalente del sistema di hosted domain per limitare l’accesso. Ad esempio, potrei sbagliarmi, ma non credo esista un modo per limitare l’accesso tramite Github OAuth2 a una specifica organizzazione Github.
In altre parole, in molti casi ti troveresti con la scelta di chiedere a tutti coloro che utilizzano quel metodo di autenticazione di concedere lo scope groups pertinente o di non utilizzare la funzionalità. Tale approccio potrebbe funzionare in alcuni contesti, ma non in molti. Questo approccio di autorizzazione incrementale offre ai diversi metodi di autenticazione maggiore flessibilità nell’implementare la funzionalità.
È vero che Google ha sostenuto l’autorizzazione incrementale nello spazio OAuth2; ad esempio, tutti i documenti di lavoro sull’argomento sono stati scritti da un dipendente di Google.
Tuttavia, il concetto non è specifico di Google (altre persone non lo chiamano necessariamente “autorizzazione incrementale”). È relativamente comune in diverse forme nelle app mobili e sta venendo adottato in OAuth2 da altri provider. Ad esempio, ecco la documentazione di Facebook sullo stesso argomento.
Probabilmente sei già familiare con questo ambito, ma è considerato “buona pratica”
informare gli utenti che stai per chiedere più autorizzazioni rispetto alle informazioni di base standard;
e forse spiegare il motivo.
Se l’utente ha cliccato “Iscriviti con Facebook” in un modulo di accesso Discourse e, oltre alla sua email, gli è stato chiesto anche l’accesso ai suoi gruppi Facebook, potrebbe abbandonare. Facebook lo esprime così:
Come regola generale, più autorizzazioni richiede un’app, meno è probabile che le persone utilizzino Facebook per accedere alla tua app. In effetti, le nostre ricerche mostrano che le app che richiedono più di quattro autorizzazioni subiscono un calo significativo nel numero di accessi completati.
Questo pone la questione se sia una buona idea richiedere scope aggiuntivi al momento dell’autenticazione; fondamentalmente ho concluso che non abbiamo davvero altre buone opzioni. C’è la necessità di avere accesso immediato ai gruppi in alcuni scenari (come hai accennato), e c’è anche la realtà che, senza richiederlo in anticipo, molti utenti non compirebbero un’ulteriore azione per autorizzare i loro gruppi su un servizio, ad esempio nel loro profilo o nella pagina dei gruppi.
Deve essere fatto al momento dell’autenticazione, il che ci riporta al problema citato sopra e al motivo per cui ho implementato un sistema di “autorizzazione secondaria”. È effettivamente inteso come un “sistema” leggero, nella misura in cui è relativamente facile per un altro servizio, ad esempio Facebook o Github, implementare una richiesta di autorizzazione secondaria per ottenere l’accesso ai gruppi dell’utente dopo che si sono autenticati e, opzionalmente, superando un certo test relativo alle loro informazioni di base.
Ogni provider deve solo:
Restituire un risultato con secondary_authorization_url
Utilizzare il parametro state per rilevare a quale richiesta di autorizzazione l’utente sta arrivando
Fornire un omniauth_secondary_authorization_description per users/omniauth_callbacks/secondary_authorization.html.erb. Ad esempio, ecco quello per Google, che l’utente vede prima di confermare il reindirizzamento per l’autorizzazione secondaria:
Poiché hai effettuato l'accesso con un'email di %{domain}, dobbiamo chiedere il permesso di visualizzare i tuoi gruppi %{domain}.
Nessuna di queste parti è specifica di Google.
Ciò che vorrei fare qui è permettere all’utente di dire “no” alla richiesta secondaria e autenticarsi comunque. Nello scenario Google Apps HD questo non è davvero un problema, poiché se il loro account fa parte di un hosted domain, è improbabile che vogliano o siano in grado di dire di no. Tuttavia, dovrebbe essere previsto per consentire l’intera gamma di scenari di autenticazione, penso.
Infine, va notato anche che l’autorizzazione secondaria non è obbligatoria per il funzionamento di associated_groups. Un provider di autenticazione può semplicemente richiedere lo scope per i gruppi in anticipo e poi aggiungere i gruppi al risultato dell’autenticazione dopo aver ricevuto la prima risposta. In effetti, dovremmo probabilmente integrarlo come opzione nei plugin oauth2 e openid connect di base.
Dominio del provider
Penso che ci debba essere una qualche forma di identificatore secondario nella tabella associated_groups leggibile dall’amministratore del sito. Ci sono diversi scenari in cui il nome del gruppo da solo potrebbe non essere sufficiente. C’è la possibilità di conflitti di nomi tra il concetto equivalente di ciascun servizio, ad esempio:
gestione dei gruppi Google multi-dominio (puoi avere più domini in un unico workspace)
gestione dei gruppi Github multi-organizzazione
gestione dei ruoli Discord multi-server
ecc.
Potremmo cambiare domain in namespace. Potremmo includerlo nel name del gruppo, ma ci darebbe qualche vantaggio? Potrebbe essere utile interrogare per “dominio” o “namespace” in qualche momento. Sì, forse namespace sarebbe meglio di domain.
Il motivo per cui deve essere “leggibile dall’amministratore” è che viene utilizzato nell’etichetta visibile all’amministratore nell’interfaccia utente dei gruppi, in parte per scopi di disambiguazione.
Sto valutando se abbia senso tentare di memorizzare anche un provider_id qui (se esiste). Potrebbe essere utile in futuro.
Sì, sono d’accordo con tutto questo e grazie per i consigli. Proverò a fare questa parte e ne discuteremo ulteriormente nella PR.
@david Ho appena applicato alcuni aggiornamenti su questo, tra cui:
DistributedMutxes e in_batches in group_associated_group
Test di accettazione (avevamo già rspec)
Senza dubbio sarà necessario un ulteriore lavoro, ma al momento funziona secondo le specifiche e tutti i test sono superati. Prova a usarlo, fammi sapere cosa ne pensi e quali modifiche vorresti apportare.
Ciao @angus! Sono curioso di sapere se hai fatto ulteriori progressi su questo? Sono molto interessato al comportamento “strict” semplice, per come lo ho capito, e dato che controlliamo noi il nostro provider OAuth2/OpenID Connect, non mi preoccupa il caso della “autorizzazione secondaria”. C’è la possibilità che qualcosa del genere possa essere implementato prima?
Se può essere d’aiuto, il nostro ambiente è documentato qui: Infrastructure/Authentication - Fedora Project Wiki, e ho configurato Discourse per richiedere l’ambito OAuth2 openid profile email https://id.fedoraproject.org/scope/groups.
Fondamentalmente, ciò che voglio è:
Lasciare invariato il livello di fiducia e i gruppi dello staff
Aggiungere l’utente a eventuali gruppi Discourse esistenti nella lista proveniente dall’SSO, se non vi è già presente
Rimuovere l’utente da tutti i gruppi in cui si trova che non sono presenti nella lista
Ammetto liberamente di non comprendere tutte le sottigliezze… c’è una complicazione che non ho capito?
Ho messo da parte del tempo questo weekend per lavorare su questo, Matt. Avrò un aggiornamento la prossima settimana, probabilmente sulla PR su GitHub.
Fantastico, grazie mille. Non voglio essere insistente, ma il supporto di Discourse ha suggerito che pubblicare in questo argomento sia il modo migliore per conoscere lo stato attuale delle cose.
Non vedo l’ora di vedere il tuo lavoro su questo, perché con i siti Discourse di Fedora avremo molte possibilità che al momento non abbiamo!