Пользовательский SSO не работает после добавления secure_session в качестве параметра

Здравствуйте! Мы разработали собственный плагин SSO, чтобы объединить данные для входа нашей компании в пакет, который Discourse может использовать для обработки входа и создания учётных записей. Начиная с версии v2.7.0beta4, мы начали получать ошибки, указывающие на то, что мы не передаём новый параметр secure_session. Нигде не удалось найти подробностей о том, какой должен быть формат этого параметра и что он должен содержать. Есть ли какая-либо дополнительная документация по этому обновлению?

Спасибо!

Вы знаете точный текст сообщения об ошибке, которое вы видите? Насколько я понимаю, Discourse теперь привязывает параметр SSO nonce к сессии пользователя. Это означает, что если приложение генерирует nonce, выполняя фоновый запрос к /session/sso, то nonce будет недействительным. Возможно, именно с этой проблемой вы столкнулись.

Конечно! Вот стек ошибок, который мы видим в нашей системе APM:

ArgumentError: missing keyword: :secure_session
…r/www/discourse/app/models/discourse_single_sign_on.rb:   28:in `initialize'
               /var/www/discourse/lib/single_sign_on.rb:   65:in `new'
               /var/www/discourse/lib/single_sign_on.rb:   65:in `parse'
…ourse_account_auth/lib/service_gateway_current_user.rb:   72:in `upsert_sso_record'
…ourse_account_auth/lib/service_gateway_current_user.rb:   53:in `create_sso_record'
…ourse_account_auth/lib/service_gateway_current_user.rb:   28:in `current_user'
                 /var/www/discourse/lib/current_user.rb:   36:in `current_user'

Файл service_gateway_current_user.rb является частью нашего плагина для формирования полезной нагрузки, отправляемой в DiscourseSingleSignOn, на основе заголовков «Service Gateway» («Service Gateway» — это внутренняя структура нашей компании для определения, авторизован ли пользователь в нашей системе).

Чтобы пояснить эту часть стека вызовов:

  • Наша функция current_user использует заголовки для поиска SingleSignOnRecord, где external_id совпадает с идентификатором пользователя в заголовках. Если совпадения нет, мы переходим к секции create_sso_record.
  • create_sso_record создает хеш — { external_id: sg_headers[:user_id], email: sg_headers[:email] } — и передает его в нашу функцию upsert_sso_record.
  • upsert_sso_record принимает этот хеш (payload в контексте ниже) и пытается создать объект DiscourseSingleSignOn:
def upsert_sso_record(payload)
  encoded_query = Base64.encode64(payload.to_query)
  sig = OpenSSL::HMAC.hexdigest('sha256', ENV['SSO_SECRET'], encoded_query)
  sso = DiscourseSingleSignOn.parse({ sso: encoded_query, sig: sig }.to_query)

  if SiteSetting.verbose_sso_logging
    Rails.logger.warn("Verbose SSO log: Started SSO process\n\n#{sso.diagnostics}")
    Rails.logger.warn("SSO Payload:\n\n#{payload}")
  end

  begin
    user = sso.lookup_or_create_user(@request.ip)
  rescue ActiveRecord::RecordInvalid => ex
    Rails.logger.error "Unable to find/create sso user #{ex}"
  end
  user
end

Вызов parse на третьей строке этой функции, по-видимому, является местом возникновения проблемы. Как только он пытается инициализировать объект, ожидаемое значение secure_session отсутствует.

Поднимаю! Мне тоже интересно!

Мне интересно, поможет ли отключение новой настройки сайта discourse_connect_csrf_protection (установив её значение в false) решить вашу проблему. Эта настройка скрыта, поэтому её необходимо изменить через консоль Rails. Подробную информацию об этом можно найти в ответах на эту тему: DiscourseConnect flow no longer functions.

К сожалению, я не думаю, что новый параметр сам по себе поможет — внутренняя часть кода всё равно потребует передачи объекта secure_session, даже если он не используется.

В целом мы рекомендуем использовать официальные хуки для плагинов аутентификации, а не переопределять реализацию DiscourseConnect в ядре.

Без просмотра всего плагина точно сказать сложно, но можно попробовать передать nil для secure_session. Это действие, в сочетании с включением нового параметра сайта, может помочь приблизить ситуацию к рабочей.

После более детального изучения кода версии v2.7.0.beta4 я заметил, что в ApplicationController есть определение secure_session. Изменение кода нашего плагина следующим образом, похоже, работает в нашей тестовой среде:

sso = DiscourseSingleSignOn.parse({ sso: encoded_query, sig: sig }.to_query, secure_session: secure_session)

…Неужели всё так просто? :thinking:

Звучит так, будто это должно сработать — безопасная сессия обычно формируется именно этим методом в ApplicationController. :slight_smile:

Отлично! Спасибо, что дал мне возможность «разобрать» эту проблему с помощью уточки. :duck: :metal: