Use Discourse as an identity provider (SSO, DiscourseConnect)

이걸로 마인크래프트 서버용 계정 연동 시스템을 만들 수 있었어요! 어떤 모습인지 보여드리려고 공유합니다. Discourse SSO를 처음 다루는 거라 모든 걸 너무 복잡하게 만들었을 수도 있어요. 하지만 작동하는 게 가장 중요한 거니까요.

4개의 좋아요

안녕하세요. 모든 단계를 올바르게 수행했습니다. 하지만 사용자가 로그인되어 있지 않으면 Discourse에서 사용자 로그인 팝업이 표시됩니다. 사용자명과 비밀번호를 입력해도 return_url로 다시 리다이렉트되지 않습니다. 도움을 주실 수 있으신가요?

nonce가 리플레이 공격을 방지하기 위해 존재한다고 가정합니다. HTTPS에서는 리플레이 공격이 불가능하다는 내용을 온라인에서 읽었습니다. 저는 HTTPS를 사용 중입니다. 따라서 여전히 nonce를 사용해야 하나요? nonce를 어디에 저장해야 할지 확신이 서지 않아서 질문합니다. 사용자의 브라우저에 보안이 보장되는 평문 쿠키로 저장하는 것이 합리적인가요? 그리고 반환 페이로드와 함께 브라우저에서 읽는 방식이 맞을까요?

원래 게시물에서 링크된 이 라이브러리(GitHub - ArmedGuy/discourse_sso_node: npm package for Discourse SSO login features. · GitHub)는 사용자 검증 시 nonce를 사용하지 않습니다.

네, 여전히 nonce를 검증해야 합니다. 이는 Discourse가 사용자를 사이트로 리다이렉트할 때 전송하는 페이로드의 재사용을 방지하기 때문입니다.

예를 들어, 사이트가 subscribers 그룹의 멤버만 접근할 수 있는 유료 콘텐츠가 있고, Discourse가 사이트로 전송하는 페이로드의 groups 필드를 사용하여 유료 콘텐츠를 subscribers 그룹 멤버에게만 표시한다고 가정해 보겠습니다. nonce를 검증하지 않으면, 더 이상 subscribers 그룹에 속하지 않는 사용자가 멤버였을 때의 오래된 페이로드를 사용하여 사이트에 로그인하고 유료 콘텐츠를 볼 수 있습니다.

nonce를 짧은 만료 날짜와 함께 데이터베이스에 저장하고, 사용된 즉시 데이터베이스에서 삭제하는 것이 가장 좋습니다. 그러나 데이터베이스를 사용할 수 없는 경우, nonce를 저장하기 위해 쿠키를 사용할 수 있지만, 페이로드 재사용을 방지하기 위해 몇 가지 추가 단계가 필요합니다:

  1. nonce 생성 시 만료 날짜를 부여합니다. 예를 들어, 현재 시각 기준 10분 후 등.
  2. 사용자가 nonce와/또는 만료 날짜를 수정하지 못하도록 쿠키 전체(Nonce + 만료 날짜)에 서명을 합니다.
  3. 쿠키의 서명을 검증하고 nonce가 만료되지 않았는지 확인합니다.

이것은 페이로드 재사용에 대해 충분히 좋은 보호 장치를 제공합니다. 기술적으로는 여전히 페이로드 재사용이 가능하지만, 영원한 것이 아니라 10분이라는 시간 창으로 제한됩니다.

쿠키가 필요 없는 더 간단한 솔루션은 생성하는 페이로드에 커스텀 필드로 만료 날짜를 포함하는 것입니다. 그런 다음 Discourse가 페이로드와 함께 사용자를 사이트로 리다이렉트하면, 커스텀 필드가 포함되며 만료 날짜를 검색하여 만료되지 않았는지 검증할 수 있습니다. 페이로드에 커스텀 필드를 포함하려면 custom. 접두사가 붙은 필드를 포함해야 하므로, 페이로드는 다음과 같이 보입니다:

nonce=NONCE&return_sso_url=RETURN_URL&custom.expiration_date=TIMESTAMP
4개의 좋아요

또한 nonce를 세션에 저장할 수도 있습니다. 이렇게 하면 사용자가 nonce를 변조하는 것도 방지할 수 있습니다.

3개의 좋아요

수년 전 이 스레드에 다시 돌아와 봅니다.

누군가 Discourse SSO 제공자가 무엇을 반환하는지 알려주실 수 있을까요? (@pfaffman 또는 @tobiaseigen 또는 @iamntz) 직접 시도해 봐야 한다는 것은 알지만, 문서로 정리되어 있으면 좋겠습니다. GitHub의 PHP 샘플 코드에는 다른 필드에 대한 언급이 거의 없습니다.

