secure_session을 매개변수로 추가한 후 커스텀 SSO가 작동하지 않음

안녕하세요! 당사는 Discourse가 로그인 처리 및 계정 생성에 사용할 수 있도록 회사 로그인 정보를 패키지로 묶어주는 맞춤형 SSO 플러그인을 개발했습니다. v2.7.0beta4 버전부터 새로운 파라미터인 secure_session을 제공하지 않는다는 오류가 발생하기 시작했습니다. 이 파라미터의 형식이 어떻게 되어야 하고 어떤 내용을 포함해야 하는지에 대한 자세한 정보를 어디에서도 찾을 수 없습니다. 이 업데이트와 관련하여 추가적인 문서가 어딘가에 있을까요?

감사합니다!

현재 확인되고 있는 정확한 오류 메시지가 무엇인지 아시나요? 제 이해로는 Discourse가 이제 SSO의 nonce 매개변수를 사용자의 세션과 연결하고 있습니다. 이는 앱이 /session/sso에 백그라운드 요청을 보내 nonce를 생성해 왔다면, 해당 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는 “Service Gateway” 헤더를 기반으로 DiscourseSingleSignOn에 전송할 페이로드를 생성하기 위한 우리 플러그인의 일부입니다. ("Service Gateway"는 사용자가 우리 시스템에 로그인되어 있는지 여부를 결정하기 위한 회사의 내부 구조입니다.)

스택의 해당 부분을 설명하자면 다음과 같습니다:

  • 우리의 current_user 함수는 헤더를 사용하여 헤더의 User ID와 external_id가 일치하는 SingleSignOnRecord를 조회합니다. 그렇지 않은 경우 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

해당 함수의 3번째 줄인 parse 호출에서 문제가 발생하는 것으로 보입니다. 객체를 initialize하려고 시도할 때, 기대하는 secure_session 값이 존재하지 않기 때문입니다.

방구석! 저도 이거 궁금해요!

새 사이트 설정인 discourse_connect_csrf_protection을 false로 설정하면 문제가 해결될지 궁금합니다. 해당 사이트 설정은 숨겨져 있으므로 Rails 콘솔을 통해 설정해야 합니다. 자세한 내용은 이 토픽의 답변에서 확인할 수 있습니다: DiscourseConnect flow no longer functions.

불행히도 그 새로운 설정만으로는 문제를 해결할 수 없다고 생각합니다. 해당 값이 실제로 사용되지 않더라도, 코드 내부에서는 여전히 secure_session 객체를 전달해야 하므로요.

일반적으로 인증 플러그인의 경우 코어 DiscourseConnect 구현을 오버라이드하는 것보다 공식 훅을 사용하는 것을 권장합니다.

플러그인 전체를 확인해 보지 않은 상태에서는 단정하기 어렵지만, secure_session에 nil 값을 전달해 보는 것을 시도해 볼 수 있습니다. 이를 수행하면서 새로운 사이트 설정을 토글하면, 아마 문제를 해결하는 데 조금 더 도움이 될 수 있습니다.

v2.7.0.beta4 코드를 더 자세히 살펴본 결과, ApplicationController에 secure_session 정의가 있다는 것을 알게 되었습니다. 플러그인 코드를 아래처럼 수정하니 Staging 환경에서는 정상적으로 동작하는 것 같습니다:

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

…정말 이렇게 간단한 건가요? :thinking:

그렇게 하면 작동할 것 같은데요 - 보안 세션은 보통 ApplicationController의 해당 메서드에서 생성되거든요. :slight_smile:

완벽해요! 이 문제를 오리 돌봄(rubber-duck debugging)으로 함께 생각해 주셔서 감사합니다. :duck: :metal: