인증으로 그룹 멤버십 관리

@david 이 논의는

에서 이어지는 것입니다.

추측하시겠지만, 저는 openid-connect 플러그인에 국한되지 않는 이 문제의 안정적인 해결책도 보고 싶습니다. 앞으로 나아갈 방법을 함께 찾아보겠습니다.

제가 언급한 점을 이어가 보겠습니다.

사용자가 어떤 그룹에 추가되고 제거되는지 어떻게 결정하나요? 그리고 이것이 Discourse 자체에서 사용자를 그룹에 수동으로 추가/제거하는 데 어떤 영향을 미치나요?

  1. 사용자가 ['group1', 'group2'] 그룹으로 OIDC 인증을 수행합니다.
  2. Discourse UI에서 사용자는 group3에 추가됩니다.
  3. 나중에, 같은 사용자가 ['group1'] 그룹으로 OIDC 인증을 수행합니다.

이를 사람이 검사해 보면, 최종 상태는 group1, group3(group2는 제거되어야 함)여야 한다는 것을 알 수 있습니다. 하지만 프로그래밍 방식으로 그 결정을 내릴 만큼 충분한 상태가 추적되고 있다고 생각하지 않습니다. 마찬가지로:

  1. 사용자가 ['group1', 'group2'] 그룹으로 OIDC 인증을 수행합니다.
  2. Discourse UI에서 사용자는 group1에서 제거됩니다.
  3. 나중에, 같은 사용자가 ['group1', 'group2'] 그룹으로 OIDC 인증을 수행합니다.

이제 어떻게 되어야 하나요? 관리자가 명시적으로 사용자를 group1에서 제거했는데, OIDC가 다시 추가해 버렸습니다. 이는 관리자 입장에서 매우 혼란스러운 UX입니다. 그룹을 '외부 관리’로 식별하고 모든 추가/제거 UI를 숨기는 방법이 필요할 수도 있습니다 :thinking:

이 문제를 처리할 수 있는 몇 가지 방법이 있다고 생각합니다 (서로 배타적이지 않음)

그룹 및 토큰 속성 식별

어떤 구현 방식이든, 다음 사항을 명시적으로 식별해야 합니다.

  1. 인증을 통해 멤버십을 관리할 수 있는 그룹은 어떤 것인지; 그리고
  2. 인증 토큰 내에서 그룹 멤버십을 결정하는 속성은 어떤 것인지

이는 사이트 설정, 그룹 설정 또는 기타 방식으로 이루어져야 하며, 기본값은 "꺼짐"이어야 합니다.

엄격하거나 관대한 처리

그룹이 식별된 토큰 속성에 없으면 사용자가 제거되는 경우 처리 방식은 "엄격(strict)"합니다. 그룹이 식별된 토큰 속성에 없어도 사용자가 제거되지 않는 경우 처리 방식은 "관대한(permissive)"입니다.

엄격/관대한 상태는 그룹별로 설정됩니다. 사이트 설정에서 이를 수행하는 방법에 대한 예시는 여기 있습니다: Conslidate group settings into group claim map - discourse/discourse-openid-connect@1665bdc - GitHub. 동일한 접근 방식을 그룹 설정을 통해서도 수행할 수 있습니다.

제안하신 대로, 처리 방식이 "엄격"이라면 그룹 관리자를 통한 그룹 멤버십 추가/제거를 비활성화할 수 있습니다.

멤버십 출처 식별

또한 group_users 테이블에 그룹 멤버십의 출처를 저장하여 그룹 내에서 "혼합"된 접근 방식을 허용할 수도 있습니다. 즉, 관리자가 인증 토큰을 통해 생성된 멤버십을 제거할 수 없도록 하는 것입니다.

이 문제에 대해 더 생각할수록, 이는 그룹 설정을 통해 수행되어야 한다고 생각합니다.

이 작업을 시작해 주셔서 감사합니다, @angus! 이 기능을 원하는 분들이 많다는 걸 잘 알고 있습니다!

흥미롭네요! 저는 항상 인증 과정에서 그룹이 자동으로 생성되고, 그룹 이름이 IDP(Identity Provider)의 그룹 이름과 1:1로 매핑될 것이라고 상상했습니다. 현재 DiscourseConnect가 작동하는 방식이 바로 그런 방식입니다.

하지만 저는 이 더 명시적인 옵션을 훨씬 더 좋아합니다! 이렇게 하면 IDP에서 불필요한 그룹들이 사용자의 Discourse 인스턴스를 오염시키는 것을 방지할 수 있고, 관리자가 원하는 대로 그룹 이름을 사용자 정의할 수 있습니다. 기존 이메일 도메인 기반 멤버십 관리 방식과도 일관성이 있습니다.