이상적으로는 Discourse가 SSO에 외부 스크립트를 사용할 때와 동일한 필드(예: 외부 ID, 이메일, 사용자 이름, 이름, 아바타 사진 등)를 보내 주면 좋겠습니다. 이렇게 하면 해당 정보를 가져와서 우리 쪽에서 사용자를 생성할 수 있습니다!

이메일도 WordPress에 전달되나요?

그룹, 배지 등은 어떻게 되나요? REST 호출을 통해 이 정보를 찾을 수 있을까요?

마지막으로, 사용자의 개인 메시지나 기타 정보는 어떻게 되나요? Discord가 oAuth 제공자로서 우리 앱이 이 정보를 소비할 수 있도록 허용한다면 정말 훌륭할 것 같습니다.

Discourse Connect를 활성화하려고 하면 다음 오류가 발생합니다:
enable_discourse_connect: You cannot enable DiscourseConnect and invite only at the same time.

해결 방법이 있을까요?

해당 질문을 이미 이곳에서 한 적이 있는 것 같습니다: Setup DiscourseConnect - Official Single-Sign-On for Discourse (sso) - #537. enable discourse connect 설정이 아니라 enable discourse connect provider 설정을 구성하려는 경우라면, 다른 해당 토픽이 질문을 하는 올바른 장소입니다.

enable discourse connect provider 설정은 다른 사이트를 위해 Discourse 사이트를 ID 제공자(Identity Provider)로 사용하고자 할 때 사용합니다. enable discourse connect 설정은 외부 사이트를 통해 Discourse에 사용자를 로그인하고자 할 때 사용합니다.

1개의 좋아요

Flask 애플리케이션을 개발하면서 해당 절차를 Python으로 구현했습니다. 필요하신 분들을 위해 기본적인 코드 일부를 공유합니다. 이 주제에 정리된 단계는 비교적 따라하기 쉬웠지만, 저는 보안 전문가가 아니므로 혹시 누락된 부분이 있다면 알려주세요!

2개의 좋아요

Discourse를 SSO 제공자로 구현하려고 시도하고 있는데, Discourse가 어떤 사용자를 검증해야 하는지 어떻게 아는지 이해가 되지 않습니다. 안내 문구에는 "nonce와 return url을 포함한 새로운 payload를 생성하세요"라고 되어 있습니다. 그런데 이 payload를 fetch를 통해 Discourse에 전송할 때, Discourse는 로그인 상태인지 확인할 사용자를 어떻게 파악하는 것일까요? 이 질문이 바보 같게 들릴 수 있지만, 솔직히 이 방식이 어떻게 작동하는지 이해가 되지 않습니다. 저는 수년간 다양한 인증 시스템을 다뤄왔기 때문에 어느 정도 익숙한 편인데도요. 로그인 상태를 확인하려는 사용자 이메일이 Discourse로 전송되는 payload에 포함되어야 하는 건가요? 그렇다면 Discourse에 전송해야 하는 payload의 정확한 구조는 무엇인가요? 만약 그렇다면, Discourse가 정확히 무엇을 확인하는 것인가요? 제 추측은 우리 쪽에서 사용자에게 이메일을 요청한 후, 해당 이메일을 포함한 payload를 Discourse로 보내서 특정 사용자가 로그인되어 있는지 확인하는 방식일 것 같은데, 이는 안내 문구와 일치하지 않아서 완전히 혼란스럽습니다. 도움이 되시면 감사하겠습니다.

괜찮습니다. 해결했습니다. SSO URL을 Discourse 인스턴스에 POST 요청으로 보내야 하고, 그 후 응답을 받는 줄 알았습니다. 그런데 이제 보니 Discourse로 리다이렉트되고, Discourse가 다시 우리 사이트로 리다이렉트하는 방식이었습니다. 이제 어떻게 해야 하는지 명확해졌습니다. 이전 게시글에 대해 죄송합니다.

3개의 좋아요

참고로: 인증 요청에 prompt=none 파라미터를 허용하도록 PR을 제출했습니다. OpenID Connect 프로토콜의 기능과 유사하게, 이 기능을 통해 SSO 사용자는 로그인 다이얼로그로 리디렉션하지 않고도 사용자/클라이언트가 이미 로그인되어 있는지 여부를 확인할 수 있습니다.

해당 PR은 Discourse 팀의 최종 리뷰를 약 8주째 기다리고 있습니다. 예상보다 상당히 오래 걸리는 것 같습니다. :crying_cat_face:

7개의 좋아요

안녕하세요 @mdoggydog - 이렇게 긴 지연으로 죄송합니다!

방금 PR을 검토하고 병합했습니다 - 기여해 주셔서 감사합니다! :raised_hands:

3개의 좋아요

와! @david님, 감사합니다.

