بروتوكول المصادقة بخصوص تكامل التطبيق

Hey all! My team and I have been looking into possible ways to integrate our [future] Discourse instance into our mobile application - Noom. Our iOS app is written in Objective-C with some Swift, and our Android application is written in Java with some Kotlin. We’ve been debating between full integration via API or web view as our goal is an SSO flow where our users can seamlessly transition from an in-app experience to our Discourse instance.

I’m currently waiting on our QA team to get back to me on which authentication protocols we currently use, but I was curious as to anyone else’s experience integrating Discourse into a mobile application with or without SSO, and what methods (if any) you found most useful throughout the process. I’m aware of Discourse’s compatibility with OAuth/2, although not aware of other potential protocols.

My apologies if this is in the wrong category!

There is a new way of authentication. However the documentation is yet to be released.

I’m going to update the documentation about delegated authentication soon, but I can give you some pointers right here.

First you need to open a browser session to discourse.site/user-api-key/new with the following parameters:

scopes: 'notifications,session_info,one_time_password',
client_id: YOUR_APP_CLIENT_ID,
nonce: GENERATED_NONCE,
auth_redirect: YOUR_APP_URL_SCHEME,
application_name: YOUR_APP_NAME,
push_url: PUSH_URL (if you are going to send PNs from Discourse to your app),
public_key: PUBLIC_KEY (generated in your app)

You can have a look at the implementation of our DiscourseMobile app for details on the above but the main idea is that your app will launch a browser screen to the URL above, asking the user to authenticate to the Discourse site and authorize your app access to it. Once user authorizes access, Discourse will redirect to YOUR_APP_URL_SCHEME?payload= with an encrypted payload. You’ll need to set up your app to decrypt the payload and store the authToken. In iOS, you should use ASWebAuthenticationSession | Apple Developer Documentation (I don’t know if there is an Android equivalent).

Your authToken can make API requests limited by the scopes requested initially, for a full list of scopes, please look under allow user api key scopes in site settings.

The one_time_password scope allows the authToken to make a request for a one-time-password. The endpoint for this is /user-api-key/otp with the parameters auth_redirect, application_name and public_key.

I will write a proper documentation shortly, but this should help you get started.

@hosna + @pmusaraj Thank you both! I will pass this information along to our QA director.

@pmusaraj مرحبًا، هل يتوفر بالفعل أي توثيق رسمي متاح للجمهور؟ أحب أن أغوص في التفاصيل.

سأقوم بمعالجة هذا الأمر بالضبط خلال الأيام القليلة القادمة، وسأحدثكم عن نجاحي أو فشلي. إذا كان لدى أي شخص أدلة أو إرشادات، فسأرحب بها :slight_smile:

أعتقد أنك تبحث عن User API keys specification وتحديداً القسم “تدفق إنشاء مفتاح API”.

مرحبًا بنار، شكرًا على التوجيه. أنا بعيد كل البعد عن أن أكون خبيرًا في هذا المجال، فلم أعمل كثيرًا مع التشفير أو واجهات برمجة التطبيقات مثل هذه من قبل. وأتساءل عما إذا كان بإمكانك تقديم بعض المساعدة حول كيفية ترميز بعض هذه المعاملات؟ أنا ألقّي نظرة على تطبيق DiscourseMobile الخاص بك، لكنني لم أستخدم JavaScript من قبل، لذا أواجه بعض الصعوبات.

nonce و client_id - هل هما مجرد سلاسل عشوائية من 16 و 32 بايت على التوالي، مشفرة بصيغة سداسية عشرية (hex)؟
public_key - هل لديك أي توجيهات حول كيفية إنشائه؟ أحاول استخدام هذا المورد مما سيترك لي كائنًا من نوع SecKeyRef. هل عليّ تحويله إلى NSData ثم ترميزه بصيغة UTF8؟

هل يتسنى لك أن يكون لديك أي مقتطفات برمجية بلغة Swift أو Objective-C؟ سأتمكن من حل هذا الأمر بنفسي، لكن كل هذا جديد بالنسبة لي، لذا فإن أي مساعدة صغيرة تُقدّر جدًا.

nonce هو مفتاح عشوائي، انظر generateNonce هنا: DiscourseMobile/js/site_manager.js at main · discourse/DiscourseMobile · GitHub

client_id هو deviceToken للتطبيق، ويتم تمريره إلى التطبيق عبر GitHub - react-native-push-notification/ios: React Native Push Notification API for iOS. · GitHub
public_key يتم إنشاؤه بواسطة https://github.com/SamSaffron/react-native-key-pair، هذه المكتبة صغيرة جدًا، والأجزاء ذات الصلة موجودة في Objective C.

مرحبًا Penar، شكرًا على الروابط، فهي مفيدة جدًا.
أحرز تقدمًا، لكنني أعتقد أنني أقوم بتنسيق المفتاح العام بشكل غير صحيح، كيف يبدو التنسيق في هذا المثال؟