기술적인 관점에서는 좋은 아이디어입니다. 제 유일한 우려는 사용자가나 관리자에게 설명하기가 어려울 수 있다는 점입니다. 만약 '인증 관리 그룹의 명시적 식별’이라는 아이디어를 따르는 것이라면, 단순히 ‘엄격(strict)’ 처리만 구현해도 되지 않을까요?

그룹이 인증 관리 대상으로 설정되면, 수동으로 추가/제거하는 기능은 큰 경고 메시지로 숨겨지고, 설명하신 ‘엄격’ 모드처럼 작동합니다.

어떻게 생각하시나요?

부연 설명: 사용자가 자동으로 추가/제거되는 모든 작업이 그룹 로그에 기록되도록 해야 합니다. 이렇게 하면 모든 사용자가 어떤 일이 일어나고 있는지, 그리고 왜 그런지 이해하기 쉬워집니다.

네, 적어도 이 기능 세트의 v1에서는 명시적으로 설정하는 것이 더 좋다고 생각합니다. 아직까지 자동 생성이 정말로 필요한, 즉 클레임(claims)이 처리되기 전에 사이트 관리자가 그룹을 설정하고 구성할 수 없었던 사용 사례를 만나본 적이 없습니다.

명시적 버전의 구현을 완료한 후, 자동 생성을 옵션으로 추가하는 방향으로 전환할 수 있을까요?

그룹 설정(Group Settings)의 경우 다음과 같은 형태가 될 것입니다.

설정 섹션: 멤버십 (기존 섹션)
설정 그룹 제목: 인증 관리 (Authentication Management)
설정:

  • 서비스: 인증 서비스 목록. "전체(All)"가 옵션으로 포함됩니다. 이 설정은 동시에 이 기능 세트의 “활성화/비활성화” 상태 역할을 합니다. 즉, 기본값은 "없음(None)"입니다.
  • 클레임(claim): id 토큰 클레임을 식별하기 위한 텍스트 입력 필드. 이 기능의 “설명”(아마도 메타(meta)의 게시물에서)에서는 지원되는 형식(예: 불리언, 쉼표로 구분된 문자열 등)을 설명할 것입니다.
  • 모드: 아래 참조

네, 엄격/관대(strict/permissive) 설정이 있다면 이를 간결하게 설명해야 합니다. 그 접근 방식은 "추가(addition)"와 "제거(removal)"에 초점을 맞추는 것이라고 생각합니다. 다음과 같이 설명할 수 있을 것입니다.

설정: “모드”

옵션 1 (관대, permissive):

  • 라벨: “멤버 추가”
  • 설명: “인증 시 이 그룹에 멤버가 추가되도록 허용”

옵션 2 (엄격, strict):

  • 라벨: “멤버 추가 및 제거”
  • 설명: “인증 시 멤버가 추가되고 제거되도록 허용. 이 경우 수동 멤버십 제어 기능이 비활성화됩니다.”

“관대” 옵션을 유지하려는 이유는, 이전에 클라이언트와 이러한 작업을 수행할 때 이것이 가장 흔한 사용 사례이기 때문입니다. 즉,

  • 주요 필요 사항은 외부 서비스의 상태에 따라 그룹 접근 권한을 허용하는 것입니다.

  • 외부 서비스의 상태에 따라 접근 권한을 제거해야 하는 필요성은 더 주변적인 것입니다. 즉, 사람들이 접근 권한을 잃고 제거되어야 하는 경우는 있지만, 상대적으로 중요도가 낮습니다.

  • Discourse에서 멤버십을 수동으로 제어할 수 있는 기능을 유지하려는 욕구가 종종 존재합니다. “엄격” 방식을 구현했을 때, 설명에도 불구하고(기술적으로는) 올바르게 작동하고 있음에도 불구하고 "왜 X님이 멤버십을 잃었나요?"라는 “놀라움”(surprising) 반응이 있었습니다.

네, 이것은 중요합니다. 특히 엄격 모드에서 누군가가 추가되거나 제거되는 이유에 대해 사람들이 자주 궁금해할 것이며, 우리는 (즉, "Discourse"는) 외부 인증 서비스가 만드는 클레임의 정확성에 대해 통제할 수 없기 때문입니다.

아마도 해당 작업을 수행한 인증 서비스를 포함하는 "사용자 제거(Remove User)"와 "사용자 추가(Add User)"라는 새로운 유형의 작업을 추가할 수 있을 것입니다. 예를 들어 "사용자 제거 ([서비스 이름])"처럼요.

여기서 또 하나의 함정은, 이 기능이 "특정 그룹의 멤버십을 외부 서비스 X에 기반하고 싶다"는 사용 사례에 대한 만능 해결책은 아니라는 점을 명확히 해야 한다는 것입니다. 사람들은 그렇게 자주 인증을 하지 않기 때문이지요. 적어도 그 유형의 표준 사용 사례가 요구하는 수준에서는 그렇습니다.

