مبادئ OAuth2 في Discourse

:discourse2: Summary Discourse OAuth2 Basic supports basic OAuth2 providers, assuming they have a JSON API endpoint where user details can be retrieved by token.
:open_book: Install Guide This plugin is bundled with Discourse core. There is no need to install the plugin separately.

Features

This plugin allows you to use a basic OAuth2 provider as authentication for Discourse. It should work with many providers, with the caveat that they must provide a JSON endpoint for retrieving information about the user you are logging in.

This is mainly useful for people who are using login providers that aren’t very popular. If you want to use Google, Facebook or Twitter, those are included out of the box and you don’t need this plugin. You can also look for other login providers in our Github Repo.

Configuration

Basic Configuration

  1. First, register your Discourse application with your OAuth2 provider. It will require a Redirect URI which will be:

    http://DISCOURSE_HOST/auth/oauth2_basic/callback

:information_source: Replace DISCOURSE_HOST with the appropriate value, and make sure you are using https if enabled. The OAuth2 provider should supply you with a client ID and secret, as well as a couple of URLs.

  1. Visit your AdminSettingsOAuth2 Login and fill in the basic configuration for the OAuth2 provider:
  • oauth2_enabled - check this off to enable the feature
  • oauth2_client_id - the client ID from your provider
  • oauth2_client_secret - the client secret from your provider
  • oauth2_authorize_url - your provider’s authorization URL
  • oauth2_token_url - your provider’s token URL.

:information_source: If you can’t figure out the values for the above settings, check the developer documentation from your provider or contact their customer support.

Configuring the JSON User Endpoint

Discourse is now capable of receiving an authorization token from your OAuth2 provider. Unfortunately, Discourse requires more information to be able to complete the authentication.

We require an API endpoint that can be contacted to retrieve information about the user based on the token.

For example, the OAuth2 provider SoundCloud provides such a URL. If you have an OAuth2 token for SoundCloud, you can make a GET request to https://api.soundcloud.com/me?oauth_token=A_VALID_TOKEN and will get back a JSON object containing information on the user.

To configure this on Discourse, we need to set the value of the oauth2_user_json_url setting. In this case, we’ll input the value of:

https://api.soundcloud.com/me?oauth_token=:token

The part with :token tells Discourse that it needs to replace that value with the authorization token it received when the authentication completed.

There is one last step to complete. We need to tell Discourse what attributes are available in the JSON it received. Here’s a sample response from SoundCloud:

{
  "id": 3207,
  "permalink": "jwagener",
  "username": "Johannes Wagener",
  "uri": "https://api.soundcloud.com/users/3207",
  "permalink_url": "http://soundcloud.com/jwagener",
  "avatar_url": "http://i1.sndcdn.com/avatars-000001552142-pbw8yd-large.jpg?142a848",
  "country": "Germany",
  "full_name": "Johannes Wagener",
  "city": "Berlin"
}

The oauth2_json_user_id_path, oauth2_json_username_path, oauth2_json_name_path and oauth2_json_email_path variables should be set to point to the appropriate attributes in the JSON.

The only mandatory attribute is id - we need that so when the user logs on in the future that we can pull up the correct account. The others are great if available – they will make the signup process faster for the user as they will be pre-populated in the form.

Here’s how I configured the JSON path settings:

  oauth2_json_user_id_path: 'id'
  oauth2_json_username_path: 'permalink'
  oauth2_json_name_path: 'full_name'

I used permalink because it seems more similar to what Discourse expects for a username than the username in their JSON. Notice I omitted the email path: SoundCloud do not provide an email so the user will have to provide and verify this when they sign up the first time on Discourse.

If the properties you want from your JSON object are nested, you can use periods. So for example if the API returned a different structure like this:

{
  "user": {
    "id": 1234,
    "email": {
      "address": "test@example.com"
    }
  }
}

You could use user.id for the oauth2_json_user_id_path and user.email.address for oauth2_json_email_path.

If the key itself includes periods, you will need to put double quotes around it, or escape the periods with a backslash. For example, given this JSON:

