إضافة طريقة مصادقة "مُدارة" جديدة إلى Discourse

متابعة من Future Social Authentication Improvements…

نحن الآن في مرحلة نقل جميع معلومات “الحسابات المرتبطة” إلى جدول قاعدة بيانات واحد. سيساعد ذلك في تقليل المنطق المكرر بشكل كبير، ويتيح تطويرًا أسرع في المستقبل. على سبيل المثال، نقل منطق Twitter الأساسي إلى النظام الجديد قلّل عدد أسطر الكود من 136 إلى 24 فقط :tada:.

لا يُصمَّم هذا المنشور ليكون دليلًا تعليميًا خطوة بخطوة لإضافة مزوّد مصادقة جديد، بل يهدف إلى تقديم نظرة عامة، مع الإشارة إلى الكود المصدري ذي الصلة عند الحاجة.

تنفيذ المصادق (Authenticator)

يجب على كل مصادق (Authenticator) أن ينفّذ فئة فرعية من Auth::Authenticator. لاستخدام المنطق المشترك الجديد، يمكن للمصادق بدلاً من ذلك أن يوسّع Auth::ManagedAuthenticator. يمكن العثور على مثال لتنفيذ أساسي في مصادق Facebook الأساسي:

يجب تجاوز name و register_middleware من قبل الفئات المنفذة، بالإضافة إلى enable_setting — وهو إعداد الموقع المنطقي (boolean) الذي يستخدمه المسؤول لتفعيل المزوّد.

ينبغي أيضًا أن يُعلن المصادق عن required_settings: وهي إعدادات الموقع التي يجب أن تحتوي على قيمة قبل أن تنجح المصادقة. تستخدم الفئة الأساسية هذه الإعدادات لـ configured?، و enabled? هو enable_setting && configured?، لذا فإن المزوّد الذي تفتقد بياناته الاعتمادية لن يُعلن عنه أبدًا في صفحة تسجيل الدخول، ويبقى مساره /auth/<name> مغلقًا — وإلا فإن النقر على الزر سيترك المستخدم عالقًا في صفحة خطأ خاصة بالمزوّد دون سبيل للعودة. كما يتيح الإعلان عن required_settings لـ AuthProviderCredentialsValidator رفض تفعيل المزوّد في المقام الأول؛ قم بتوصيله باستخدام validator: "AuthProviderCredentialsValidator" على إعداد التفعيل.

def enable_setting
  :enable_google_oauth2_logins
end

def required_settings
  %i[google_oauth2_client_id google_oauth2_client_secret]
end

المصادق الذي يتجاوز enabled? مباشرةً يختار الخروج من كلا البوابتين.

:information_source: هامش: لتوافق المواقع المتعددة (multisite)، من المهم أن يتم توفير أي معلومات خاصة بالموقع إلى omniauth في دالة setup (lambda)، بدلاً من تثبيتها عند وقت التعريف. راجع جميع المصادقات الأساسية كمثال على ذلك.

جميع المنطق لربط الحسابات الخارجية بحسابات Discourse يتم التعامل معه بواسطة Auth::ManagedAuthenticator. يعتمد هذا على أن مزوّد omniauth يعيد البيانات بالصيغة المعرّفة في توثيقهم. إذا كان هناك حاجة إلى أي معالجة لهذه البيانات، يمكن للمصادقات (Authenticators) تجاوز طريقة after_authenticate، ومعالجة auth_token كما هو مطلوب. على سبيل المثال، يزيل مصادق Twitter الأساسي جميع معلومات extra من الرمز (token):

تُخزَّن البيانات في جدول قاعدة البيانات user_associated_accounts. يتم أخذ provider_uid و info و credentials و extra مباشرة من البيانات التي يعيدها omniauth.

بمجرد تعريف فئة Authenticator، يجب تسجيلها. يجب أن يحدث هذا مبكرًا في دورة حياة التطبيق، ولا يمكن أن يحدث داخل طريقة after_initialize للإضافة (plugin). يمكن أن يحتوي التسجيل الأدنى ببساطة على مرجع إلى المصادق. في الإضافة (plugin)، يمكن إجراء التسجيل باستخدام دالة auth_provider. على سبيل المثال:

auth_provider authenticator: OpenIDConnectAuthenticator.new()

في النواة (core)، يتم التسجيل في discourse.rb. يمكن العثور على قائمة كاملة بخيارات AuthProvider الممكنة هنا. يمكن تعريف المحتوى النصي باستخدام هذه الخيارات، لكن من الأفضل توفير نصوص قابلة للتوطين في client.en.yml باتباع المفاتيح القياسية. على سبيل المثال:

ملاحظات إضافية حول ManagedAuthenticator بواسطة @fantasticfears

ManagedAuthenticator بالتفصيل

قد تحتاج إلى العمل على شيء خاص للمصادقة. وتريد معرفة المزيد عن ManagedAuthenticator. بشكل أساسي، لديه عدة عمليات وخيارات، ويتحكم في كيفية استخدام البيانات.

يدير Discourse معلومات المستخدمين من خلال متحكمين (controllers). يدير Users::OmniauthCallbacksController الحمولة (payload) بمجرد اكتمال مصادقة OAuth2. يتم استدعاء after_authenticate هنا. يُستخدم أيضًا can_connect_existing_user? هنا.
هناك بعض الطرق الخاصة (private methods) يمكنك قراءتها لفهم كيفية عمل حقول البيانات المختلفة.

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 على revoke_account الذي يستخدم can_revoke? و revoke. لكن لكي تعمل طريقة revoke عن بُعد، تحتاج إلى بناء تنفيذك الخاص.

UserAuthenticator هي فئة خدمة تساعد في مصادقة (التحقق من تأكيد البريد الإلكتروني أو مسار OAuth2) المستخدمين. يتم استدعاء after_create_account هنا.

يبقى المنطق الأساسي في after_authenticate مع فئة بيانات Auth::Result. نتبع هيكل البيانات هنا. سيتم تمرير 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)

المستخدم هو نسخة جديدة (fresh instance) سيتم ملؤها ببيانات الجلسة التي جهّزها مزوّد المصادقة. ثق بي، إنه مجرد سحر.


الترحيل إلى النظام الجديد

لتوفير تبديل سلس إلى النظام الجديد، يجب ترحيل البيانات من موقع التخزين القديم. لمزودي المصادقة الأساسيين، قد تكون هذه جداول مخصصة. للإضافات (plugins)، قد تكون plugin_store_rows أو oauth2_user_infos. البيانات الدنيا المطلوبة في صف user_associated_accounts هي provider_name و provider_uid و user_id. لمثال على الترحيل راجع:

بمجرد إصدار نظام ManagedAuthenticator إلى الفرع المستقر (stable branch) مع الإصدار 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 وعرض نموذج “إرسال رابط تسجيل دخول لي” كرد؟