이(인증 관리)는 해당 유형의 사용 사례를 처리하는 데 필요한 필수 요소이지만, 이를 제대로 지원하려면 이벤트 기반 통합, 예를 들어 웹훅 수신기(그룹 관리를 위해 설계된 사설 웹훅 수신기 플러그인을 하나 보유하고 있으며, 머지않아 오픈소스로 공개할 계획입니다)를 설정하는 것도 필요합니다.

@david 이거 어떻게 생각하세요? PR을 만들어서 올려도 될까요?

여기서 정말 가치 있는 일을 하고 계시네요! :sunflower:

기능 비교 차원에서, Global Legal Empowerment Network에서 어떻게 하는지 공유해 보려고 합니다. 저희는 워드프레스와 디스커스 WP 플러그인을 사용하고 있으며, 워드프레스에서 사용자 정보가 업데이트될 때마다 디스커스에서도 자동으로 동기화되도록 설정해 두었습니다. 여기에는 특수 그룹(예: 핵심 멤버, 리소스 기여자 등)과 프로필 세부 정보도 포함됩니다. 문제 해결과 정상 작동 여부를 확인하기 위해 '마지막 업데이트’라는 숨겨진 사용자 필드를 추가했습니다.

원격으로 관리되는 해당 그룹은 디스커스에서 사용자가 직접 가입하거나 탈퇴할 수 없도록 잠가 두었지만, 스태프에 의한 그룹 멤버십 관리를 제한할 필요는 없다고 판단했습니다. 위에서 시도하고 계신 방식의 외형은 좋아 보이지만, 솔직히 제 역량에는 조금 벗어난 것 같습니다.

디스커스 포 팀즈 고객 중에는 Okta를 사용하여 회사 앱 전체의 접근 권한을 능동적으로 관리하는 곳들이 있습니다. 이러한 고객들은 Okta에서 역할(Role)을 설정해 두며, 이를 통해 예를 들어 Microsoft Tableau에 대한 특정 수준의 접근 권한을 부여하는 등 지원합니다. Okta는 디렉터리 역할도 수행하므로 사용자 아바타, 자기소개, 위치 등 기타 프로필 유형 정보도 관리합니다. 이들은 이러한 프로필 정보도 Okta 외부에서 팀즈에 업데이트되기를 원하고 있습니다.

가능하다면 초기술적인 내용은 그룹 설정에서 제외하는 것이 좋습니다. 구문 문서를 링크해야 한다는 것은 그것이 단순화되거나 관리자 사이트 설정으로 이관되어야 함을 의미할 가능성이 큽니다. 이는 매우 다양할 수 있으므로 해당 부분의 구현은 각 인증 플러그인에 맡기는 것이 좋다고 생각합니다.

OIDC를 예로 들면, 해당 플러그인은 openid_connect_roles_claim이라는 새로운 사이트 설정을 추가합니다. Okta를 사용하는 경우, 관리자는 이를 groups로 설정합니다. 문자열 배열은 OIDC에서 상당히 표준적인 형식이지만, 정말로 필요할 경우 더 복잡한 옵션을 탐색해 볼 수 있습니다.

