Continuando da discussione su Future Social Authentication Improvements…
Stiamo attualmente lavorando per spostare tutte le informazioni relative agli “account associati” in un’unica tabella del database. Questo aiuterà a ridurre significativamente la logica duplicata e consentirà uno sviluppo più rapido in futuro. Ad esempio, la migrazione della nostra logica core di Twitter al nuovo sistema ha ridotto il numero di righe di codice da 136 a sole 24
.
Questo post non è progettato per essere un manuale di istruzioni passo-passo per aggiungere un nuovo provider di autenticazione, ma ha lo scopo di fornire una panoramica, indicando il codice sorgente pertinente ove necessario.
Implementazione di un autenticatore
Ogni autenticatore deve implementare una sottoclasse di Auth::Authenticator. Per utilizzare la nuova logica condivisa, l’autenticatore può invece estendere Auth::ManagedAuthenticator. Un esempio di implementazione essenziale può essere trovato nell’autenticatore Facebook core:
name e register_middleware devono essere sovrascritti dalle classi di implementazione, insieme a enable_setting — l’impostazione booleana del sito che un amministratore utilizza per attivare il provider.
Un autenticatore dovrebbe anche dichiarare required_settings: le impostazioni del sito che devono avere un valore prima che un’autenticazione possa avere successo. La classe di base le utilizza per configured?, e enabled? è enable_setting && configured?, quindi un provider con credenziali mancanti non viene mai pubblicizzato sulla pagina di accesso e la sua rotta /auth/<name> rimane chiusa — altrimenti, facendo clic sul pulsante, l’utente verrebbe lasciato sulla pagina di errore del provider stesso senza modo di tornare indietro. Dichiarare required_settings consente anche a AuthProviderCredentialsValidator di rifiutare l’attivazione del provider in primo luogo; collegalo con validator: "AuthProviderCredentialsValidator" sull’impostazione di attivazione.
def enable_setting
:enable_google_oauth2_logins
end
def required_settings
%i[google_oauth2_client_id google_oauth2_client_secret]
end
Un autenticatore che sovrascrive direttamente enabled? opta fuori da entrambi i controlli.
Nota a margine: per la compatibilità multisite, è importante che qualsiasi informazione specifica del sito venga fornita a omniauth in una lambda
setup, piuttosto che essere fissata al momento della definizione. Vedi tutti gli autenticatori core per esempi di questo.
Tutta la logica per collegare gli account esterni agli account Discourse è gestita da Auth::ManagedAuthenticator. Questo si basa sul provider omniauth che restituisce dati nel formato definito nella loro documentazione. Se è necessaria qualsiasi manipolazione di questi dati, gli Autenticatori possono sovrascrivere il metodo after_authenticate e manipolare l’auth_token come richiesto. Ad esempio, l’autenticatore Twitter core rimuove tutte le informazioni extra dal token:
I dati sono memorizzati nella tabella del database user_associated_accounts. provider_uid, info, credentials e extra sono tutti presi direttamente dai dati restituiti da omniauth.
Una volta definita una classe Authenticator, deve essere registrata. Questo deve avvenire presto nel ciclo di vita dell’applicazione e non può avvenire all’interno del metodo after_initialize di un plugin. La registrazione minima può semplicemente contenere un riferimento all’autenticatore. In un plugin, la registrazione può essere effettuata utilizzando la funzione auth_provider. Ad esempio:
auth_provider authenticator: OpenIDConnectAuthenticator.new()
Nel core, la registrazione avviene in discourse.rb. Un elenco completo delle opzioni possibili per AuthProvider può essere trovato qui. Il contenuto testuale può essere definito utilizzando queste opzioni, ma è meglio fornire stringhe localizzabili in client.en.yml seguendo le chiavi standard. Ad esempio:
Note aggiuntive su ManagedAuthenticator di @fantasticfears
ManagedAuthenticator in dettaglio
Potresti aver bisogno di lavorare su qualcosa di speciale per l’autenticazione. E vorresti sapere di più su ManagedAuthenticator. Fondamentalmente, ha diverse operazioni, opzioni e controlla come verranno utilizzati i dati.
Discourse gestisce le informazioni degli utenti con due controller. Users::OmniauthCallbacksController gestisce il payload una volta completata l’autenticazione OAuth2. after_authenticate viene chiamato qui. can_connect_existing_user? viene utilizzato anche qui.
Ci sono alcuni metodi privati che puoi leggere per capire come funzionano i diversi campi dei dati.
if authenticator.can_connect_existing_user? && current_user
@auth_result = authenticator.after_authenticate(auth, existing_account: current_user)
else
@auth_result = authenticator.after_authenticate(auth)
end
UsersController ha revoke_account che utilizza can_revoke? e revoke. Ma perché il metodo revoke funzioni in remoto, è necessario costruire la propria implementazione.
UserAuthenticator è una classe di servizio che aiuta ad autenticare (verificando la conferma dell’email o il percorso OAuth2) gli utenti. after_create_account viene chiamato qui.
La logica principale rimane in after_authenticate con la classe dati Auth::Result. Seguiamo la struttura dati qui. extra_data verrà passato a after_create_account per creare i record correlati.
result.extra_data = {
provider: auth_token[:provider],
uid: auth_token[:uid],
info: auth_token[:info],
extra: auth_token[:extra],
credentials: auth_token[:credentials]
}
Proverà a corrispondere e collegare a un account esistente.
Potresti chiederti perché la creazione automatica dell’account è possibile ma non c’è User.create. Questo viene fatto in UsersController#create.
authentication = UserAuthenticator.new(user, session)
L’utente è un’istanza nuova che verrà popolata con i dati della sessione preparati dal provider di autenticazione. Fidati di me, è solo magia.
Migrazione al nuovo sistema
Per fornire un passaggio senza interruzioni al nuovo sistema, i dati dovrebbero essere migrati dalla vecchia posizione di archiviazione. Per i provider di autenticazione core, ciò potrebbe riguardare tabelle dedicate. Per i plugin, potrebbe trattarsi di plugin_store_rows o oauth2_user_infos. I dati minimi richiesti in una riga di user_associated_accounts sono provider_name, provider_uid e user_id. Per un esempio di migrazione vedere:
Una volta che il sistema ManagedAuthenticator sarà stato rilasciato nel ramo stabile con la v2.2.0, inizieremo a migrare i plugin di autenticazione ufficiali. A quel punto, verrà aggiunto qui un esempio di migrazione di plugin_store_row.
Questo documento è sottoposto a controllo di versione - suggerisci modifiche su github.