{
  "example.com/uid": "myuid"
}

You would specify the path as example\.com/uid or "example.com/uid"

:warning: If you set oauth2_json_email_path, the OAuth2 provider must confirm the user owns that email address. Failure to do this can result in account takeover in Discourse!

:discourse2: Hosted by us? This plugin is available on our Business and Enterprise plans. OAuth 2.0 & OpenID Connect Support | Discourse - Civilized Discussion

Last edited by @tobiaseigen 2025-07-16T21:39:12Z

Check documentPerform check on document:
28 إعجابًا

مرحباً،
نحن نحاول دمج Discourse مع تطبيقنا باستخدام OAuth2 Basic ولكننا نواجه الخطأ التالي في السجلات:
ملاحظة: نحن نستخدم NGROK لأننا نقوم بتصحيح الاتصال.

OAuth2 Debugging: request POST https://formshare.ngrok.io/oauth2/token

Headers: {"User-Agent"=>"Faraday v1.9.3", "Content-Type"=>"application/x-www-form-urlencoded", "Authorization"=>"Basic S2k2SFZtTVpuSTFHUExiRXVlWVJDNENiOkNvb1k0anlQemt3dWNRV21Sa2FWOVNnbHZLbjJFT3cxc3BIMmtMck9yY21vNDM4Tg=="}

Body: {"client_id"=>"Ki6HVmMZnI1GPLbEueYRC4Cb", "client_secret"=>"...some_secret_...", "grant_type"=>"authorization_code", "code"=>"5pPCrsp0pZ84373MNaHh2cuskfc8AlbfmdwMBFIVW4n4z9aX", :redirect_uri=>"https://community.formshare.org/auth/oauth2_basic/callback"}

------------------

OAuth2 Debugging: response status 200

From POST https://formshare.ngrok.io/oauth2/token

Headers: {"content-length"=>"108", "content-type"=>"text/html; charset=UTF-8", "date"=>"Thu, 01 Sep 2022 21:42:08 GMT", "ngrok-trace-id"=>"79cdc3f1c3eae5e37a30796aebbf9bd6", "server"=>"gunicorn"}

Body: {"token_type": "Bearer", "access_token": "p0FVuwjSXL1ZINEklMAVqUlpZxSll1SgnbpE8YWP4C", "expires_in": 864000}

-----------------------------------

(oauth2_basic) Authentication failure! invalid_credentials: OAuth2::Error, {"token_type": "Bearer", "access_token": "p0FVuwjSXL1ZINEklMAVqUlpZxSll1SgnbpE8YWP4C", "expires_in": 864000}

لقد تركنا المعلمات “oauth2 callback user id path” و “oauth2 callback user info paths” فارغة.

أي فكرة ستكون موضع تقدير.

هل يمكنني استخدام هذا للمصادقة مع خدمة XBL من Microsoft؟

أفترض أن المنطق سيكون مشابهًا لهذا؟

مرحباً بالجميع. أحاول تكوين هذه الإضافة باستخدام خادم Oauth2 الداخلي الخاص بنا مع تدفق رمز التفويض.

عندما ينقر المستخدم على “الاتصال باستخدام Oauth”، يعمل نقطة النهاية /authorize ويتم إرجاع رمز إلى الاستدعاء. ولكن بعد ذلك، يعرض Discourse خطأ عام 500 “عفوًا. واجه البرنامج الذي يدعم منتدى المناقشة هذا مشكلة غير متوقعة” ولا يتم الوصول إلى نقطة النهاية /token.

يقول سجل الأخطاء ذلك:
OAuth2::ConnectionError (FinalDestination: all resolved IPs were disallowed) lib/final_destination/ssrf_detector.rb:74:in lookup_and_filter_ips' lib/final_destination/http.rb:13:in connect’ lib/midd

