Discourse APIを使用して別のアプリでユーザーを認証できますか?

動作中の Discourse フォーラムがあり、ユーザーはローカルログイン(ユーザー名 + パスワード)を作成できます。

これらのユーザー名とパスワードを別のアプリケーションで再利用したいと考えています。つまり、ユーザーは別のアプリケーションにユーザー名とパスワードを入力し、そのアプリケーションはそれがフォーラムの有効なログインであるかどうかを確認できる必要があります。

Discourse API のドキュメントを読みました。ユーザー名とパスワードの設定など、多くのことが可能ですが、フォーラムユーザーのリストに対して既存のユーザー名とパスワードを検証するための API エンドポイントは見つかりませんでした。

フォーラムが Web インターフェイス経由でユーザーをログインさせるためにこれを行う必要があるため、そのような API エンドポイントが存在すると想定しています。

フォーラムにログインするためにユーザー名とパスワードを確認する API エンドポイントは何ですか?

「いいね!」 3

より直接的には、ユーザーを検証するメカニズムとして DiscourseConnect を使用するか、アプリケーションの前面で discourse-auth-proxy を使用できます。

これらは、ログイン資格情報を直接処理するのではなく、ユーザーを認証するための推奨される方法です。また、2FA の詳細を処理する必要がないことも意味します。

「いいね!」 5

実際には、私の「他のアプリケーション」はWebアプリではなくデスクトップアプリです。その場合、discourse-auth-proxyは機能しないと思います。

DiscourseConnectページでは、最初の声明の1つが次のようになっています。

Discourseサイトと統合したい多くのサイトは、すべてのユーザー登録を別のサイトで管理したいと考えています。そのようなセットアップでは、すべてのログイン操作は、その異なるサイトにアウトソースされるべきです。

これは私がやりたいこととは正反対です。すべてのログイン操作をDiscourseにアウトソースしたいのです。DiscourseConnectを使用してそれを実行する方法はありますか?

「いいね!」 1

はい、もちろんです。

難しいのは、プロバイダー(Discourse)とコンシューマー(あなたのアプリ)の間で共有シークレットがあることです。アプリを配布すると、ユーザーはアプリ内のすべてのシークレットにアクセスできるようになります。

カスタムの最小限のWebサービスの前で認証プロキシを配置し、アプリに署名付きトークンを渡すことでうまくいくかもしれません。

これを行う他の方法もきっとあると思いますが、私は思いつきません。

APIキーのことですか? 「粒度の細かい」APIキーを作成できるようです。これは特定のAPIエンドポイントにのみアクセスできます。そのアプローチを使用する場合、どのエンドポイントが必要になるかはまだはっきりしていません。ご存知ですか?

はい、最小限のWebサービスと認証プロキシは良い解決策になるでしょう。実験して調べる必要があります。

いいえ、正確にはそうではありません。enable discourse connect provider と連携して設定する必要があるのは、アプリケーションの discourse connect provider secrets の値になります。

詳細については、こちらをご覧ください: Use Discourse as an identity provider (SSO, DiscourseConnect)

「いいね!」 1

デスクトップアプリの場合、このReact Nativeの例で使用されているこの方法は役に立つかもしれません。

https://github.com/pmusaraj/discourse-mobile-single-site-app/blob/main/js/Authenticate.js

「いいね!」 1

もし私の理解が正しければ、この方法ではユーザーはブラウザ経由でログインすることになります。それは可能ですが、ブラウザを開かずにデスクトップアプリケーションでユーザー名とパスワードを入力できる方法を探していました。

私が考えているアプローチでは、自分で実装しない限りTFA(二要素認証)をサポートできず、またサードパーティプロバイダー(Google、Facebook、Discordなど)経由のログインもサポートできないことは理解しています。

これは明示的にサポートされているとは思いませんが、ユーザーがWebブラウザで行うのと同じ方法でセッションを作成します。


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

そして、このログインロジックをアプリケーションで複製します。

私の理解では、現時点では、React Nativeの例で使用されているメソッドをPythonで記述されたデスクトップアプリケーションに転用できるようです。

使用されているAPIアクセスポイントは<site>/sessionのようで、ユーザー名、パスワード、csrfトークンが必要です。csrfトークンは<site>/session/csrfから取得できます。

これは私が探していたものに非常に近いです。試してみます。うまくいけば報告します。

<site>/session APIアクセスポイントはどこかに文書化されていますか?

「いいね!」 1

デスクトップアプリで目的のものを実現する最善の方法は、ユーザーAPIキーを使用することです。

アプリ内またはブラウザを開くことでWebインターフェイスが必要になりますが、モバイルアプリで使用されているプロトコルハンドラをアプリにすることで、その方法で簡単にトークンを取得でき、トークンが期限切れになったり、別のデバイスが使用されたりした場合にのみブラウザを再度使用すればよくなります。

これに関する私の個人的な経験では、セッションエンドポイントを使用しようとするよりも、ユーザーAPIキーを使用する方がはるかに安全で簡単なオプションです。 :slight_smile:

「いいね!」 2

React Native コードの @renato が参照しているものとほぼ同じことを行う 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)

これを適用しようとしましたが、うまくいきません。以下は、.../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>

管理者として、「詳細なDiscourse Connectログ記録」を有効にし、試してから、フォーラムの「/logs」で詳細なエラーを確認してください。例:https://forum.embeetle.com/logs

例えば、次のような表示が確認できます。

引用されたペイロードではなく、ペイロードに署名する必要があります。例:

signature = hmac.new(
  sso_secret, payload_base64.encode('utf-8'), hashlib.sha256
).hexdigest()

そうすれば機能します!

ちなみに、このシークレットは直ちに無効化して変更する必要があります。なぜなら、私がテスト中に実行したように、これを持っている人なら誰でもあなたのアプリにログインできるからです。

「いいね!」 3

心配しないでください。リダイレクト先のページはテスト専用の公開ページです。

念のためシークレットは変更しました。

「いいね!」 2

その特定のタスクに最適なオープンソースの提案を3つ教えていただけますか?

Nginxを使用していますが、Keycloakのようなより堅牢な開発で管理できるかもしれませんか?

auth-proxyとは、こちらのことを指します: GitHub - discourse/discourse-auth-proxy: An http proxy that uses the DiscourseConnect protocol to authenticate users

「いいね!」 2

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.