我可以使用 Discourse API 在另一个应用程序中验证用户吗?

我有一个可用的 Discourse 论坛,用户可以在其中创建本地登录(用户名 + 密码)。

我想在另一个应用程序中重新使用他们的登录名和密码。换句话说:用户将在另一个应用程序中输入他们的用户名和密码,该应用程序应能够检查这是否是论坛的有效登录。

我已阅读 Discourse API 的文档。许多事情都是可能的,包括为给定用户设置用户名和密码,但我没有找到一个 API 端点来验证现有用户名和密码是否与论坛用户列表匹配。

我假设必须存在这样一个 API 端点,因为论坛必须能够通过 Web 界面执行此操作才能登录用户。

用于检查用户名和密码以登录论坛的 API 端点是什么?

3 个赞

更直接地说,您可以使用 DiscourseConnect 作为验证用户的机制,或者在应用程序前面使用 discourse-auth-proxy

这些是用于验证用户的建议方法,而不是直接处理登录凭据。这也意味着您无需尝试处理双因素认证 (2FA) 的详细信息。

5 个赞

实际上,我的“其他应用程序”是一个桌面应用程序,而不是一个 Web 应用程序。我认为 discourse-auth-proxy 在这种情况下不起作用。

DiscourseConnect 页面上,开头的陈述之一是:

许多希望与 Discourse 站点集成的站点希望将所有用户注册保留在一个单独的站点中。在这种设置中,所有登录操作都应外包给该不同的站点。

这与我想要做的完全相反:我想将所有登录操作外包给 Discourse。是否有办法使用 DiscourseConnect 来做到这一点?

1 个赞

是的,绝对可以。

棘手之处在于提供商(Discourse)和使用者(您的应用程序)之间有一个共享的密钥。如果分发您的应用程序,用户将可以访问其中的所有密钥。

在自定义的最小 Web 服务前面放置一个 auth-proxy,该服务向您的应用程序提供一个签名的令牌,这可能会很好地起作用。

我确信还有其他我没想到的方法可以做到这一点。

你是指 API 密钥吗?似乎可以创建一个“细粒度”的 API 密钥,它只能访问特定的 API 端点。如果我采用这种方法,我仍然不清楚需要哪些端点。你知道吗?

是的,一个带有身份验证代理的最小化 Web 服务可能是一个不错的解决方案;我需要做一些实验才能弄清楚。

不完全是——这需要是应用程序的 discourse connect provider secrets 值,并且需要与 enable discourse connect provider 一起设置。

更多信息请参见: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 等)登录。

我不认为这是明确支持的,但我会以用户在网页浏览器中的方式创建会话:


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 个赞

这是 20 行 Python 代码,其功能与 @renato 引用的 React Native 代码大致相同(除了不支持 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)

我尝试应用此方法,但无法使其正常工作。下面是一些(简化的)Python 代码,用于生成指向 .../session/sso_provider 的 URL。当我尝试它时,我收到 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

您会看到例如:

您需要对 payload 进行签名,而不是对引用的 payload 进行签名,例如:

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

然后它就能正常工作了!

顺便说一句,您需要立即撤销并更改此密钥,因为任何拥有它的人都可以登录您的应用程序,就像我刚才在测试时所做的那样。

3 个赞

别担心,我正在重定向到的页面是公共页面,只是为了测试。

无论如何我都更改了密钥。

2 个赞

对于这项特定任务,您在开源方面有哪些排名前三的建议?

我正在使用 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.