이 정보를 수신하기 위해 Auth::Result에는 간단한 문자열 배열을 받는 새로운 roles 속성이 부여됩니다. 코어는 이를 가져와서(Auth::Result#apply_user_attributes! 안의) 컬럼(provider_name, user_id, role)을 가진 새로운 UserAssociatedRoles 테이블에 로드합니다. 이 UserAssociatedRoles 테이블은 OP에서 언급한 '멤버십 출처 식별’을 제공합니다.

그룹 설정은 다음과 같이 보일 것으로 예상합니다:

역할 이름은 provider_name으로 접두사가 붙습니다. 자동완성은 UserAssociatedRoles 테이블에 있는 기존 값 전체를 기반으로 하지만, Discourse가 아직 해당 역할을 확인하지 못한 경우를 대비하여 자동완성되지 않은 값도 허용됩니다.

데이터베이스에 완전한 user_associated_roles 테이블을 갖추는 아름다움은 관리자가 Discourse 그룹으로 무엇이든 할 수 있으며, 사용자가 다시 로그인할 필요 없이 멤버십이 즉시 업데이트된다는 점입니다.

그렇습니다. v1의 경우 가능한 한 단순하게 유지하는 것이 최선이라고 생각하므로 여러 '모드’를 가지고 싶지 않습니다. 기본 동작으로 다음을 어떻게 생각하시나요:

  • 인증 제공자에서 일치하는 역할을 가진 경우 멤버 추가
  • 해당 역할이 사라지면 멤버 제거
  • Discourse에서도 추가/제거 허용
  • 관리자가 인증 제공자를 통해 추가된 사용자를 제거하려고 하면 "이 사용자는 다음 로그인 시 다시 추가될 수 있습니다"라는 경고 표시

여기서는 기본적으로 안전을 확보하려는 시도를 해야 한다고 생각하며, 식별자 제공자에서 역할을 잃으면 멤버를 제거해야 합니다. 멤버십 로그 개선으로 인해 현재 상황보다 혼란이 적을 것으로 기대합니다.

정말 원하지 않는 사이트의 경우, remove_group_membership_when_auth_role_lost(기본값 true)와 같은 사이트 설정을 가질 수 있습니다.

네, 당연히 그렇습니다. 이름, 아바타 등 다른 사용자 메타데이터에서도 이러한 문제가 있습니다. 머지않아 다른 인증 플러그인을 위한 sync_sso 동등물을 만드는 것을 살펴봐야 할 것 같습니다. 이는 일반적으로 OIDC / SAML 등을 통해 전달되는 모든 정보, 그리고 이 새로운 ‘역할’ 정보를 포함하여 전달될 수 있습니다. 아마도 별도의 프로젝트가 될 것입니다.


@angus, 이것이 어떻게 들리시나요? 별도의 엄격/허용 모드가 있는 것보다 조금 덜 유연하지만, 하나의 모드만 있으면 문제 해결 / 문서 / 지원이 훨씬 쉬워질 것이라고 생각합니다.


이 계획에 따라, 관리자 관점에서 고수준으로 보았을 때 다음과 같습니다:

초기 설정

  1. Okta 인증을 설정하고 Okta 측에서 groups claim 활성화

  2. Discourse에서 openid_connect_roles_claim을 groups로 설정

새 그룹 설정

  1. 평소처럼 discourse 그룹을 생성합니다. 이름 / 전체 이름 / flair 등을 원하는 대로 구성

  2. ‘멤버십’ 설정으로 가서, SSO 역할 드롭다운에 커서를 놓고 드롭다운에서 역할을 선택합니다. Discourse가 아직 해당 역할로 로그인한 사람을 본 적이 없다면 수동으로 입력해야 합니다

    하나의 Discourse 그룹에 여러 역할을 지정할 수 있습니다. 예: Discourse의 ‘팀’ 그룹은 oidc:employees와 oidc:contractors의 조합일 수 있습니다

  3. 저장 버튼을 누르면, Discourse가 이전 로그인에서 캐시한 역할 정보로 그룹 멤버십이 즉시 업데이트됩니다.

  4. 향후 로그인 시, 식별자 제공자 역할에 대한 모든 변경 사항이 Discourse 그룹에 반영됩니다

  5. 여전히 네이티브 Discourse UI에서 그룹에 사용자를 추가/제거할 수 있습니다

    • 식별자 제공자를 통해 추가된 사용자를 제거하려고 하면, 해당 사용자가 다음 로그인 시 다시 추가될 수 있다는 경고가 표시됩니다
  6. 나중에 해당 IDP 역할을 그룹과 연관시키고 싶지 않다고 결정하면, 이를 제거할 수 있으며, 해당 역할을 통해 멤버였던 모든 사용자가 제거됩니다

동의합니다.

음, 네. 하지만 부울(boolean) 클레임을 지원하는 것이 의미가 있는 경우가 있습니다. 하지만 네, 지금은 문자열 배열만 지원하도록 단순하게 유지하는 것에 동의합니다.

인증기별로 구현한다고 가정하면, Auth::Authenticator에도 group_sync_enabled? 메서드가 있을 것이며, 이는 개별 인증기에서 오버라이드되고, 일반 인증기에서는 관련 설정에 값이 존재하는지에 따라 동작할 것입니다.

이것은 흥미로운 질문을 제기합니다. 이는 Facebook(“groups”?), Google(“groups”?), Discord(“roles”?)와 같은 특정 서비스 인증기에서도 사용될 수 있습니다. 그들의 ID 토큰에 이러한 정보가 포함되어 있는지 확실하지 않지만, 기술적 설계 관점(즉, 추후에 이 기능을 추가할 수 있는 능력)에서 고려해 볼 가치가 있을 수 있습니다. 다만 v1에서는 주요 관심사가 아니라고 생각합니다.

해당 메커니즘은 괜찮아 보이지만, 왜 여기서 “groups” 대신 "roles"를 사용하도록 변경되었는지 궁금합니다. 큰 문제는 아니지만, 제가 놓치고 있는 것이 없는지 확인하고 싶습니다.

UX 관점에서는, 유용한 기술적 구분에 기반하더라도 "roles"와 "groups"가 동시에 사용되는 용어 사용으로 인해 혼란이 발생할 수 있다고 봅니다. 또한 이러한 배경 지식이 없는 개발자들도 혼란스러워할 수 있습니다.

별개의 테이블은 좋지만, provider_name 대신 user_associated_accounts와 외래 키 연관 관계를 가져야 하지 않을까요? 정리 작업 등에 유용할 수 있습니다. 그 질문에 대한 답은 일부로, 제품 관점에서 그룹/역할 연관 관계를 연관된 계정과 얼마나 긴밀하게 연결하고 싶은지에 달려 있다고 생각합니다. 지금 두 가지를 연결하는 데 어떤 단점이 있을까요?

** 수정: user_id를 통해 이미 연관 관계가 있는 것 같습니다.

괜찮아 보입니다. 다만 제안하신 사이트 설정을 통해 프로바이더별 기반으로도 이 정보를 가지고 있으므로, 역할이 아직 확인되지 않은 경우를 포함하여 이를 커버할 수 있을 것 같습니다. 여기서 Discourse.authenticators를 사용할 방법이 있을지 모르겠습니다. 즉, 프로바이더와 그들의 클레임의 메모리 내 목록 같은 것이요.

좋은 타협점이라고 생각합니다. 특히 아래에서 언급된 사이트 설정도 함께 있다면 더 그렇습니다.

플러그인(즉, 인증기) 단위로 말씀하시는 건가요? 그렇다면 동의합니다. v1에서 그 요구 사항을 충족하기에 충분할 것입니다.

사용자 플로우에 대한 좋은 요약입니다 :+1: 제가 제안한 비교적 사소한 사항들을 제외하고는 방향성에 동의합니다.

:+1:

네, 당연히요. 특히 Google을 생각하고 있었습니다. 사람들이 GSuite 조직에서 Google을 많이 사용하는데, GSuite에는 그룹이라는 개념이 있을 것으로 추측하기 때문입니다.

"Identity Provider Groups"와 “Discourse Groups” 사이의 혼란을 피하려던 것 같습니다… 하지만 이름을 변경하면서 오히려 더 혼란을 야기했을 수도 있습니다. 여기서는 "groups"를 유지하는 데 전혀 문제가 없습니다.

저희는 여전히 관리되지 않는 인증기(authenticator) 인증 제공자를 지원하므로, user_associated_account 레코드가 없을 수 있다고 생각했습니다. 말씀하신 대로 user_id를 통해 여전히 조인할 수 있습니다 :+1:

사이트 설정이 여기서 도움이 될지 확실하지 않습니다. "roles"를 나타내는 문자열 배열에 대해 이야기하고 있다면, 사이트 설정은 역할이 실제로 무엇인지 지정하지 않습니다. 배열을 어떻게 가져올지 지정할 뿐입니다. 이해되시나요?

훌륭합니다 :smiley:

네, 맞습니다. 저는 설정 자체에 특정 그룹을 포함했던 이전 구현 방식을 생각 중이었지만, 여기서는 그렇게 하지 않는 것이므로 문제없습니다.

PR 작업을 시작하기에 충분한 정보가 있다고 생각합니다. 이번 주 후반에 시작할 예정인데, 혹시 남은 우려 사항이 있으신가요? 진행 중 문제가 발생하면 여기서 업데이트하겠습니다.

정말 좋네요 - 진행 과정에서 발생하는 문제나 질문이 있다면 기꺼이 도와드릴게요 :slight_smile:

드디어 공유할 수 있는 진행 중인 작업이 생겼습니다.

몇 가지 참고 사항이 있습니다.

초기 구현에서는 구글 앱스 HD(HD) 그룹이라는 다소 복잡한 경우를 선택했습니다. 이를 통해 프로바이더의 도메인별 그룹과 같은 다양한 조합을 고려하는 데 도움이 된다고 생각했기 때문입니다.

이 사용 사례를 구현하기 위해 인증 시점에 점진적 인가(incremental authorization)를 허용하기 위해 '2차 인가(secondary authorization)'라는 새로운 개념을 도입해야 했습니다. 특정 사용자의 그룹 권한(예: HD로 인증하는 경우)을 요청하는 몇 가지 다른 구현 방식을 고려해 보았으나, 이 방식이 가장 실현 가능해 보였습니다. 이 부분은 예상보다 큰 변경 사항일 수 있지만, 논의할 가치가 있다고 생각합니다.

구글 HD 그룹 경우를 구현하려면, 구글 앱스 HD 그룹의 비관리자 멤버가 자신의 그룹 목록을 가져오기(관리자 디렉터리 API를 통해) 위해 위임된 관리자 권한을 부여해야 합니다. 실제로 이 용도에 잘 맞는 "Groups Reader"라는 “베타” 단계의 사전 구축 관리자 역할이 있습니다. Prebuilt administrator roles  |  User management  |  Google Workspace Help 참고하세요.

구글 구현은 정상적으로 작동합니다. 설정을 마친 후 HD로 인증하면, 자동 그룹 멤버십 설정에서 구글 HD 그룹을 사용할 수 있게 됩니다. 해당 HD 그룹이 선택되면 사용자는 해당 Discourse 그룹에 추가되고, 제거되면 그룹에서도 제외됩니다(두 동작 모두 구체적인 내용과 함께 기록됩니다). 또한, 이후 해당 구글 HD 그룹에서 인증하는 사용자는 즉시 추가됩니다.

세부 사항은 코드와 스펙에서 확인하실 수 있습니다. 또한 3개의 새 테이블을 추가했다는 점도 눈치채실 것입니다. 몇 가지 더 ‘가벼운’ 솔루션을 시도해 보았지만, 사용자의 관련 그룹과 그룹의 관련 그룹 업데이트를 처리할 때 각각 더 복잡하고 비효율적인 결과로 이어졌습니다. 각각에 대해 새 테이블을 생성하는 것을 피하기는 어렵습니다. 데이터 모델링 측면에서, 그리고 더 일반적으로 아이디어를 환영합니다.


남은 기술적 할 일(위에서 언급된 개념적/제품 관련 질문 제외)은 다음과 같습니다. 이 부분에도 제안이 있다면 환영합니다:

  • 관련 그룹의 label을 직렬화(serialize)하는 것(클라이언트에서 모델링하는 대신).
  • 누락된 스펙과 qunit 추가
  • 대규모 데이터에서 지연이 발생할 수 있으므로 user_associated_group / group_associated_group 생성/소거를 백그라운드 작업으로 이동하는 것.

정말 멋져 보입니다!

'2차 인증’과 provider_domain 컬럼에 대해서는 조금 망설여집니다. 왜 필요한지 좀 더 자세히 설명해 주실 수 있나요? 구글에 특화된 것 같아서… 첫 번째 인증 요청 시 admin.directory.group.readonly 스코프를 요청할 수 없는 이유가 있나요? 그리고 그룹 이름 앞에 도메인을 접두사로 붙이는 것(아니면, 사람들이 단일 구글 '호스티드 도메인’만 사용할 것이라고 가정하므로 도메인을 완전히 제외하는 것)은 어떨까요?