약속한 대로, 여기 위키 문서를 업데이트하여 새로운 파라미터에 대한 설명(그리고 이전의 새로운 logout 파라미터)을 추가하고, 일부 오탈자와 문법 오류를 수정했으며, 소스 코드를 살펴본 결과 제 이해에 따라 sso= 페이로드를 문서화하는 참고 섹션을 추가했습니다.

2개의 좋아요

웹사이트를 Discourse의 SSO로 사용하는 것을 중단하고, 대신 Discourse의 내장 로그인 도구를 사용하여 우리 웹사이트의 특정 자료에 대한 접근을 제한하고 싶습니다.

사용할 적절한 도구는 다음과 같습니다: GitHub - discourse/discourse-auth-proxy: An http proxy that uses the DiscourseConnect protocol to authenticate users · GitHub

이를 사용하는 방법에 대한 자세한 설명서를 찾지 못했습니다.

이 도구를 Discourse 사이트와 동일한 DigitalOcean 드롭렛에 설치할 수 있나요, 아니면 다른 곳에 호스팅해야 하나요?

수정: 질문을 굵은 글씨로 표시했습니다 :slight_smile:

1개의 좋아요

위 질문에 대한 도움이 있을까요? Use Discourse as an identity provider (SSO, DiscourseConnect) - #148 by alehandrof

return_sso_url에 자체적으로 쿼리 파라미터를 가진 URL을 설정하고 있습니다:

http://localhost:7000/completeLogin?returnto=%2F

base64 인코딩하기 전에 /session/sso_provider로 sso 파라미터로 전송하는 페이로드는 다음과 같습니다:

nonce=ENIwf0bElViDu325dTd6&return_sso_url=http://localhost:7000/completeLogin?returnto=%2F

인증 후 Discourse가 실제로 리다이렉트하는 URL은 다음과 같습니다(sso와 sig 파라미터는 생략):

http://localhost:7000/completeLogin?returnto=/&sso=...&sig=...

여기서 놀라운 점은 return_sso_url에 설정한 쿼리 문자열이 무엇인지에 의해 URL 디코딩된 것처럼 보인다는 것입니다. returnto=%2F 대신 returnto=/로 나타나기 때문입니다. base64 디코딩 후 sso 내부에서 찾은 return_sso_url의 값에도 %2F 대신 슬래시가 있습니다.

이것이 예상되는 동작인가요? (그렇다면 왜인가요?) 이것이 Discourse의 버그인가요?

sso 페이로드가 /u/{username}.json 및 /session/current.json에서 반환되는 avatar_template 대신 avatar_url을 포함하는 이유는 무엇인가요?

avatar_url은 아바타를 설정하지 않은 사용자의 경우 존재하지 않는 반면, avatar_template은 Discourse에서 해당 사용자의 아바타를 표시할 때 실제로 사용되는 leter_avatar_proxy 경로를 포함합니다. 또한 아바타를 설정한 사용자의 경우, avatar_url은 원본 아바타 이미지 자체를 가리키며 원하는 크기로 조정된 이미지를 가리키지 않습니다.

sso에 포함된 아바타 정보를 사용하려는 모든 사람이 원할 것은 avatar_template인 것 같습니다. 하지만 그렇게 되면 이를 가져오기 위해 추가적인 API 요청을 해야 합니다.

2개의 좋아요

현재 내 앱에 SSO를 구현하고 있습니다. 로그인까지는 잘 작동하고 있지만, 로그아웃은 그렇지 않습니다.

내 Discourse 인스턴스가 sso나 sig 파라미터 없이 return URL로 올바르게 리다이렉트되고 있지만, Discourse를 열면 내 계정이 여전히 로그인된 상태입니다.

혹시 아이디어가 있으신가요?

Discourse의 logout redirect 사이트 설정을 사용하여 사용자가 Discourse에서 로그아웃한 후 애플리케이션으로 다시 리디렉션하는 중이라고 가정합니다.

이 문제의 가능한 원인은 Discourse 사이트에 login required 설정이 활성화된 경우입니다. 이 설정이 활성화되면 Discourse는 인증되지 않은 사용자가 Discourse 사이트로 직접 이동할 경우 SSO 제공자 사이트로 자동으로 리디렉션합니다. 즉, 사용자가 처음 logout redirect URL로 리디렉션될 때 애플리케이션에서 로그아웃 처리를 하지 않는 한, 사용자가 사이트를 다시 방문할 때 자동으로 Discourse에 로그인됩니다. 브라우저의 인스펙터를 네트워크 탭으로 열어 두었다가 이 과정을 거치면 이 동작을 확인할 수 있습니다.

도움이 될 경우, WP Discourse 플러그인이 Discourse 로그아웃 리디렉션을 처리하는 방식은 다음과 같습니다: wp-discourse/lib/sso-provider/discourse-sso.php at main · discourse/wp-discourse · GitHub.

2개의 좋아요