OAUTH2 기본 - 악몽이에요 :(

여러분 안녕하세요,

2일째 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"}

보드 설정(스코프 관련)은 다음과 같습니다:

IdP 설정은 다음과 같습니다:

이 문제를 더 깊이 디버깅하는 방법에 대해 조언을 주실 수 있는 분이 있을까요?

테스트하거나 디버깅 출력을 더 잘 볼 수 있는 좋은 curl 명령이나 방법(이미 상세 디버깅 옵션은 켜둔 상태)이 있을까요?

이 문제로 머리가 지끈지끈 아파옵니다 :frowning:

감사합니다!

WS

업데이트:

옵션을 다시 "scope"만 선택하도록 변경했습니다.

하지만 여전히 작동하지 않았습니다 :frowning:

IdP의 UI에서 scope가 잘못 설정되어 있다는 것을 발견했습니다 …

UI에는 openId로 표시되어 있지만, 실제 값은 openid입니다 :slight_smile:

한 단계는 더 나아졌지만, 이제 시간 초과 오류가 발생하고 있습니다.

image

이 호출 이후


OAuth2 디버깅: 요청 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

다음과 같은 오류로 종료됩니다.

하지만 코드(code)를 가져오는 authorize 호출은 성공적으로 진행되었으므로, 네트워크 문제는 아니겠죠?

curl을 사용하여 호출을 수행할 때

curl --request POST \
  --url https://<IdP address>/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

discourse 도커 컨테이너 내에서 이 명령을 실행하면 다음과 같은 결과가 나옵니다.

{
  "access_token": "<token>",
  "refresh_token": "<refresh_token>",
  "scope": "openid profile email",
  "id_token": "<censored>",
  "token_type": "Bearer",
  "expires_in": 7199
}

이것이 문제의 원인일 수 있을까요?

image

이 필드들이 응답에 없습니다. :frowning:

아무도 없나요? :frowning:

이 주제에 대해 매우 관심이 있습니다. 제 지역 사회를 위한 첫 번째 디스커스 페이지에 얼마나 많은 노력이 필요할지 조사 중이며, 이후 점차 자녀들의 학교로 확장할 계획이기 때문입니다. 현재 다양한 플랫폼에서 오는 알림으로 인해 과부하 상태에 있습니다.

페이지로의 접근 장벽을 줄이는 것이 매우 중요하므로 도움을 드리고 싶습니다. 저는 루비(Ruby) 초보자인데, 찾은 몇 가지 리소스와 해당 리소스에 대한 아이디어를 공유해 드릴 수 있습니다.

이미 확인하셨을 수도 있지만, discourse-openid-connect의 GitHub 저장소를 살펴보시는 것도 좋습니다.

해당 저장소에는 37명의 기여자가 있으며, 그중 한 명이 여러분의 질문에 도움을 줄 수 있을 것 같습니다.

이것이 도움이 되기를 바랍니다. 정말 좋은 질문입니다.

Hey @thecatfix,

전망이 좋아 보입니다. 바로 시도해 보고 최대한 빨리 피드백을 드리겠습니다.
불행히도 오늘 시간이 없습니다. 주말에 작업을 할 수 있을지도 모르겠네요.

감사합니다. 주말 잘 보내세요!

안녕하세요,

기다리지 못하고 오늘 테스트해 보았지만, 어째서인지 우리 EAM 부서의 사람들이 이 discovery를 엉망으로 만들어 버렸습니다. 작동하지 않으며, 자격 증명이 1000% 유효하다는 것을 확신하고 있음에도 불구하고(curl로 테스트함) invalid client - authentication failed 오류가 발생합니다.
그래서 다시 처음부터 시작하게 되었습니다.

재미있는 점은 authorize 엔드포인트에 대한 요청이 항상 timeoutError를 발생시키고, 그 속도가 꽤 빠르다는 것입니다.


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

이것을 발견했고, discourse에서도 이와 같은 일이 가능한지 궁금합니다.

https://stackoverflow.com/questions/17227277/faradayerrortimeouterror-timeouterror
저는 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: # lo의 pcap는 두 번 캡처됩니다
      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에 접속해서 가져온 코드로 userinfo를 받아야 하는데, 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......

[0m<[0m[31m[1mEther[0m  [34mdst[0m[0m=[0m[35m00:00:00:00:00:00[0m [34msrc[0m[0m=[0m[35m00:00:00:00:00:00[0m [34mtype[0m[0m=[0m[35mIPv4[0m [0m|[0m[0m<[0m[31m[1mIP[0m  [34mihl[0m[0m=[0m[35m5[0m [34mlen[0m[0m=[0m[35m52[0m [34mid[0m[0m=[0m[35m7742[0m [34mflags[0m[0m=[0m[35mDF[0m [34mfrag[0m[0m=[0m[35m0[0m [34mttl[0m[0m=[0m[35m127[0m [34mproto[0m[0m=[0m[35mtcp[0m [34mchksum[0m[0m=[0m[35m0xdf83[0m [34m[4m[1msrc[0m[0m=[0m[35m[4m[1m127.0.0.1[0m [34m[4m[1mdst[0m[0m=[0m[35m[4m[1m127.0.0.1[0m [0m|[0m[0m<[0m[31m[1mTCP[0m  [34msport[0m[0m=[0m[35m3000[0m [34mdport[0m[0m=[0m[35m33574[0m [34mseq[0m[0m=[0m[35m271589520[0m [34mack[0m[0m=[0m[35m3955524768[0m [34mdataofs[0m[0m=[0m[35m8[0m [34mflags[0m[0m=[0m[35mA[0m [34mwindow[0m[0m=[0m[35m499[0m [34mchksum[0m[0m=[0m[35m0xfe28[0m [34moptions[0m[0m=[0m[35m[('NOP', None), ('NOP', None), ('Timestamp', (3962821542, 3962821542))][0m [0m|[0m[0m>[0m[0m>[0m[0m>[0m
[0m<[0m[31m[1mEther[0m  [34mdst[0m[0m=[0m[35m00:00:00:00:00:00[0m [34msrc[0m[0m=[0m[35m00:00:00:00:00:00[0m [34mtype[0m[0m=[0m[35mIPv4[0m [0m|[0m[0m<[0m[31m[1mIP[0m  [34mihl[0m[0m=[0m[35m5[0m [34mlen[0m[0m=[0m[35m281[0m [34mid[0m[0m=[0m[35m55898[0m [34mflags[0m[0m=[0m[35mDF[0m [34mfrag[0m[0m=[0m[35m0[0m [34mttl[0m[0m=[0m[35m127[0m [34mproto[0m[0m=[0m[35mtcp[0m [34mchksum[0m[0m=[0m[35m0x2282[0m [34m[4m[1msrc[0m[0m=[0m[35m[4m[1m127.0.0.1[0m [34m[4m[1mdst[0m[0m=[0m[35m[4m[1m127.0.0.1[0m [0m|[0m[0m<[0m[31m[1mTCP[0m  [34msport[0m[0m=[0m[35m3000[0m [34mdport[0m[0m=[0m[35m33530[0m [34mseq[0m[0m=[0m[35m491394[0m [34mack[0m[0m=[0m[35m1852886175[0m [34mdataofs[0m[0m=[0m[35m8[0m [34mflags[0m[0m=[0m[35mPA[0m [34mwindow[0m[0m=[0m[35m507[0m [34mchksum[0m[0m=[0m[35m0xff0d[0m [34moptions[0m[0m=[0m[35m[('NOP', None), ('NOP', None), ('Timestamp', (3962823893, 3962816870))][0m [0m|[0m[0m<[0m[31m[1mRaw[0m  [34mload[0m[0m=[0m[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'[0m [0m|[0m[0m>[0m[0m>[0m[0m>[0m[0m>[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

.....

[0m<[0m[31m[1mEther[0m  [34mdst[0m[0m=[0m[35m00:00:00:00:00:00[0m [34msrc[0m[0m=[0m[35m00:00:00:00:00:00[0m [34mtype[0m[0m=[0m[35mIPv4[0m [0m|[0m[0m<[0m[31m[1mIP[0m  [34mihl[0m[0m=[0m[35m5[0m [34mlen[0m[0m=[0m[35m52[0m [34mid[0m[0m=[0m[35m15439[0m [34mflags[0m[0m=[0m[35mDF[0m [34mfrag[0m[0m=[0m[35m0[0m [34mttl[0m[0m=[0m[35m127[0m [34mproto[0m[0m=[0m[35mtcp[0m [34mchksum[0m[0m=[0m[35m0xc172[0m [34m[4m[1msrc[0m[0m=[0m[35m[4m[1m127.0.0.1[0m [34m[4m[1mdst[0m[0m=[0m[35m[4m[1m127.0.0.1[0m [0m|[0m[0m<[0m[31m[1mTCP[0m  [34msport[0m[0m=[0m[35m33574[0m [34mdport[0m[0m=[0m[35m3000[0m [34mseq[0m[0m=[0m[35m3955524768[0m [34mack[0m[0m=[0m[35m271589749[0m [34mdataofs[0m[0m=[0m[35m8[0m [34mflags[0m[0m=[0m[35mA[0m [34mwindow[0m[0m=[0m[35m511[0m [34mchksum[0m[0m=[0m[35m0xfe28[0m [34moptions[0m[0m=[0m[35m[('NOP', None), ('NOP', None), ('Timestamp', (3962831618, 3962831618))][0m [0m|[0m[0m>[0m[0m>[0m[0m>[0m
[0m<[0m[31m[1mEther[0m  [34mdst[0m[0m=[0m[35m00:00:00:00:00:00[0m [34msrc[0m[0m=[0m[35m00:00:00:00:00:00[0m [34mtype[0m[0m=[0m[35mIPv4[0m [0m|[0m[0m<[0m[31m[1mIP[0m  [34mihl[0m[0m=[0m[35m5[0m [34mlen[0m[0m=[0m[35m710[0m [34mid[0m[0m=[0m[35m7744[0m [34mflags[0m[0m=[0m[35mDF[0m [34mfrag[0m[0m=[0m[35m0[0m [34mttl[0m[0m=[0m[35m127[0m [34mproto[0m[0m=[0m[35mtcp[0m [34mchksum[0m[0m=[0m[35m0xdcef[0m [34m[4m[1msrc[0m[0m=[0m[35m[4m[1m127.0.0.1[0m [34m[4m[1mdst[0m[0m=[0m[35m[4m[1m127.0.0.1[0m [0m|[0m[0m<[0m[31m[1mTCP[0m  [34msport[0m[0m=[0m[35m3000[0m [34mdport[0m[0m=[0m[35m33574[0m [34mseq[0m[0m=[0m[35m271589749[0m [34mack[0m[0m=[0m[35m3955524768[0m [34mdataofs[0m[0m=[0m[35m8[0m [34mflags[0m[0m=[0m[35mPA[0m [34mwindow[0m[0m=[0m[35m512[0m [34mchksum[0m[0m=[0m[35m0xbb[0m [34moptions[0m[0m=[0m[35m[('NOP', None), ('NOP', None), ('Timestamp', (3962831618, 3962831618))][0m [0m|[0m[0m<[0m[31m[1mRaw[0m  [34mload[0m[0m=[0m[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'[0m [0m|[0m[0m>[0m[0m>[0m[0m>[0m[0m>[0m
<!DOCTYPE html>
<html>
<head>
  <title>Oops - Error 500</title>
.....

로그에는 이 줄들이 출력됩니다

타임아웃을 포함하는 네트워크 오류, HTTP 헤더 및 오류 세부 사항을 보여주는 스크린샷. (AI가 캡션을 생성함)

디스코어가 콜백을 처리하는 데 어려움을 겪는 것 같습니다. 이제 디스코어의 내부에서 어떤 일이 일어나는지에 대해 더 깊이 이해할 필요가 있습니다.

알고 있는 모든 디버그 모드를 활성화해 두었음에도 불구하고 로그에서 아무런 힌트도 얻을 수 없습니다.

음, 이 문제로 정말 막혀서 답답하네요 :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가 해당 필드 없이 항목을 생성하려고 하다가 NotNull 위반 오류가 발생합니다.
하지만 원격 IdP에서 해당 값이 전달되지 않을 경우 이 값들을 채우는 것은 플러그인(oauth2_basic)의 책임이 아닌가요?

provider_name: “oauth2_basic”
provider_uid: “1234”

이런 값들이 모든 사용자 로그인 시 자동으로 사용되길 기대합니다… :frowning:

provider_name은 oauth2_basic으로 고정되어 있으며, provider_uid는 oauth2 json user id path에 입력한 값이 됩니다.

해당 사이트 설정을 제공자(provider)가 반환하는 id의 경로로 설정하기만 하면 됩니다. 플러그인은 이 JSON 경로를 사용하여 provider_uid를 채우기 때문에, 현재 경로가 비어 있거나 존재하지 않는 경로인 경우 값은 nil이 됩니다.

위 JSON을 보면 값은 sub여야 할 것 같습니다.

하지만 저는 그렇게 했습니다…

말씀이 맞습니다. 경로는 "sub"여야 하지만, 저는 여기서 그렇게 설정했습니다.

잠깐만… 사실 네가 맞았어, 내가 멍청했어 :frowning:

설정을 잘못 했었어 …

이제 작동해!

이 설정들을 어딘가에 저장할 수 있는 방법이 있을까요? :smiley:
설정 값만 백업할 수 있을까요?

데이터베이스에 저장되어 있나요?

좋습니다.

모든 사이트 설정은 데이터베이스에 저장됩니다. rails 콘솔 접근 권한이 있다고 가정할 때, 다음 명령을 실행하면 설정 목록을 확인할 수 있습니다.

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

또는 백업을 생성하는 것도 좋습니다.