NeonCRM과 discourse-oauth2-basic 플러그인 사용 시 오류 발생

discourse-oauth2-basic 플러그인을 설정하여 사용자가 NeonCRM에서 생성하고 저장한 계정 자격 증명으로 우리의 Discourse 포럼에 로그인할 수 있도록 하려 합니다.

NeonCRM 지원 기술자가 이 형식을 사용하여 수동으로 인가 URL을 생성할 수 있다고 설명해 주었습니다:

https://{{Org ID}}.z2systems.com/np/oauth/auth?response_type=code&client_id={{Client ID}}&redirect_uri={{Redirect URL}}

Discourse에서 플러그인을 위해 다음 매개변수를 설정했습니다:

oauth2 client id: MY-CLIENT-ID
oauth2 client secret: MY-CLIENT-SECRET
oauth2 authorize url: https://MY-NEON-ID.z2systems.com/np/oauth/auth
oauth2 token url: https://www.z2systems.com/np/oauth/token

따라서 이 URL을 수동으로 생성했습니다(일부 정보는 가렸습니다):

https://MY-NEON-ID.z2systems.com/np/oauth/auth?response_type=code&client_id=MY-CLIENT-ID&redirect_uri=https://MY-FORUM.COM/auth/oauth2_basic/callback

이 URL을 브라우저에 입력하면 다음과 같은 메시지가 포함된 Discourse 페이지가 표시됩니다:

그리고 로그에는 다음과 같이 표시됩니다:

(oauth2_basic) Authentication failure! csrf_detected: OmniAuth::Strategies::OAuth2::CallbackError, csrf_detected | CSRF detected

왜 이런 현상이 발생하는 것일까요?

그렇다면 이 URL을 대신 사용한다고 하면:

https://MY-NEON-ID.z2systems.com/np/oauth/auth?response_type=code&client_id=MY-CLIENT-ID&redirect_uri=https://MY-FORUM.COM/

(redirect_uri 끝에 있는 callback 부분을 제거했습니다)

실제로 NeonCRM 로그인 페이지에 도달하게 되고,在那里 테스트 사용자 자격 증명을 입력하여 로그인합니다. 그런 다음 MY-FORUM.COM으로 리디렉션됩니다. 하지만 거기에 도착하면 전혀 로그인되어 있지 않은 상태입니다. 페이지 상단의 “Login” 버튼을 클릭한 후 회색 “login with OAtuh2” 버튼을 클릭하면, 다음과 같은 메시지가 포함된 새 창이 열립니다:

그리고 로그에 두 가지 오류가 표시됩니다:

ArgumentError (Invalid URI: )

/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/excon-0.64.0/lib/excon.rb:126:in `new’

그리고

Failed to handle exception in exception app middleware : Invalid URI:

이 redirect_uri가 잘못되었습니다. 플로우가 정상적으로 작동하려면 올바르게 설정해야 합니다.

플러그인 문서에서는 기본 설정의 첫 번째 항목으로 설명되어 있습니다.

NeonCRM도 그들의 문서에서 설정 방법을 안내하고 있습니다.

@Falco님, 감사합니다!

redirect_uri를 다음과 같이 설정해 보았어요:

https://MY-FORUM.COM/auth/oauth2_basic/callback

하지만 작동하지 않았습니다. 원래 작성한 게시글을 참고해 주세요 (방금 편집해서 더 명확하게 만들었으니, 혹시 놓치셨다면 죄송합니다).

포럼이 HTTPS로 실행되고 있나요?

네, @Falco 맞습니다.

https://forum.efao.ca/

force_https 설정을 활성화하셨나요?

@Falco, force_https를 활성화하는 방법을 찾았고 true로 설정했습니다.

하지만 여전히 같은 오류가 발생합니다.

NeonCRM에서 추천한 위젯을 포럼 상단에 통합했습니다 (원하시면 직접 사용해 볼 수 있습니다).

https://forum.efao.ca/

위젯을 사용하면 원래 게시글과 동일한 결과가 나타납니다.

(oauth2_basic) 인증 실패! csrf_detected: OmniAuth::Strategies::OAuth2::CallbackError, csrf_detected | CSRF 감지됨

누구 있나요? @david님이나 @Falco님?

oauth2 플러그인에서 Excon을 사용하는 곳은 다음과 같습니다:

oauth2_user_json_url 값은 무엇으로 설정되어 있나요? 비어 있을 것으로 의심됩니다. 그렇다면 Discourse가 빈 URL에 연결을 시도하지 않도록 oauth2_fetch_user_details도 비활성화해야 합니다.

@david, 맞습니다. 실제로 비어 있습니다.

oauth2_user_json_url에 무엇을 설정해야 하는지 모르겠습니다. 불행히도 NeonCRM 기술 지원을 통해 이 문제를 해결하려고 여러 번 시도해 보았지만, 결국 실패했습니다.

기능을 비활성화하려고 하면 oauth2 callback user id path를 설정해야 한다는 메시지가 표시되는데, 앞서 말씀드린 대로 이 값이 무엇이어야 하는지 모르겠습니다.

테스트할 NeonCRM 설치 환경이 없지만, access_token으로 설정하면 작동할 것 같습니다. 시도해 볼 수 있을까요?

위에서 Falco가 링크한 문서에서:

같은 오류:

(oauth2_basic) 인증 실패! csrf_detected: OmniAuth::Strategies::OAuth2::CallbackError, csrf_detected | CSRF 감지됨

백트레이스:

 /var/www/discourse/vendor/bundle/ruby/2.6.0/gems/logster-2.3.2/lib/logster/logger.rb:110:in `report_to_store'