المضيف discourse-app
process_id 653
application_version 702f27e6ee10ac257f5fee3f331d05f5fa5d7a45
HTTP_HOST *****
REQUEST_METHOD GET
HTTP_USER_AGENT Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.0.0 Safari/537.36
HTTP_ACCEPT text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,/;q=0.8,application/signed-exchange;v=b3;q=0.9
HTTP_REFERER *****
HTTP_X_FORWARDED_FOR *****
HTTP_X_REAL_IP *****
time 10:25 pm
params
code def50200babf84f7376f99fefa34369d876566b6bc0a341d8fba431999a72549ac06f6aad01df6fa43061707c525ba5d725ad
state 20139e0a134a5972566d4ddb6f7f9092a2cddb9e5216973a

على حد فهمي، هناك مشكلة في بعض عناوين IP؟ حاليًا، يتم استضافة خادم Oauth2 في بيئة التطوير الخاصة بي (localhost) وتم تكوين نقاط النهاية للتفويض والرمز وفقًا لذلك. هل هذه مشكلة؟

تم العثور على المشكلة:

  1. لسبب ما، لم يتم استدعاء نقطة النهاية /token أبدًا. بعد ملء الحد الأقصى من الخيارات في معلمات المسؤول المتعلقة بـ oauth، تم استدعاء نقطة النهاية دون إجابة
  2. نسيت أن خادم Discourse هو الذي سيستدعي نقطة النهاية /token وليس عميل الويب. لذلك، لم يتمكن الخادم من الوصول إلى خادم Oauth2 المحلي الخاص بي. وضع خادم Oauth2 الخاص بنا خلف نطاق حل المشكلة

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

هل هناك رد اتصال مخصص لتسجيل دخول المستخدم بدلاً من تسجيل الدخول؟ أو معلمة محددة لتعيينها للسماح بإنشاء الحساب؟

كان خادم OAuth الخاص بشركتي يُنشئ استجابة JSON لـ /profile مع خطأ إملائي صغير في أحد الحقول. كان كل شيء على ما يرام بعد إصلاح الخطأ الإملائي.
ولكن يجب أن أقول أن سجلات Discourse يمكن أن تكون مضللة للغاية! لم يكن هناك خطأ في رد الاتصال.

مرحباً بالفريق،

أواجه مشكلة في استخلاص المعرف (ID) الذي أحتاجه لطلب JSON الخاص بالمستخدم من استجابة التفويض. عند قراءة الوثائق، يبدو أن معرف الحساب يتم إرساله في مصفوفة متداخلة:

HTTP/1.1 200 OK
Content-Type: application/json;charset=UTF-8
Cache-Control: no-store
Pragma: no-cache

{
   "access_token":"2YotnFZFEjr1zCsicMWpAA",
   "token_type":"Bearer",
   "expires_in":1800,
   "refresh_token":"tGzv3JOkF0XG5Qx2TlKWIA",
   "permissions":[
      {
        "accountId":123,
        "availableScopes":["contacts_view", "contacts_me",
"contacts_edit", "finances_view", "events_view"]
      }
   ]
}

لقد حاولت تعيين مسار معرف المستخدم في رد OAuth2 إلى permissions[0].accountId ولكن قيمة معرف المستخدم الخاصة بي تكون دائماً فارغة. للأسف، تتطلب المكالمات لاستخلاص JSON الخاص بالمستخدم معرف الحساب هذا في عنوان URL الخاص بـ JSON.

تمكنت من جعل هذا يعمل عن طريق تمرير permissions.first.accountId، ووجدت أنه عند تمرير permissions إلى خاصية اختبار، تم تحليل المصفوفة بالفعل كمصفوفة Ruby. للأسف، تبدو الحقول رافضة لبناء جمل Ruby لاستدعاء عناصر المصفوفة وأي محاولة لاستخدام بناء جمل Javascript ستؤدي إلى خطأ TypeError String to Integer. لحسن الحظ، كان لدى Ruby بناء الجملة أعلاه، هل هذه هي الطريقة المقصودة؟