여기서는 테이블 3개에 대해 전혀 문제없습니다. 오히려 구조를 더 깔끔하게 유지해 줍니다.

동의합니다 :+1:

여기서는 주의가 필요합니다. 모든 그룹 멤버십은 사용자가 사이트를 처음 로드하기 전에 할당되어야 합니다. 그렇지 않으면 사용자가 처음 로그인할 때 그룹 관련 항목(예: 보안 카테고리)을 볼 수 없습니다. 따라서 user_associated_group 레코드 변경은 동기적으로 처리되어야 한다고 생각합니다.

하지만 group_associated_group 레코드 변경의 경우, 말씀하신 대로 맞습니다.那里的 변경은 수천 명의 사용자에게 영향을 미칠 수 있으므로 in_batches로 처리해야 합니다. 저는 먼저 UI에 로딩 스피너를 표시하면서 동기적으로 처리하는 것으로 시작하겠습니다. 이렇게 하면 관리자가 작업이 실행 중인지 완료되었는지 명확히 확인할 수 있습니다.

30초(유니콘 요청 타임아웃)에 근접한다면 백그라운드 작업에 대해 고려해 볼 필요가 있을 것입니다.

여기에 분산 뮤텍스(DistributedMutex) 잠금도 추가해야 할지 고려해 볼 필요가 있습니다. 예를 들어, group_associated_group 변경이 처리되는 동안 사용자가 로그인하면 어떻게 될까요? 전체 아키텍처가 최종 확정되면 GitHub에서 이런 종류의 주제에 대해 논의하는 것을 환영합니다.

