OAUTH2 basic — кошмар :-(

Всем привет,

Уже два дня пытаюсь настроить OAuth2, но столкнулся с проблемами.

Я всё настроил согласно документации, но постоянно получаю следующую ошибку:

(oauth2_basic) Authentication failure! invalid_scope: OmniAuth::Strategies::OAuth2::CallbackError, invalid_scope | Unknown/invalid scope(s)
Started GET "/auth/failure?message=invalid_scope&origin=<censored>%2Flatest&strategy=oauth2_basic" for 10.153.107.106 at 2024-11-27 05:30:33 +0000
Processing by Users::OmniauthCallbacksController#failure as HTML
  Parameters: {"message"=>"invalid_scope", "origin"=>"<censored>/latest", "strategy"=>"oauth2_basic"}

Моя конфигурация на доске (относительно scopes) выглядит так:

IdP настроен следующим образом:

У кого-нибудь есть подсказка, как можно дальше отладить эту проблему?

Может, есть какие-то curl-запросы, с помощью которых можно протестировать или лучше вывести отладочную информацию (опцию подробной отладки я уже включил)?

Уже схожу с ума от этого :frowning:

Спасибо и привет!

WS

ОБНОВЛЕНИЕ:

Только что вернул настройки обратно, оставив только “scope”

но это тоже не сработало :frowning:

Заметил, что у нашего IdP в интерфейсе указан неверный scope…

В интерфейсе указано openId, а правильный — openid :slight_smile:

Сделали ещё шаг вперёд, но теперь получаю тайм-аут:

image

После этого вызова:


OAuth2 Debugging: request POST https://<myAuthProvider/auth/oauth2/realms/root/realms/<realm>/access_token

Headers:
--- !ruby/hash-with-ivars:Faraday::Utils::Headers
ivars:
  :@names:
    user-agent: User-Agent
    content-type: Content-Type
elements:
  User-Agent: Faraday v2.12.1
  Content-Type: application/x-www-form-urlencoded


Body:
---
client_id: <client-id>
client_secret: <client-secret>
grant_type: authorization_code
code: <code>
redirect_uri: https://<myDiscourse>/auth/oauth2_basic/callback

что заканчивается ошибкой:

Но так как вызов authorize для получения кода прошёл успешно, проблем с сетью быть не должно, верно?

Когда я делаю вызов с помощью curl

curl --request POST \
  --url https://<IdP адрес>/auth/oauth2/realms/root/realms/<realm>/access_token \
  --header 'content-type: application/x-www-form-urlencoded' \
  --data client_id=<clientId> \
  --data client_secret=<secret> \
  --data grant_type=authorization_code \
  --data code=<code> \
  --data redirect_uri=https://<discourse>/auth/oauth2_basic/callback

изнутри docker-контейнера Discourse, я получаю следующее:

{
  "access_token": "<токен>",
  "refresh_token": "<refresh_token>",
  "scope": "openid profile email",
  "id_token": "<censored>",
  "token_type": "Bearer",
  "expires_in": 7199
}

Может ли это быть проблемой из-за этого?

image

Этих полей нет в ответе :frowning:

Никто? :frowning:

Я очень заинтересован в этой теме, так как изучаю, сколько усилий потребуется для создания моей первой страницы на Discourse для моего района, а затем постепенно для школ моих детей. У меня слишком много уведомлений от разных платформ.

Снижение трения при работе со страницей критически важно, поэтому я хотел бы предложить свою помощь. Я новичок в Ruby, поэтому могу предложить только найденные мной ресурсы и идеи по ним.

Возможно, вы уже смотрели это, но вам стоит проверить репозиторий на GitHub для discourse-openid-connect:

В репозитории 37 участников, и я предполагаю, что кто-то из них сможет помочь вам ответить на ваш вопрос.

Надеюсь, это поможет, так как это отличный вопрос.

Привет @thecatfix,

выглядит многообещающе. Я сразу же попробую и как можно скорее дам обратную связь.
К сожалению, сегодня у меня нет времени. Возможно, я смогу поработать над этим в выходные.

Большое спасибо и отличных выходных!

Привет,

не смог удержаться и протестировал это сегодня, но, похоже, ребята из нашего отдела EAM всё испортили. Это не работает, и я получаю ошибку «invalid client - authentication failed», хотя на 1000 % уверен, что учётные данные работают (проверил через curl).
Так что я снова вернулся к началу.

Интересно то, что я постоянно получаю ошибку таймаута (timeoutError) при запросе к конечной точке авторизации, и она возникает довольно быстро.


Faraday::TimeoutError (Net::ReadTimeout with #<TCPSocket:(closed)>)

Нашёл это и интересно, возможно ли что-то подобное в Discourse?

Я вообще не специалист по Ruby, поэтому мне понадобится помощь.

Я хочу отлаживать каждый отдельный запрос, обрабатываемый на стороне Discourse. Возможно ли это?
Также думал установить локальный прокси для перехвата вызовов, но прежде чем углубляться в это, хотел спросить, есть ли более простые способы :slight_smile:

Вы хотите просматривать сырой HTTP-трафик или иметь возможность переходить непосредственно в код?

Сырой трафик …

запросы, отправляемые с заголовками и полезной нагрузкой …

это возможно каким-то образом?

Самый простой способ сделать это — изнутри контейнера; вы можете перехватывать и выводить запросы между nginx и Discourse, войдя в контейнер и выполнив:

apt-get update && apt-get -y install scapy
scapy

# в приглашении scapy вставьте:
class Callback:
  def __init__(self):
    self.last = None
  def prn(self, p):
    if p != self.last: # pcaps на lo захватывают дважды
      self.last = p
      p.hide_defaults()
      print(repr(p)) # эта строка выводит пакет, оставьте её или удалите
      if scapy.packet.Raw in p.layers():
        try:
          print(p.load.decode())
        except:
          print(p.load)

sniff(filter="port 3000", iface="lo", prn=Callback().prn)

Это огромный шаг, спасибо… Я искал именно такой инструмент!

Хм… но

кажется, что Discourse получает обратный вызов, а затем…
По моему пониманию, он должен затем связаться с userinfoendpoint и получить информацию о пользователе с помощью полученного кода, но я получаю HTTP 500


GET /auth/oauth2_basic/callback?code=_B1HRB1e6kZKc8nuGLkzGC8&iss=https%3A%2F%2F<myAuthDomain>%3A443%2Fauth%2Foauth2%2Frealms%2Froot%2Frealms%2Fintranetrealm&state=544801ae7e8262ea1667ea7531487f28e83aae232d5182b4&client_id=eadaa55a-1697-494a-8fg5-bb1137c68caa HTTP/1.0
Host: <discourseHost>
X-Request-Start: t=1732956951.485
X-Real-IP: 10.111.101.84
X-Forwarded-For: 10.131.101.84
X-Forwarded-Proto: https
Connection: close
cache-control: max-age=0
upgrade-insecure-requests: 1
user-agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36
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.7
sec-fetch-site: same-site
sec-fetch-mode: navigate
sec-fetch-user: ?1
sec-fetch-dest: document
sec-ch-ua: "Google Chrome";v="131", "Chromium";v="131", "Not_A Brand";v="24"
sec-ch-ua-mobile: ?0
sec-ch-ua-platform: "Windows"
referer: https://discourseHost/
accept-encoding: gzip, deflate, br, zstd
accept-language: de-DE,de;q=0.9
priority: u=0, i
cookie: lbwdn=01; win=teUNUPSc8oibB......

e[0m<e[0me[31me[1mEthere[0m  e[34mdste[0me[0m=e[0me[35m00:00:00:00:00:00e[0m e[34msrce[0me[0m=e[0me[35m00:00:00:00:00:00e[0m e[34mtypee[0me[0m=e[0me[35mIPv4e[0m e[0m|e[0me[0m<e[0me[31me[1mIPe[0m  e[34mihle[0me[0m=e[0me[35m5e[0m e[34mlene[0me[0m=e[0me[35m52e[0m e[34mide[0me[0m=e[0me[35m7742e[0m e[34mflagse[0me[0m=e[0me[35mDFe[0m e[34mfrage[0me[0m=e[0me[35m0e[0m e[34mttle[0me[0m=e[0me[35m127e[0m e[34mprotoe[0me[0m=e[0me[35mtcpe[0m e[34mchksume[0me[0m=e[0me[35m0xdf83e[0m e[34me[4me[1msrce[0me[0m=e[0me[35me[4me[1m127.0.0.1e[0m e[34me[4me[1mdste[0me[0m=e[0me[35me[4me[1m127.0.0.1e[0m e[0m|e[0me[0m<e[0me[31me[1mTCPe[0m  e[34msporte[0me[0m=e[0me[35m3000e[0m e[34mdporte[0me[0m=e[0me[35m33574e[0m e[34mseqe[0me[0m=e[0me[35m271589520e[0m e[34macke[0me[0m=e[0me[35m3955524768e[0m e[34mdataofse[0me[0m=e[0me[35m8e[0m e[34mflagse[0me[0m=e[0me[35mAe[0m e[34mwindowe[0me[0m=e[0me[35m499e[0m e[34mchksume[0me[0m=e[0me[35m0xfe28e[0m e[34moptionse[0me[0m=e[0me[35m[('NOP', None), ('NOP', None), ('Timestamp', (3962821542, 3962821542))]e[0m e[0m|e[0me[0m>e[0me[0m>e[0me[0m>e[0m
e[0m<e[0me[31me[1mEthere[0m  e[34mdste[0me[0m=e[0me[35m00:00:00:00:00:00e[0m e[34msrce[0me[0m=e[0me[35m00:00:00:00:00:00e[0m e[34mtypee[0me[0m=e[0me[35mIPv4e[0m e[0m|e[0me[0m<e[0me[31me[1mIPe[0m  e[34mihle[0me[0m=e[0me[35m5e[0m e[34mlene[0me[0m=e[0me[35m281e[0m e[34mide[0me[0m=e[0me[35m55898e[0m e[34mflagse[0me[0m=e[0me[35mDFe[0m e[34mfrage[0me[0m=e[0me[35m0e[0m e[34mttle[0me[0m=e[0me[35m127e[0m e[34mprotoe[0me[0m=e[0me[35mtcpe[0m e[34mchksume[0me[0m=e[0me[35m0x2282e[0m e[34me[4me[1msrce[0me[0m=e[0me[35me[4me[1m127.0.0.1e[0m e[34me[4me[1mdste[0me[0m=e[0me[35me[4me[1m127.0.0.1e[0m e[0m|e[0me[0m<e[0me[31me[1mTCPe[0m  e[34msporte[0me[0m=e[0me[35m3000e[0m e[34mdporte[0me[0m=e[0me[35m33530e[0m e[34mseqe[0me[0m=e[0me[35m491394e[0m e[34macke[0me[0m=e[0me[35m1852886175e[0m e[34mdataofse[0me[0m=e[0me[35m8e[0m e[34mflagse[0me[0m=e[0me[35mPAe[0m e[34mwindowe[0me[0m=e[0me[35m507e[0m e[34mchksume[0me[0m=e[0me[35m0xff0de[0m e[34moptionse[0me[0m=e[0me[35m[('NOP', None), ('NOP', None), ('Timestamp', (3962823893, 3962816870))]e[0m e[0m|e[0me[0m<e[0me[31me[1mRawe[0m  e[34mloade[0me[0m=e[0me[35m'HTTP/1.1 500 Internal Server Error\r\nDate: Sat, 30 Nov 2024 08:55:53 GMT\r\nConnection: close\r\nContent-Type: text/html; charset=utf-8\r\nContent-Length: 658\r\nX-Request-Id: 7f4bafa0-7590-4b9b-8b8d-70a4fa1ae6c5\r\nX-Runtime: 10.078459\r\n\r\n'e[0m e[0m|e[0me[0m>e[0me[0m>e[0me[0m>e[0me[0m>e[0m
HTTP/1.1 500 Internal Server Error
Date: Sat, 30 Nov 2024 08:55:53 GMT
Connection: close
Content-Type: text/html; charset=utf-8
Content-Length: 658
X-Request-Id: 7f4bafa0-7590-4b9b-8b8d-70a4fa1ae6c5
X-Runtime: 10.078459

.....

e[0m<e[0me[31me[1mEthere[0m  e[34mdste[0me[0m=e[0me[35m00:00:00:00:00:00e[0m e[34msrce[0me[0m=e[0me[35m00:00:00:00:00:00e[0m e[34mtypee[0me[0m=e[0me[35mIPv4e[0m e[0m|e[0me[0m<e[0me[31me[1mIPe[0m  e[34mihle[0me[0m=e[0me[35m5e[0m e[34mlene[0me[0m=e[0me[35m52e[0m e[34mide[0me[0m=e[0me[35m15439e[0m e[34mflagse[0me[0m=e[0me[35mDFe[0m e[34mfrage[0me[0m=e[0me[35m0e[0m e[34mttle[0me[0m=e[0me[35m127e[0m e[34mprotoe[0me[0m=e[0me[35mtcpe[0m e[34mchksume[0me[0m=e[0me[35m0xc172e[0m e[34me[4me[1msrce[0me[0m=e[0me[35me[4me[1m127.0.0.1e[0m e[34me[4me[1mdste[0me[0m=e[0me[35me[4me[1m127.0.0.1e[0m e[0m|e[0me[0m<e[0me[31me[1mTCPe[0m  e[34msporte[0me[0m=e[0me[35m33574e[0m e[34mdporte[0me[0m=e[0me[35m3000e[0m e[34mseqe[0me[0m=e[0me[35m3955524768e[0m e[34macke[0me[0m=e[0me[35m271589749e[0m e[34mdataofse[0me[0m=e[0me[35m8e[0m e[34mflagse[0me[0m=e[0me[35mAe[0m e[34mwindowe[0me[0m=e[0me[35m511e[0m e[34mchksume[0me[0m=e[0me[35m0xfe28e[0m e[34moptionse[0me[0m=e[0me[35m[('NOP', None), ('NOP', None), ('Timestamp', (3962831618, 3962831618))]e[0m e[0m|e[0me[0m>e[0me[0m>e[0me[0m>e[0m
e[0m<e[0me[31me[1mEthere[0m  e[34mdste[0me[0m=e[0me[35m00:00:00:00:00:00e[0m e[34msrce[0me[0m=e[0me[35m00:00:00:00:00:00e[0m e[34mtypee[0me[0m=e[0me[35mIPv4e[0m e[0m|e[0me[0m<e[0me[31me[1mIPe[0m  e[34mihle[0me[0m=e[0me[35m5e[0m e[34mlene[0me[0m=e[0me[35m710e[0m e[34mide[0me[0m=e[0me[35m7744e[0m e[34mflagse[0me[0m=e[0me[35mDFe[0m e[34mfrage[0me[0m=e[0me[35m0e[0m e[34mttle[0me[0m=e[0me[35m127e[0m e[34mprotoe[0me[0m=e[0me[35mtcpe[0m e[34mchksume[0me[0m=e[0me[35m0xdcefe[0m e[34me[4me[1msrce[0me[0m=e[0me[35me[4me[1m127.0.0.1e[0m e[34me[4me[1mdste[0me[0m=e[0me[35me[4me[1m127.0.0.1e[0m e[0m|e[0me[0m<e[0me[31me[1mTCPe[0m  e[34msporte[0me[0m=e[0me[35m3000e[0m e[34mdporte[0me[0m=e[0me[35m33574e[0m e[34mseqe[0me[0m=e[0me[35m271589749e[0m e[34macke[0me[0m=e[0me[35m3955524768e[0m e[34mdataofse[0me[0m=e[0me[35m8e[0m e[34mflagse[0me[0m=e[0me[35mPAe[0m e[34mwindowe[0me[0m=e[0me[35m512e[0m e[34mchksume[0me[0m=e[0me[35m0xbbe[0m e[34moptionse[0me[0m=e[0me[35m[('NOP', None), ('NOP', None), ('Timestamp', (3962831618, 3962831618))]e[0m e[0m|e[0me[0m<e[0me[31me[1mRawe[0m  e[34mloade[0me[0m=e[0me[35m'<!DOCTYPE html>\n<html>\n<head>\n  <title>Oops - Error 500</title>\n  <meta http-equiv="Content-Type" content="text/html; charset=utf-8">\n</head>\n<body>\n    <h1>Oops</h1>\n    <p>The software powering this discussion forum encountered an unexpected problem. We apologize for the inconvenience.</p>\n    <p>Detailed information about the error was logged, and an automatic notification generated. We\'ll take a look at it.</p>\n    <p>No further action is necessary. However, if the error condition persists, you can provide additional detail, including steps to reproduce the error, by posting a discussion topic in the site\'s feedback category.</p>\n</body>\n</html>\n'e[0m e[0m|e[0me[0m>e[0me[0m>e[0me[0m>e[0me[0m>e[0m
<!DOCTYPE html>
<html>
<head>
  <title>Oops - Error 500</title>
.....

В логах выводятся следующие строки

Скриншот, показывающий ошибку сети с тайм-аутом, включая заголовки HTTP и детали ошибки. (Подпись сгенерирована ИИ)

Мне кажется, что Discourse испытывает трудности с обработкой обратного вызова, и теперь мне нужно более глубокое понимание того, что происходит за кулисами в Discourse.

Я не вижу никаких подсказок в логах, несмотря на то, что включил все известные мне режимы отладки.

Хм, действительно застрял на этом, и это раздражает :frowning:

Привет, ребята,

Я уже продвинулся гораздо дальше :slight_smile:

Это было связано с нашей инфраструктурой EAM …

Теперь у меня возникла следующая проблема :smiley:

Мой userinfotoken выглядит так:


{
  "company-i": "A1",
  "accounting-code": "5806",
  "given_name": "Mister",
  "family_name": "Bean",
  "name": "Mister Bean",
  "departmentnumber": "Covert Operations",
  "salutation": "Dude",
  "description": "Some description",
  "preferredlanguage": "DE",
  "inumber": "723jfio-7zwe8489",
  "employeenumber": "36484332",
  "employeetype": "BigCompany",
  "uid": "f57383",
  "adupn": "f57383@europe.bigcom.corp",
  "uniqueuid": "f57383",
  "uniqueuidq": "f57383",
  "loginname": "f57383",
  "email": "Mister.Bean@bigcom.corp",
  "sub": "f57383",
  "subname": "f57383"
}

И в логах я получаю эту ошибку:


ActiveRecord::NotNullViolation (PG::NotNullViolation: ERROR:  null value in column "provider_uid" of relation "user_associated_accounts" violates not-null constraint
DETAIL:  Failing row contains (6, oauth2_basic, null, null, 2024-12-03 13:06:49.831182, {"name": "Mister Bean", "email": "Mister.Bean@bigcom.corp", "user..., {"token": "dszghsdhsdfoph", "expires": true, "expir..., {}, 2024-12-03 13:06:49.831362, 2024-12-03 13:06:49.831362).

Похоже, что Discourse не может сопоставить ответ от userinfoendpoint со своими внутренними именами.

Я пытался настроить это через UI:

но, вероятно, неправильно.

Может ли кто-нибудь дать подсказку, как это настроить?

Спасибо и привет,

WS

Заполнено ли поле oauth2 json user id path?

Убедитесь, что эти значения установлены: https://meta.discourse.org/t/configure-sign-up-and-log-in-with-auth0-using-the-oauth2-basic-plugin/64633#configure-discourse-3.

Привет,

спасибо за ответ.

Похоже, мой провайдер не отправляет поля provider_name и provider_uid, из-за чего Discourse пытается создать запись без них и получает нарушение NOT NULL.
Но разве это не задача плагина (oauth2_basic) заполнить эти значения (если они не приходят от удалённого IdP)?
Я ожидал что-то вроде:

provider_name: “oauth2_basic”
provider_uid: “1234”

Чтобы это автоматически применялось при каждом входе пользователя… :frowning:

provider_name зафиксирован как oauth2_basic, а provider_uid будет тем значением, которое вы укажете в oauth2 json user id path.

Вам просто нужно настроить этот параметр сайта на путь к идентификатору в зависимости от того, что возвращает ваш провайдер. Плагин использует этот JSON-путь для заполнения provider_uid, поэтому, если он в настоящее время пуст или путь не существует, значение будет nil.

Судя по вашему JSON выше, значение должно быть sub.

Но я сделал именно это…

Вы правы, путь должен быть “sub”, но я настроил это здесь

Погодите… Вы действительно были правы, а я глупо поступил :frowning:

Неверная настройка…

Теперь всё работает!

Есть ли способ сохранить эти настройки куда-нибудь :smiley:
сделать резервную копию только настроек конфигурации?

Они хранятся в базе данных?

Отлично.

Все настройки сайта сохраняются в базе данных. Если у вас есть доступ к rails console, следующая команда покажет вам список настроек.

SiteSetting.where("name LIKE 'oauth2_%'").pluck(:name, :value)

Вы также можете просто создать резервную копию.