https://myserver.com/user-api-key/new?client_id=11223344556677889900AABBCCDDEEFF11223344556677889900AABBCCDDEEFF&nonce=11223344556677889900AABBCCDDEEFF&application_name=AppName&public_key=-----BEGIN%20PUBLIC%20KEY-----MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtoNwaYpbUcX1vqxPkxRA%0D1EyfLPxkKL6zgx4Fkk9zkYbTPTWQFuqy+O1EJtVnsW+Tx8iarFLI+ypghcx22nI0%0DN/hDFYsaT/xri+LLDc790uf2UtyolgBJfkyjJDxIXXy0pdpi6f2dgKMN2holDkxf%0DTUUnZad+wE8gT8IciX1XjU97MOngSV+IDeKPLomTuTiI1Z0hJe4WDww5+53ci4o4%0DoE5A79H/Fz/QY8vDLTcBNrQ6OdJYsRqhE5M+1sNmxpqDKT+9NcAPY7yphxm1iLuV%0Dm7c6K/xrbZExGDQd1qHvggT2ldMJIsmQEnleMdfuaYLh8+EYt8LNQ8X86V0Jj+9C%0DmQIDAQAB-----END%20PUBLIC%20KEY-----&auth_redirect=appname://auth&scopes=notifications,session_info,one_time_password

في سجل وحدة التحكم، أرى:
OpenSSL::PKey::RSAError (Neither PUB key nor PRIV key: nested asn1 error) /var/www/discourse/app/controllers/user_api_keys_controller.rb:189:in `initialize’

شكرًا لك.

لقد نشرت للتو سكريبتًا يقوم بهذا بالضبط @xceph.

راجع توليد مفاتيح API للمستخدمين لأغراض الاختبار.

شكرًا لك! لقد حققت تقدمًا للتو، كانت مشكلتي في ترميز المعاملات، حيث كنت أستخدم مجموعة الأحرف URLQueryAllowedCharacterSet الخاصة بـ iOS، والتي لا تقوم بترميز “/” أو “+”. بمجرد إنشاء نص اختباري بلغة Ruby لعزل السطر الذي تسبب في انهيار التطبيق، تمكنت من العمل بشكل عكسي لحل المشكلة. شكرًا لك!

لا أستطيع جعل إعادة التوجيه تعمل بشكل صحيح باستخدام ASWebAuthenticationSession كما اقترح Penar. يبدو أن المكالمات تعمل كما هو متوقع؛ أحصل على نافذة حوار “التفويض”، وعند النقر عليها، تُعرض لي شاشة بيضاء فارغة، وهو ما أتوقعه كمحاولة لإعادة التوجيه، لكنها لا تغلق ولا ترسل المعلومات إلى استدعائي.

لقد اختبرت أن عنوان URL المخصص الخاص بي يعمل بشكل جيد من Safari، وعلاوة على ذلك، إذا قمت بإزالة auth_redirect، فإنني أواجه موقع “لقد قمنا بتوليد مفتاح واجهة برمجة تطبيقات مستخدم جديد للاستخدام..”، لذا يبدو أن كل شيء ما عدا إعادة التوجيه يعمل بشكل صحيح.

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

تعديل: بالطبع، وجدت هذا فورًا بعد طرحي للسؤال. في الإعدادات، يجب بالفعل تعيين “إعادة توجيه مصادقة واجهة برمجة تطبيقات المستخدم المسموح بها” إلى مخطط العنوان المخصص الخاص بك.

الشاشة البيضاء الفارغة هي على الأرجح علامة على أن الخادم يواجه خطأً. عند تجربتك مرة أخرى، تحقق من مجلد /logs الخاص بموقع Discourse، فمن المرجح أن تجد شيئًا هناك. ربما تواجه مشكلة حيث لا يمكن تسجيل نفس معرف العميل (client ID) عدة مرات. قد تحتاج إلى مسح معرف العميل هذا يدويًا على مثيل Discourse، وهو أمر يمكن تنفيذه حاليًا فقط عبر وحدة تحكم Rails.

نعم، كان الأمر متعلقًا بإعداد “Allowed user api auth redirects” في لوحة التحكم، وقد تطلب تعيينه ليعمل بشكل صحيح.

حاليًا، يمكنني المصادقة، لكنني أواجه صعوبة في فك تشفير الحمولة (payload) على iOS. يمكنني أخذ الحمولة المستلمة ومفتاحي الخاص وفك تشفيرهما في Ruby أو NodeJS لإثبات أن الأمر يعمل، لكن جعل iOS يقوم بفك التشفير مباشرةً أكثر تعقيدًا مما توقعت.

إذا نظرت إلى كود المصدر الخاص بتطبيقنا، ستجد أمثلة على كيفية إجراء فك التشفير؛ لقد تطلب الأمر كمية معقدة نسبيًا من الكود لجعل كل شيء يعمل بشكل صحيح.

شكرًا لك، لقد نجحت في جعل كل شيء يعمل بشكل صحيح. سأشارك بعض الأكواد لاحقًا لمساعدة أي شخص آخر يبحث عن تنفيذ هذا بشكل أصلي على iOS. أقدر إرشاداتك.

بمجرد إنشاء رمز واجهة برمجة التطبيقات بهذه الطريقة، هل يمكن استخدامه للمصادقة عند تسجيل الدخول في عرض ويب؟ كنت أفترض أن هذا ما تفعله التطبيق، لكنني لا أزال أعمل على هذا الجزء الآن. أحاول توفير كل من الوصول إلى واجهة برمجة التطبيقات وإمكانية التصفح عبر عميل الويب العادي لـ Reface من خلال تسجيل دخول واحد.