관심 있게 지켜보고 있습니다 — 관련 내용으로 이 링크를 공유합니다: Does `sso overrides groups` work with Oauth2?

제 사용 사례에서는 다른 인증 제공자에도 Discourse Connect 동작이 구현되면 완벽하게 만족하겠습니다.

좋네요 :slight_smile: 점점 잘 되고 있어요.

각 항목에 대해 차례대로 설명드리겠습니다.

점진적 인증 (Incremental authorization)

첫 번째 요청 시 그룹 권한을 요청할 수 없는 이유는, 누가 로그인하는지 또는 사용자가 어떤 정보 공유를 허용하는지 알 수 없기 때문입니다. google_oauth2_hd 설정을 사용하는 사용자로 Google 관련 그룹 매핑을 제한할 수는 있지만, 이렇게 하면 기능의 범위가 상당히 제한됩니다. Google Apps를 사용하는 팀과 함께, Google 인증을 사용하려는 ‘공개’ 사용자들도 있는 경우는 비교적 흔합니다.

*수정: 명확히 하자면, 그룹 스코프를 요청했는데 사용자가 이를 부여할 수 없는 경우(예: 위에서 설명한 대로 HD가 비관리자 사용자에게 권한을 위임하지 않은 경우)에는 인증이 실패합니다. 필수 스코프와 함께 선택적 스코프를 요청할 수 없습니다.