لقد نجحت للتو في تشغيل هذا باستخدام Authentik OAuth2، ومع ذلك كانت هناك بعض المشاكل مع إعداد oauth2 user json url. لقد استخدمت نقطة نهاية user_info الخاصة بـ Authentik لذلك (/application/o/userinfo/)، ومع ذلك لم أكن أعرف كيفية تعيين الحقول. بالنسبة لأي شخص يبحث عن كيفية إعداد Discourse باستخدام OAuth2 الخاص بـ Authentik، إليك الملخص:

  • مسار معرف المستخدم: preferred_username
  • مسار اسم المستخدم: preferred_username
  • مسار الاسم: name
  • مسار البريد الإلكتروني: email
  • مسار البريد الإلكتروني الذي تم التحقق منه: email_verified
  • الصورة الرمزية: فارغة.

كانت لدي المشاكل التالية:

  1. في البداية، نسيت الشرطة المائلة اللاحقة في عنوان URL الخاص بـ JSON https://DOMAIN/application/o/userinfo/. أدى هذا إلى أن طلب معلومات المستخدم (رابط دائم للمصدر) أعاد رمز HTTP 301، مما تسبب في فشل تسجيل الدخول. لا أعرف ما إذا كان يجب أن تكون الشرطة المائلة اللاحقة موجودة حسب المواصفات، ولكن ربما سيكون من الجيد التعامل مع 301 بشكل صحيح.
  2. كان تصحيح هذا صعبًا. كان إعداد oauth2 debug auth بمثابة المنقذ ولكن… يقوم Logster باقتطاع سجل التصحيح قبل تفريغ بيانات الاستجابة ذات المعنى فعليًا. اضطررت إلى تعديل سطر السجل يدويًا في الحاوية إلى
    log("user_json_response: #{user_json_response.status} #{user_json_response.headers} #{user_json_response.body}")
    
    ربما يمكن تحديث سطر السجل هذا؟ أعتقد أنه يمكن أن يساعد الآخرين في معرفة مسار سمات JSON.
4 إعجابات

لقد قمت للتو بإعداد Auth0 باستخدام المكون الإضافي ووجدت أن الصور الرمزية لم يتم التقاطها.

هذه هي الإعدادات ذات الصلة:

  DISCOURSE_OAUTH2_ENABLED: true
  DISCOURSE_OAUTH2_CLIENT_ID: '${DISCOURSE_OAUTH2_CLIENT_ID}'
  DISCOURSE_OAUTH2_CLIENT_SECRET: '${DISCOURSE_OAUTH2_CLIENT_SECRET}'
  DISCOURSE_OAUTH2_AUTHORIZE_URL: '${DISCOURSE_OAUTH2_ISSUER}/authorize?connection=xxx&login_options=yyy'
  DISCOURSE_OAUTH2_TOKEN_URL: '${DISCOURSE_OAUTH2_ISSUER}/oauth/token'
  DISCOURSE_OAUTH2_USER_JSON_URL: '${DISCOURSE_OAUTH2_ISSUER}/userinfo'
  DISCOURSE_OAUTH2_SCOPE: 'email openid profile'
  DISCOURSE_OAUTH2_JSON_USER_ID_PATH: 'sub'
  DISCOURSE_OAUTH2_JSON_USERNAME_PATH: 'nickname'
  DISCOURSE_OAUTH2_JSON_NAME_PATH: 'name'
  DISCOURSE_OAUTH2_JSON_EMAIL_PATH: 'email'
  DISCOURSE_OAUTH2_JSON_EMAIL_VERIFIED_PATH: 'email_verified'
  DISCOURSE_OAUTH2_JSON_AVATAR_PATH: 'picture'
  DISCOURSE_OAUTH2_EMAIL_VERIFIED: true
  DISCOURSE_OAUTH2_OVERRIDES_EMAIL: true
  DISCOURSE_OAUTH2_ALLOW_ASSOCIATION_CHANGE: false

في سجل التصحيح، يمكنني رؤية أن عنصر picture تم تعيينه في استجابة JSON، ولكن صورة المستخدم الرمزية لا تتغير، لا للمستخدمين الجدد ولا للمستخدمين الحاليين.