/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/logster-2.3.2/lib/logster/logger.rb:101:in `add_with_opts'
/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/logster-2.3.2/lib/logster/logger.rb:52:in `add'
/usr/local/lib/ruby/2.6.0/logger.rb:543:in `error'
/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/omniauth-1.9.0/lib/omniauth/strategy.rb:163:in `log'
/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/omniauth-1.9.0/lib/omniauth/strategy.rb:486:in `fail!'
/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/omniauth-oauth2-1.6.0/lib/omniauth/strategies/oauth2.rb:71:in `callback_phase'
/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/omniauth-1.9.0/lib/omniauth/strategy.rb:238:in `callback_call'
/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/omniauth-1.9.0/lib/omniauth/strategy.rb:189:in `call!'
/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/omniauth-1.9.0/lib/omniauth/strategy.rb:169:in `call'
/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/omniauth-1.9.0/lib/omniauth/strategy.rb:192:in `call!'
/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/omniauth-1.9.0/lib/omniauth/strategy.rb:169:in `call'
/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/omniauth-1.9.0/lib/omniauth/strategy.rb:192:in `call!'
/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/omniauth-1.9.0/lib/omniauth/strategy.rb:169:in `call'
/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/omniauth-1.9.0/lib/omniauth/strategy.rb:192:in `call!'
/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/omniauth-1.9.0/lib/omniauth/strategy.rb:169:in `call'
/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/omniauth-1.9.0/lib/omniauth/strategy.rb:192:in `call!'
/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/omniauth-1.9.0/lib/omniauth/strategy.rb:169:in `call'
/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/omniauth-1.9.0/lib/omniauth/strategy.rb:192:in `call!'
/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/omniauth-1.9.0/lib/omniauth/strategy.rb:169:in `call'
/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/omniauth-1.9.0/lib/omniauth/builder.rb:64:in `call'
/var/www/discourse/lib/middleware/omniauth_bypass_middleware.rb:30:in `call'
/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/rack-2.0.7/lib/rack/tempfile_reaper.rb:15:in `call'
/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/rack-2.0.7/lib/rack/conditional_get.rb:25:in `call'
/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/rack-2.0.7/lib/rack/head.rb:12:in `call'
/var/www/discourse/lib/content_security_policy/middleware.rb:12:in `call'
/var/www/discourse/lib/middleware/anonymous_cache.rb:218:in `call'
/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/rack-2.0.7/lib/rack/session/abstract/id.rb:232:in `context'
/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/rack-2.0.7/lib/rack/session/abstract/id.rb:226:in `call'
/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/actionpack-5.2.3/lib/action_dispatch/middleware/cookies.rb:670:in `call'
/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/actionpack-5.2.3/lib/action_dispatch/middleware/callbacks.rb:28:in `block in call'
/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/activesupport-5.2.3/lib/active_support/callbacks.rb:98:in `run_callbacks'
/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/actionpack-5.2.3/lib/action_dispatch/middleware/callbacks.rb:26:in `call'
/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/actionpack-5.2.3/lib/action_dispatch/middleware/debug_exceptions.rb:61:in `call'
/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/actionpack-5.2.3/lib/action_dispatch/middleware/show_exceptions.rb:33:in `call'
/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/logster-2.3.2/lib/logster/middleware/reporter.rb:43:in `call'
/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/railties-5.2.3/lib/rails/rack/logger.rb:38:in `call_app'
/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/railties-5.2.3/lib/rails/rack/logger.rb:28:in `call'
/var/www/discourse/config/initializers/100-quiet_logger.rb:18:in `call'
/var/www/discourse/config/initializers/100-silence_logger.rb:31:in `call'
/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/actionpack-5.2.3/lib/action_dispatch/middleware/remote_ip.rb:81:in `call'
/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/actionpack-5.2.3/lib/action_dispatch/middleware/request_id.rb:27:in `call'
/var/www/discourse/lib/middleware/enforce_hostname.rb:17:in `call'
/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/rack-2.0.7/lib/rack/method_override.rb:22:in `call'
/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/actionpack-5.2.3/lib/action_dispatch/middleware/executor.rb:14:in `call'
/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/rack-2.0.7/lib/rack/sendfile.rb:111:in `call'
/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/rack-mini-profiler-1.1.0/lib/mini_profiler/profiler.rb:184:in `call'
/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/message_bus-2.2.0/lib/message_bus/rack/middleware.rb:57:in `call'
/var/www/discourse/lib/middleware/request_tracker.rb:163:in `call'
/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/railties-5.2.3/lib/rails/engine.rb:524:in `call'
/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/railties-5.2.3/lib/rails/railtie.rb:190:in `public_send'
/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/railties-5.2.3/lib/rails/railtie.rb:190:in `method_missing'
/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/rack-2.0.7/lib/rack/urlmap.rb:68:in `block in call'
/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/rack-2.0.7/lib/rack/urlmap.rb:53:in `each'
/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/rack-2.0.7/lib/rack/urlmap.rb:53:in `call'
/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/unicorn-5.5.1/lib/unicorn/http_server.rb:605:in `process_client'
/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/unicorn-5.5.1/lib/unicorn/http_server.rb:700:in `worker_loop'
/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/unicorn-5.5.1/lib/unicorn/http_server.rb:548:in `spawn_missing_workers'
/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/unicorn-5.5.1/lib/unicorn/http_server.rb:144:in `start'
/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/unicorn-5.5.1/bin/unicorn:128:in `<top (required)>'
/var/www/discourse/vendor/bundle/ruby/2.6.0/bin/unicorn:23:in `load'
/var/www/discourse/vendor/bundle/ruby/2.6.0/bin/unicorn:23:in `<main>'

흥미롭네요… 백트레이스에 따르면 해당 오류는 OAuth2 라이브러리의 이 부분에서 발생하고 있습니다. 즉, state 매개변수가 올바르게 매칭되지 않고 있다는 뜻입니다.

확인해 주시겠지만, 이 오류는 프로덕션 사이트에서 발생한 것이 맞나요? 해당 사이트에 로그인해 보았을 때, 제 쪽에서는 state가 올바르게 전달되는 것으로 보입니다.

이는 서버 측에 문제가 있음을 시사합니다. 사이트는 어떻게 호스팅하고 계신가요? 표준 절차를 따르셨나요?

그리고 @david, 이건 관련이 있는지 모르겠지만, 로그에 이 별도의 항목도 있습니다:

Job exception: SSL_connect returned=1 errno=0 state=error: certificate verify failed (self signed certificate)

우리는 SSL 인증서에 Let’sEncrypt를 사용하고 있습니다.

그 오류에 백트레이스가 첨부되어 있나요?

네, forum.efao.ca가 저희 프로덕션 사이트입니다. Digital Ocean에서 호스팅 중이며 표준 절차에 따라 설치했습니다.

/usr/local/lib/ruby/2.6.0/net/protocol.rb:44:in `connect_nonblock'
/usr/local/lib/ruby/2.6.0/net/protocol.rb:44:in `ssl_socket_connect'
/usr/local/lib/ruby/2.6.0/net/pop.rb:553:in `do_start'
/usr/local/lib/ruby/2.6.0/net/pop.rb:531:in `start'
/var/www/discourse/app/jobs/scheduled/poll_mailbox.rb:43:in `poll_pop3'
/var/www/discourse/app/jobs/scheduled/poll_mailbox.rb:18:in `execute'
/var/www/discourse/app/jobs/base.rb:232:in `block (2 levels) in perform'
/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/rails_multisite-2.0.7/lib/rails_multisite/connection_management.rb:63:in `with_connection'
/var/www/discourse/app/jobs/base.rb:221:in `block in perform'
/var/www/discourse/app/jobs/base.rb:217:in `each'
/var/www/discourse/app/jobs/base.rb:217:in `perform'
/var/www/discourse/app/jobs/base.rb:279:in `perform'
/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/mini_scheduler-0.11.0/lib/mini_scheduler/manager.rb:83:in `process_queue'
/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/mini_scheduler-0.11.0/lib/mini_scheduler/manager.rb:34:in `block in initialize'

서버에 접근할 수 없기 때문에 이 문제를 진단하기 위해 제가 더 할 수 있는 일은 많지 않습니다. 많은 사용자가 oauth2 플러그인을 문제 없이 사용하고 있으므로, 어딘가에 설정 문제가 있는 것 같습니다. 도움이 될 수 있는 유일한 다른 방법은 안정 버전(stable branch) 대신 Discourse의 최신 버전으로 업그레이드하는 것입니다.

커뮤니티의 다른 누군가가 도움을 줄 수도 있지만, 그렇지 않다면 Marketplace 채널에서 도움을 요청해 보시는 것이 성공할 수 있습니다.