또한, 이러한 접근 방식, 즉 다양한 인증 방법에서 이를 구현할 때 표준 관행으로 사전에 그룹 스코프를 요청하는 방식은 대안보다 오히려 더 Google 특화적이라고 할 수 있습니다. 로그인을 제한하기 위한 호스티드 도메인 시스템과 동일한 개념이 항상 존재하지는 않기 때문입니다. 예를 들어, 제 생각이 틀리지 않았다면, GitHub OAuth2 로그인을 특정 GitHub 조직으로 제한하는 방법은 없는 것 같습니다.

즉, 많은 경우 해당 인증 방법을 사용하는 모든 사람에게 관련 groups 스코프를 부여하도록 요청하거나, 아니면 해당 기능을 사용하지 않는 것 중 하나를 선택해야 합니다. 이러한 접근 방식은 일부 환경에서는 작동할 수 있지만, 많은 환경에서는 그렇지 않습니다. 이 점진적 인증 접근 방식은 다양한 인증 방법이 기능을 구현하는 데 더 많은 유연성을 제공합니다.

Google이 OAuth2 분야에서 점진적 인증을 적극 추진해 왔다는 것은 사실입니다. 예를 들어, 해당 주제에 관한 작업 논문들은 모두 Google 직원이 작성했습니다.

그러나 이 개념은 Google 특화적이진 않습니다(다른 사람들은 이를 ‘점진적 인증’이라고 부르지 않을 수도 있지만). 모바일 앱에서 다양한 형태로 비교적 흔하며, 다른 제공자들도 OAuth2에서 이를 채택하고 있습니다. 예를 들어, Facebook의 동일한 주제에 대한 문서를 참조할 수 있습니다.

이미 해당 분야에 익숙하시겠지만, 다음과 같은 것이 ‘좋은 관행’으로 여겨집니다.

  • 표준적인 기본 정보보다 더 많은 권한을 요청하려 한다는 것을 사용자에게 알리는 것;
  • 그리고 가능하면 그 이유를 설명하는 것.

사용자가 Discourse 로그인 폼에서 "Facebook으로 가입"을 클릭한 후, 이메일 외에도 Facebook 그룹에 대한 접근 권한을 요청받으면 이탈할 수 있습니다. Facebook은 이를 다음과 같이 표현합니다.

일반적으로 앱이 요청하는 권한이 많을수록 사람들이 Facebook으로 앱에 로그인할 가능성이 낮아집니다. 실제로 우리의 연구에 따르면, 4개 이상의 권한을 요청하는 앱은 완료된 로그인 수가 현저히 감소하는 것으로 나타났습니다.

이는 인증 시점에 추가 스코프를 요청하는 것이 좋은 아이디어인지에 대한 질문을 제기합니다. 기본적으로는 다른 좋은 대안이 거의 없다는 결론에 도달했습니다. 일부 시나리오에서는 그룹에 대한 즉시 접근이 필요하기 때문(앞서 언급하신 대로)이며, 사전에 요청하지 않으면 많은 사용자가 프로필이나 그룹 페이지에서 그룹을 승인하기 위한 추가 단계를 밟지 않을 것이라는 현실도 있기 때문입니다.

이는 인증 시점에 이루어져야 하며, 이는 위에서 언급한 문제로 되돌아오게 되며, 제가 “secondary authorization” 시스템을 구현한 이유입니다. 이는 다른 서비스, 예를 들어 Facebook이나 Github가 인증 후 사용자의 그룹에 대한 접근 권한을 얻기 위한 secondary authorization 요청을 구현하는 것이 비교적 쉽다는 점에서, 경량 '시스템’으로 의도된 것입니다. 그리고 그들의 기본 정보와 관련된 특정 테스트를 통과하는 경우 선택적으로 이를 수행할 수 있습니다.

각 제공자는 다음을 수행하기만 하면 됩니다.

  • secondary_authorization_url이 포함된 결과를 반환

  • state 매개변수를 사용하여 사용자가 어떤 인증 요청 단계에 있는지 감지

  • users/omniauth_callbacks/secondary_authorization.html.erb를 위한 omniauth_secondary_authorization_description을 제공. 예를 들어, 이것은 Google용 것으로, 사용자가 secondary authorization 리다이렉트를 확인하기 전에 볼 수 있습니다.

    %{domain} 이메일로 로그인하셨으므로, %{domain} 그룹을 볼 수 있는 권한을 요청해야 합니다.

이 중 어느 부분도 Google 특화적이지 않습니다.

