Discourseに新しい「managed」認証メソッドを追加

Future Social Authentication Improvements… の続きです。

現在、すべての「関連アカウント」情報を単一のデータベーステーブルに移行するプロセスを進めています。これにより、重複したロジックを大幅に削減し、将来の開発をより迅速に行うことができます。例えば、コアのTwitterロジックを新しいシステムに移行した結果、コードの行数が136行からわずか24行に減少しました :tada:

この投稿は、新しい認証プロバイダーを追加するためのステップバイステップの操作マニュアルを目的としたものではありませんが、必要に応じて関連するソースコードへのリンクを示しつつ、概要を提供することを目的としています。

認証器(Authenticator)の実装

各認証器は、Auth::Authenticator のサブクラスを実装する必要があります。新しい共有ロジックを使用するには、代わりに Auth::ManagedAuthenticator を拡張することもできます。最小限の実装の例は、コアのFacebook認証器にあります:

nameenabled? および register_middleware は、実装クラスによってオーバーライドされる必要があります。

:information_source: 補足: マルチサイト互換性のため、サイト固有の情報は定義時に固定するのではなく、setup ラムダを介して omniauth に供給することが重要です。これの例は、すべてのコア認証器を参照してください。

外部アカウントをDiscourseアカウントにリンクするすべてのロジックは Auth::ManagedAuthenticator によって処理されます。これは、omniauthプロバイダーがそのドキュメントで定義された形式でデータを返すことに依存しています。このデータの変換が必要な場合、認証器は after_authenticate メソッドをオーバーライドし、必要に応じて auth_token を操作できます。例えば、コアのTwitter認証器はトークンからすべての extra 情報を削除します:

データは user_associated_accounts データベーステーブルに保存されます。provider_uidinfocredentials および extra はすべて、omniauthが返すデータから直接取得されます。

Authenticator クラスが定義された後、それを登録する必要があります。これはアプリケーションのライフサイクルの早期に行わなければならず、プラグインの after_initialize メソッド内で行うことはできません。最小限の登録には、認証器への参照を含めるだけで構いません。プラグインでは、auth_provider 関数を使用して登録を行うことができます。例えば:

auth_provider authenticator: OpenIDConnectAuthenticator.new()

コアでは、登録は discourse.rb で行われます。AuthProvider のオプションの完全なリストはここにあります。テキストコンテンツはこれらのオプションを使用して定義することができますが、標準のキーに従って client.en.yml にローカライズ可能な文字列を提供する方が望ましいです。例えば:

@fantasticfears による ManagedAuthenticator の追加ノート

ManagedAuthenticator の詳細

認証に関する特別な何かを構築する必要があるかもしれません。そして ManagedAuthenticator についてさらに詳しく知りたいと思っているかもしれません。基本的に、それはいくつかの操作やオプションを持ち、データがどのように使用されるかを制御します。

Discourseは2つのコントローラーでユーザー情報を管理しています。Users::OmniauthCallbacksController は、OAuth2認証が完了した後にペイロードを管理します。after_authenticate はここで呼び出されます。can_connect_existing_user? もここで使用されます。
異なるデータフィールドがどのように機能するかを理解するために、読み取ることができるいくつかのプライベートメソッドがあります。

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 には can_revoke?revoke を使用する revoke_account があります。ただし、revoke メソッドがリモートで機能するには、独自のインプリメンテーションを構築する必要があります。

UserAuthenticator は、ユーザーの認証(メール確認またはOAuth2パスの検証)を助けるサービスクラスです。after_create_account はここで呼び出されます。

コアのロジックは、Auth::Result データクラスを持つ after_authenticate に残っています。ここではデータ構造に従います。extra_data は関連レコードの作成のために after_create_account に渡されます。

result.extra_data = {
  provider: auth_token[:provider],
  uid: auth_token[:uid],
  info: auth_token[:info],
  extra: auth_token[:extra],
  credentials: auth_token[:credentials]
}

既存のアカウントとのマッチングと接続を試みます。

なぜ自動アカウント作成が可能なのに User.create がないのかと疑問に思うかもしれません。これは UsersController#create で行われます。

authentication = UserAuthenticator.new(user, session)

ユーザーは新しいインスタンスであり、認証プロバイダーによって準備されたセッションデータによって埋められます。信じてください、それ hanyalah magic です。


新しいシステムへの移行

新しいシステムへのシームレスな切り替えを提供するために、データは旧ストレージ場所から移行されるべきです。コアの認証プロバイダーの場合、これは専用のテーブルかもしれません。プラグインの場合、plugin_store_rows または oauth2_user_infos かもしれません。user_associated_accounts の行に必要な最小限のデータは provider_nameprovider_uid および user_id です。移行の例については以下を参照してください:

ManagedAuthenticator システムが v2.2.0 で安定版ブランチにリリースされた時点で、公式の認証プラグインの移行を開始します。この時点で、plugin_store_row 移行の例がここに追加されます。


このドキュメントはバージョン管理されています - 変更提案は github で行ってください。

「いいね!」 23

@david ここで行われたすべての作業は非常にクールです。大変感謝しています。また、非常に便利な GitHub - discourse/discourse-development-auth: A discourse plugin which adds a fake authentication provider. For development purposes only. も試す機会がありました。

ただ一つ注意点があります。この機能はローカルのEmber CLIとうまく連携しません(サインアップポップアップが表示されません)。プラグインを追加して認証プロバイダーを追加しようとしているときに頭を悩ませていましたが、NO_EMBER_CLI=1 を使用することを思いつき、すべてが機能し始めました。

「いいね!」 7

認証機能の実装が、

への正しいアプローチかどうかを知りたいです。

登録されているすべての認証機能がアプリの早い段階で呼び出されるため、URLにユーザー名とメール認証のヒントが含まれているかどうかをそこでテストし、「ログインリンクを送信」フォームを応答としてレンダリングできる、と理解して合っていますか?