ما الذي فاتني؟

ما هي أفضل طريقة لاستبدال الأيقونة الموجودة على زر تسجيل الدخول بأيقونة أخرى أو صورة؟

.btn-social.oauth2_basic:before {
    content: url('https://www.contoso.com/path/to/image');
}

.btn-social.oauth2_basic > svg {
    display: none;
}

يبدو هذا كافيًا ولكنه غير احترافي بعض الشيء.

إعجابَين (2)

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

يمكنك استخدام إعدادات auth overrides email و auth overrides username و auth overrides name لتطبيق هذه الأشياء على عمليات تسجيل الدخول المستقبلية. أخشى أنه ليس لدينا حاليًا إعداد مماثل للصورة الرمزية، ولكن سيكون pr-welcome

إعجابَين (2)

شكراً لك! لقد وجدتها لاحقًا. لقد قمت بعمل نسخة من المستودع وأضفت إصداراتي الخاصة لجعله يعمل مع Roblox، والتي تضمنت التجاوز للصور الرمزية. أعتقد أن هذا يستخدم فقط تجاوز الصور الرمزية لـ DiscourseConnect حتى لا يتمكن الأشخاص من تغييرها.

شيء واحد أتمنى لو أن Roblox لا يوفر بريدًا إلكترونيًا على OAuth، لذا أحتاج للأسف إلى جعلهم يسجلون ببريد إلكتروني. لكن هذه ليست مشكلة بالنسبة لكم يا رفاق.

تم تقسيم منشور إلى موضوع جديد: تسجيل الدخول إلى تويتر لا يعمل على ميتا

هل يعرف أحد ما إذا كان هذا لا يزال يعمل؟

نعم. أنا واثق من أن هذه الإضافة تعمل. :+1:

إعجاب واحد (1)

مرحباً، لقد تمكنت من دمج هذه الإضافة في موقع discourse الخاص بي discuss.frontendlead.com، أنا أستخدم Teachable OAuth https://docs.teachable.com/docs/oauth-quickstart-guide

ومع ذلك، أريد فقط السماح للأشخاص بالتسجيل بنجاح إذا وفقط إذا كان لديهم حساب مدفوع حالي على Teachable. أتخيل أنني بحاجة إلى إضافة وظائف مخصصة في الإضافة للتعامل مع هذا؟ أتساءل، هل يمكنكم أو حتى أنا، إضافة حقل آخر في الإعدادات يسمى “الكود المخصص بعد OAuth”، والذي يسمح للمطورين بتنفيذ إجراءات محددة بعد التسجيل؟ أو إذا كانت هناك اقتراحات أفضل، فيرجى إخباري.

تعديل: لقد قمت بعمل نسخة من المستودع وتمكنت من تشغيله هنا:

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

إعجابَين (2)

هذا رائع!

هناك موقف مشابه مع تسجيل OAuth2 مع إضافة Discourse Patreon. عندما يتم تمكين “تسجيل الدخول باستخدام Patreon”، فإنه يسمح لأي شخص لديه حساب Patreon بالتسجيل في موقع Discourse. ما يريده أصحاب المواقع بشكل عام هو السماح فقط لمؤيديهم بتسجيل حسابات Discourse. أتساءل عما إذا كانت التفاصيل تُرجع من Patreon للسماح بإضافة منطق مماثل إلى مصادقة Patreon؟

إعجاب واحد (1)

لدي نفس الخطأ تمامًا مثل @qlands أعلاه.

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

رسالة الخطأ هي:

(oauth2_basic) فشل المصادقة! invalid_credentials: OAuth2::Error, {
  "access_token":"fa79b6fe0763862f5a8fd8",
  "token_type":"Bearer",
  "expires_in":3600,
  "scope":"profile"
}

هل ترى أي شيء خاطئ في الرد أعلاه؟
لماذا يقوم المكون الإضافي بإنشاء خطأ invalid_credentials بينما رد خادم OAuth2 برمز 200 مع رمز مميز؟