여기서 제가 하고 싶은 것은 사용자가 secondary 요청에 "아니오"라고 말하더라도 인증을 계속할 수 있도록 하는 것입니다. Google Apps HD 시나리오에서는 이것이 큰 문제가 되지 않습니다. 계정이 호스티드 도메인의 일부라면, 사용자가 '아니오’라고 말하고 싶거나 그렇게 할 위치에 있지 않을 가능성이 높기 때문입니다. 그러나 여기서는 다양한 인증 시나리오를 허용하기 위해 이를 수용해야 한다고 생각합니다.

마지막으로, secondary authorization이 associated_groups이 작동하는 데 _필수적_은 아니라는 점도 주목해야 합니다. 인증 제공자는 사전에 groups 스코프를 요청하고, 첫 번째 응답을 받은 후 인증 결과에 그룹을 추가할 수 있습니다. 실제로, 기본 oauth2 및 openid connect 플러그인에 이를 옵션으로 구축하는 것이 좋을 것입니다.

Provider Domain

associated_groups 테이블에는 사이트 관리자가 읽을 수 있는某种 형태의 보조 식별자가 필요하다고 생각합니다. 그룹 이름만으로는 충분하지 않을 수 있는 여러 시나리오가 있습니다. 각 서비스의 동등한 개념 간 이름 충돌의 가능성이 있습니다. 예를 들어:

  1. 멀티 도메인 Google 그룹 관리 (하나의 워크스페이스에 여러 도메인을 가질 수도 있음)
  2. 멀티 조직 GitHub 그룹 관리
  3. 멀티 서버 Discord 역할 관리
    등등

domain을 namespace로 변경할 수도 있습니다. 그룹의 name에 이를 포함할 수 있지만, 그렇게 하면 어떤 이점이 있을까요? 나중에 “domain” 또는 "namespace"로 쿼리를 실행하는 데 유용할 수 있습니다. 네, 아마 namespace가 domain보다 더 적합할 것입니다.

이것이 “관리자가 읽을 수 있는” 형태여야 하는 이유는, 그룹 UI에서 관리자가 보는 라벨에 사용되며, 일부는 명료화 목적을 위해서입니다.

여기서 provider_id(존재한다면)를 저장하는 것이 의미가 있는지 고민하고 있습니다. 나중에 유용할 수 있습니다.

네, 모두 동의합니다. 팁을 주셔서 감사합니다. 이 부분을 제가 시도해 보고, PR에서 더 논의해 보겠습니다.

@david 이 PR에 몇 가지 업데이트를 적용했습니다.

  • group_associated_group에 DistributedMutxes와 in_batches 추가
  • 인수 테스트 (이미 rspec이 있었음)

아직 더 많은 작업이 필요할 것 같지만, 현재는 사양에 맞게 작동하며 모든 테스트가 통과하고 있습니다. 직접 테스트해 보고 의견이나 원하시는 변경 사항이 있으면 알려 주세요.

일단 초안이 아닌 것으로 표시하는 것이 좋을까요?

@angus님, 안녕하세요! 이 부분에 대해 더 진전이 있었는지 궁금합니다. 제가 이해하기로는, 단순한 “strict” 동작 방식에 매우 관심이 있습니다. 또한 우리는 Oauth2/OpenID connect 제공자를 직접 관리하고 있으므로 “secondary authorization” 사례에 대해서는 걱정하지 않습니다. 이와 유사한 기능이 좀 더 빨리 반영될 수 있을까요?

도움이 된다면, 저희 환경은 여기에 문서화되어 있습니다: Infrastructure/Authentication - Fedora Project Wiki 그리고 저는 DIscourse를 oauth2 scope openid profile email https://id.fedoraproject.org/scope/groups를 요청하도록 설정했습니다.

결국 제가 원하는 것은 다음과 같습니다:

  • 신뢰 수준(trust level)과 스태프 그룹은 그대로 두기
  • SSO에서 가져온 목록에 있는 기존 Discourse 그룹 중 사용자가 아직 속해 있지 않은 그룹에 추가하기
  • 사용자가 속해 있긴 하지만 목록에 없는 그룹에서 제거하기

솔직히 모든 세부 사항을 완벽하게 이해하지는 못합니다… 제가 놓치고 있는 복잡한 부분이 있을까요?

Matt, 이번 주말에 이 작업에 시간을 할애할 거야. 다음 주에 업데이트를 공유할 예정이고, 아마 GitHub의 PR로 올릴 것 같아.

정말 좋습니다 — 정말 감사합니다. 계속 물어보는 건 아니지만, Discourse 지원팀에서 이 주제에 게시하는 것이 현재 상황을 파악하는 가장 좋은 방법이라고 조언해 주셨거든요. :slight_smile:

이 작업을 진행해 주셔서 정말 기대됩니다. 이 기능이 갖춰지면 현재는 할 수 없었던 것들을 Fedora Discourse 사이트에서 훨씬 더 많이 할 수 있을 테니까요!