ygramoel
(Johan Cockx)
1
현재 정상적으로 작동하는 Discourse 포럼이 있습니다. 여기서 사용자는 로컬 로그인(사용자 이름 + 비밀번호)을 생성할 수 있습니다.
다른 애플리케이션에서도 이 로그인 정보(사용자 이름과 비밀번호)를 재사용하고 싶습니다. 즉, 사용자가 다른 애플리케이션에서 사용자 이름과 비밀번호를 입력하면, 해당 애플리케이션이 포럼에 유효한 로그인 정보인지 확인할 수 있어야 합니다.
Discourse API 문서를 읽었지만, 특정 사용자의 사용자 이름과 비밀번호를 설정하는 등 많은 기능이 가능한 반면, 기존 사용자 이름과 비밀번호를 포럼 사용자 목록과 대조하여 검증하는 API 엔드포인트는 찾지 못했습니다.
웹 인터페이스를 통해 사용자를 로그인할 수 있으므로 포럼은 이러한 검증을 수행할 수 있어야 하며, 따라서 해당 API 엔드포인트가 존재할 것이라고 추측합니다.
포럼에 로그인하기 위해 사용자 이름과 비밀번호를 확인하는 API 엔드포인트는 무엇인가요?
더 직접적인 방법으로, DiscourseConnect를 사용자 인증 메커니즘으로 사용할 수 있습니다. 또는 애플리케이션의 앞단에서 discourse-auth-proxy를 사용할 수도 있습니다.
이것들은 로그인 자격 증명을 직접 처리하는 대신 사용자를 인증하기 위한 권장 방법입니다. 또한 2FA 세부 사항을 직접 처리할 필요가 없다는 뜻이기도 합니다.
ygramoel
(Johan Cockx)
3
사실, 제 "다른 애플리케이션"은 웹 앱이 아니라 데스크톱 앱입니다. 그 경우 discourse-auth-proxy가 작동하지 않을 것 같습니다.
DiscourseConnect 페이지의 첫 번째 문장 중 하나는 다음과 같습니다:
Discourse 사이트와 통합하려는 많은 사이트들은 모든 사용자 등록을 별도의 사이트에서 관리하기를 원합니다. 이러한 구성에서는 모든 로그인 작업을 해당 다른 사이트로 위임해야 합니다.
이것은 제가 원하는 것의 정반대입니다. 저는 모든 로그인 작업을 Discourse로 위임하고 싶습니다. DiscourseConnect를 사용하여 이렇게 할 수 있는 방법이 있을까요?
네, 물론입니다.
어려운 점은 프로바이더(Discourse)와 컨슈머(여러분의 앱) 사이에 공유 시크릿(shared secret)이 존재한다는 것입니다. 앱을 배포하면 사용자가 앱 내의 모든 시크릿에 접근할 수 있게 됩니다.
여러분의 앱에 서명된 토큰을 제공하는 최소한의 커스텀 웹 서비스 앞에 auth-proxy를 배치하는 방식이 효과적일 수 있습니다.
제가 생각하지 못하는 다른 방법들이 더 있을 것입니다.
ygramoel
(Johan Cockx)
5
API 키를 말씀하시는 건가요? 특정 API 엔드포인트에만 접근할 수 있는 “세분화된(granular)” API 키를 생성하는 것이 가능해 보입니다. 하지만 이 방식을 사용할 경우 어떤 엔드포인트가 필요한지는 여전히 명확하지 않습니다. 혹시 아시는 분 계신가요?
네, 인증 프록시(auth-proxy)를 갖춘 최소한의 웹 서비스가 좋은 해결책이 될 수 있겠습니다. 확인을 위해 조금 실험을 해봐야 할 것 같습니다.
정확히 말하면, enable discourse connect provider와 함께 설정해야 하는 해당 애플리케이션의 discourse connect provider secrets 값입니다.
이와 관련된 자세한 내용은 다음 링크에서 확인할 수 있습니다: Use Discourse as an identity provider (SSO, DiscourseConnect)
renato
(Renato Atilio)
7
ygramoel
(Johan Cockx)
8
제 이해가 맞다면, 이 방법은 사용자가 브라우저를 통해 로그인하는 것을 의미합니다. 이는 가능하지만, 저는 브라우저를 열지 않고 데스크톱 애플리케이션에서 직접 사용자 이름과 비밀번호를 입력할 수 있는 방법을 찾고 있었습니다.
제가 염두에 둔 방식은 제가 직접 구현하지 않는 한 TFA(2단계 인증)를 지원하지 않으며, 제3자 제공자(Google, Facebook, Discord 등)를 통한 로그인도 지원하지 않는다는 점은 이해하고 있습니다.
이것이 명시적으로 지원되는지는 확실하지 않지만, 웹 브라우저에서 사용자가 세션을 생성하는 것과 동일한 방식으로 세션을 생성할 것입니다:
curl 'https://try.discourse.org/session' \
-H 'sec-ch-ua: "Chromium";v="113", "Not-A.Brand";v="24"' \
-H 'Discourse-Present: true' \
-H 'DNT: 1' \
-H 'X-CSRF-Token: …' \
-H 'sec-ch-ua-mobile: ?0' \
-H 'User-Agent: …' \
-H 'Content-Type: application/x-www-form-urlencoded; charset=UTF-8' \
-H 'Accept: */*' \
-H 'Referer: https://try.discourse.org/' \
-H 'X-Requested-With: XMLHttpRequest' \
-H 'sec-ch-ua-platform: "Linux"' \
--data-raw 'login=demouser&password=demopassword&second_factor_method=1&timezone=America%2FToronto' \
--compressed
그리고 이 로그인 로직을 앱에 복제하십시오.
ygramoel
(Johan Cockx)
10
현재 제가 이해한 바로는, Reactive Native 예제에서 사용된 방법을 우리의 데스크톱 애플리케이션(Python으로 작성됨)에 적용할 수 있을 것 같습니다.
사용된 API 접근 지점은 <site>/session으로 보이며, 사용자 이름, 비밀번호 및 csrf 토큰을 인수로 받습니다. csrf 토큰은 <site>/session/csrf에서 가져올 수 있습니다.
이것은 제가 찾던 것과 매우 유사합니다. 이 방법을 시도해 보고, 작동하는지 여부를 보고하겠습니다.
<site>/session API 접근 지점에 대한 문서가 어디에 있나요?
데스크톱 앱에서 원하는 것을 하는 가장 좋은 방법은 사용자 API 키를 사용하는 것입니다.
앱 내부나 브라우저를 통해 웹 인터페이스가 필요하지만, 모바일 앱에서 사용하는 프로토콜의 핸들러로 앱을 설정하면 토큰을 쉽게 가져올 수 있으며, 토큰이 만료되거나 다른 장치를 사용하는 경우에만 다시 브라우저를 열면 됩니다.
제 개인적인 경험으로는, 세션 엔드포인트를 사용하는 것보다 사용자 API 키를 사용하는 것이 훨씬 안전하고 간단한 옵션입니다. 
ygramoel
(Johan Cockx)
12
@renato가 언급한 React Native 코드와 거의 동일한 작업을 수행하는 Python 코드 20줄입니다. (다만 Discourse 2.5와의 호환성은 없습니다. 저는 그 기능이 필요하지 않으므로요)
기본적인 사용자 이름-비밀번호 기반 로그인을 사용한다고 가정하면 잘 동작합니다. Discourse 인스턴스에 설정된 Discourse SSO 로그인을 사용하는 대안적인 방법들도 여전히 살펴보겠습니다.
import requests
import json
def discourse_authenticate(url, name, password):
session = requests.Session()
session.headers.update({'X-Requested-With': 'XMLHttpRequest'})
r1 = session.get(url + '/session/csrf')
csrf_token = json.loads(r1.text).get('csrf')
r2 = session.post(url + '/session',
data={
'login': name,
'password': password,
'authenticity_token': csrf_token,
},
)
if r2.status_code != 200:
return None
return json.loads(r2.text)
ygramoel
(Johan Cockx)
13
이를 적용해 보았지만 작동하지 않습니다. 아래는 .../session/sso_provider를 위한 URL을 생성하는 (단순화된) Python 코드입니다. 시도해 보면 Login Error가 발생합니다. 그 의미가 무엇인지 전혀 모르겠습니다.
import secrets
import base64
import urllib.parse
import hmac
import hashlib
forum_url = 'https://forum.embeetle.com'
target_url = 'https://embeetle.com/#account'
sso_secret = b'JCLSVcqbAnEPXz2p2xBY'
nonce = secrets.token_urlsafe()
payload = f'nonce={nonce}&return_sso_url={target_url}'
payload_base64 = base64.b64encode(payload.encode('utf-8')).decode()
payload_for_url = urllib.parse.quote(payload_base64)
payload_for_url = 'bm9uY2U9YklKeEU1WWw2OFhjSkJydGlwSU15UTRZeVlMeWd6ZzQyUU9mOFo0SWF5QSZyZXR1cm5fc3NvX3VybD1odHRwczovL2VtYmVldGxlLmNvbS8jYWNjb3VudA%3D%3D'
signature = hmac.new(
sso_secret, payload_for_url.encode('utf-8'), hashlib.sha256
).hexdigest()
print(f'{forum_url}/session/sso_provider?sso={payload_for_url}&sig={signature}')
예를 들어, 한 번의 실행에서는 아래 curl 명령에 있는 URL이 생성될 수 있습니다:
johan@morla:~/sa\> curl 'https://forum.embeetle.com/session/sso_provider?sso=bm9uY2U9YklKeEU1WWw2OFhjSkJydGlwSU15UTRZeVlMeWd6ZzQyUU9mOFo0SWF5QSZyZXR1cm5fc3NvX3VybD1odHRwczovL2VtYmVldGxlLmNvbS8jYWNjb3VudA%3D%3D&sig=a392ebb81b93ba7411290fbd00240921ae053bbb82998830dda994c8a71853da'
Login Errorjohan@morla:~/sa\>
관리자로 verbose discourse connect logging을 활성화하고, 시도해 본 후 포럼의 /logs를 확인하여 더 자세한 오류를 살펴보세요. 예를 들어 https://forum.embeetle.com/logs와 같습니다.
다음과 같은 내용이 표시됩니다:
인용된 페이로드가 아니라 페이로드 자체에 서명해야 합니다. 예를 들어:
signature = hmac.new(
sso_secret, payload_base64.encode('utf-8'), hashlib.sha256
).hexdigest()
그러면 작동합니다!
참고로, 이 시크릿은 즉시 폐기하고 변경해야 합니다. 테스트 중에 제가 방금 이 시크릿으로 앱에 로그인했기 때문에, 이 시크릿을 가진 누구든 앱에 로그인할 수 있습니다.
ygramoel
(Johan Cockx)
16
걱정하지 마세요. 제가 리다이렉트하는 페이지는 테스트용 공개 페이지입니다.
그래도 시크릿을 변경해 두었습니다.
이 특정 작업을 위한 오픈소스 추천 3가지를 알려주실 수 있을까요?
현재 Nginx를 사용하고 있지만, Keycloak와 같이 더 견고한 개발 도구로 관리할 수도 있을 것 같습니다.