用户API密钥规范

I find allowed_user_api_auth_redirects default of “discourse://auth_redirect” rather restrictive, especially because “discourse” does not appear to be a valid URI scheme.

Please explain the thinking behind this default. Thank you.

I am having this issue as well. If I initiate the API from a JS application, then automatically the allowed headers are: User-Api-Key, User-Api-Client-Id even though I do not need user API keys. All I want is a simple API key but I cannot get anything to work. If I try to pass Api-Key in the headers I get a CORS error since it expects User-Api-Key. But when I try to use User-Api-Key, I get 403 errors. I am stuck. I would think this is the base usage for using the APIs. I am not trying to do anything out of the ordinary. I am simply trying to create a new topic post.

2 个赞

That is the custom URI scheme used by the DiscourseHub app for iOS and Android.

6 个赞

I’ve got a question concerning the “read tokens” and “write tokens”. This comment here is from 2016, so this possibly had already been changed? Or are the defaults still only “read tokens”?

Background: I’m one of the coders behind a distributed social media system. We already do have connectors to non-federating systems. The idea is to write an addon for discourse as well. But when most likely most system won’t allow users to generate tokens that allow posting, we will try another way. We already do have a mail connector. Then we will simply use the mailing list function of Discourse and we will try to enhance the returned content and will post via SMTP.

You can do write tokens if you ask for the scope upfront

3 个赞

Of course this is always possible. But I have the feeling that this is a support nightmare. Our software has got some hundred installations with (in total) more than 10k users. When they see that there is an addon that is connection to Discourse, many will surely like to use it. And since it most likely won’t work out of the box, this will generate questions and support work from our side. Additionally it will generate work for the admins of the several Discourse installations. And very likely not all will allow it - which will cause frustration.

So possibly at first I will focus on integrating the mailing list mode mails. Or is it possible to combine these two? Means: Reading of the posts via the API, but posting via SMTP?

大家好……我不知道如何生成 public_key……我是否应该使用 RSA 生成器来获取公钥/私钥?
如果是的话,我已经用了一些在线 RSA 生成器,但遇到了这个错误:

OpenSSL::PKey::RSAError (Neither PUB key nor PRIV key: nested asn1 error) /var/www/discourse/app/controllers/user_api_keys_controller.rb:189:in `initialize'

另外,我想请教大家,这个方案是否适合我的使用场景:
我有一个应用,主要想对用户进行身份验证并获取用户名。对于在我的应用中验证用户登录来说,生成 API 密钥的流程是否是最简单的方案?如果可能的话,我想避免使用 SSO,因为它看起来更复杂。

我也遇到同样的问题。我尝试使用 User-Api-Key(而非 Api-Key)来创建主题帖,但收到了来自 actionpack 库的 CSRF 拒绝错误。

除非 Discourse 服务器已关闭 CSRF 检查,否则从第三方桌面应用程序发帖似乎很困难。我可不想去模拟浏览器。

@sam 对于允许仅附带 read 作用域的用户 API 密钥通过 URL 参数在 GET 请求中传递,您怎么看?

用例是允许集成,例如使用用户 API 密钥将您的 带提醒的改进书签 订阅到 Google 日历中。

5 个赞

不如创建一个特定的新作用域,并添加第三个参数来指示“允许获取参数”。这样,人们就无法将其误用于其他用途(例如绕过 CORS 并从其他站点请求 Discourse API)。

来源

SCOPES = {
    read: [:get],
    write: [:get, :post, :patch, :put, :delete],
    message_bus: [[:post, 'message_bus']],
    push: nil,
    one_time_password: nil,
    notifications: [[:post, 'message_bus'], [:get, 'notifications#index'], [:put, 'notifications#mark_read']],
    session_info: [
      [:get, 'session#current'],
      [:get, 'users#topic_tracking_state'],
      [:get, 'list#unread'],
      [:get, 'list#new'],
      [:get, 'list#latest']
    ],
+   calendar: [ [:get, 'users#bookmarks_cal', true ] ],
  }

(顺便一提:为什么我们在这里使用嵌套数组……)

10 个赞

我喜欢 API 密钥在用户层面被明确标记为“允许在 GET 中使用”。

总体而言,该选项可以开放给任何 GET 请求。我喜欢的规则是,在此模式下运行时:

  1. 用户 API 密钥严格限制为单个特定的 GET 控制器操作
  2. 用户 API 密钥被标记为允许在 GET 查询参数中使用。

这限制了通过代理泄露的影响,因为该密钥永远不会被重用。

我想 {get: 'list#new'}, {get: 'list#latest'} 也能工作。

7 个赞

我对“仅通过 URL 参数获取”类型的用户 API 密钥非常感兴趣。我想请问,你们是否计划允许用户通过界面生成此类密钥?

可能是在站点设置中,或者通过插件实现。我们计划对功能集进行一定的标准化,使管理员 API 密钥也支持作用域。

4 个赞

您好……您能解决此问题吗?我也遇到了同样的问题,但无法修复。我尝试了传递不同类型的密钥,但都没有成功。非常感谢您的任何帮助。

有相关的库吗?如果没有,有示例实现吗?我正尝试使用 PHP 在网站的另一部分识别用户的 Discourse 账户。这看起来像是修改过的 OAuth 流程,但我在如何实现方面有些困惑。

具体来说,我不确定如何进行公钥/私钥生成的整个流程。

有没有办法直接使用 OAuth 2,将 Discourse 作为 OAuth 提供商?

2 个赞

你使用 User-Api-Key 成功了吗?我也收到了“您无权查看请求的资源”的提示。

1 个赞

我弄明白我错在哪里了:返回的负载不是 UserAPI 密钥本身,而是一个加密的 JSON 字符串,需要使用公私钥对中的私钥进行解密。

2 个赞

编辑:我已经让大部分功能运行起来了,一旦完全搞定,我会提供详细说明。


客户端如何获取公私钥对和 ID?

您能否提供使用 JavaScript 应用获取用户 API 密钥的代码?(一个旨在允许用户向 Discourse 论坛发起 API 调用的 JavaScript 应用。)

我遇到了 403 错误,或者出现如下错误提示:抱歉,我们无法颁发用户 API 密钥,此功能可能已被站点管理员禁用(尽管我的站点已勾选:允许生成用户 API 密钥)。

我认为问题可能出在如何生成公私钥对(具体如何操作?)以及如何处理重定向上。

任何代码示例都感激不尽。

经过一些尝试和错误,我终于让它运行起来了。

以下是我遵循的基本步骤:当我有一个自己编写的应用程序,并希望用户能够使用该应用向 Discourse 站点发起 API 调用时,我会这样做。

为此,我需要为每个特定用户生成一个专属的 API 令牌,以便代表该用户进行调用(至少在 Node.js/JavaScript 环境中是这样)。


注意:在 JavaScript 方面,我发现 @KengoTODA 在此处提供的代码非常有帮助:discourse-api-key-generator/src/index.ts at main · KengoTODA/discourse-api-key-generator · GitHub


以下是我遵循的步骤:

第一步:生成一对公钥和私钥。

这是你的应用程序需要完成的工作——生成一个公钥和一个私钥。上面的 GitHub Gist 提供了一种实现方法。

第二步:设置一个重定向 URL。

这是 Discourse 将重定向到的 URL,并在其载荷(payload)中提供最终的 API 令牌。如果你使用的是桌面应用程序(即没有浏览器 URL 的应用),那么重定向 URL 将基于你自定义的协议设置,当在浏览器中输入该重定向 URL 时,会打开你的应用程序。

请注意,重定向 URL 需要在目标 Discourse 站点的站点设置中进行白名单配置。

此外,Discourse 站点可能还需要启用“允许用户 API 密钥”的站点设置。关于“站点设置”的详细说明,请参阅该主题的原帖。

第三步:向 Discourse 的请求 URL 发送 API 请求调用。

因此,你的应用程序将向遵循以下格式的 URL 发送调用:

https://[你的目标 Discourse 站点.com]/user-api-key-new

并添加以下参数:

  • 你的应用名称
  • 你的“client_id”(对于桌面应用,我使用了 const {hostname} = require('os') 中的 hostname(),就像上面提到的 GitHub Gist 中一样)
  • 作用域(scopes,即你希望用户通过 API 能够执行的操作范围,例如 “write”、“read” 等)
  • 你的公钥(来自第一步)
  • 你的重定向 URL(来自第二步)
  • nonce(这是一个你可以自定义的值——例如,直接使用 ‘1’ 似乎就能正常工作)

第四步:用户在由请求 URL 打开的 Discourse 站点页面上授权你的应用

当你成功发送请求 URL 后,它会打开 Discourse 站点上的一个页面,告知用户你的应用希望访问该站点。

在该页面上,有一个按钮供用户允许此操作。当用户点击该按钮时,Discourse 站点会重定向到你提供的重定向 URL,并在参数中附加 ?payload=[API 密钥]。这里的 API 密钥就是你需要在应用中解码的密钥。

第五步:你的应用获取重定向 URL 的值(包含 payload 值),并解码 API 密钥

你快要成功了。你的应用程序需要解析 Discourse 重定向到的 URL,并获取载荷中包含的 API 密钥。

一旦你获得了该 API 密钥,你需要做两件事:

  1. 获取实际的密钥,而不是 URL 编码版本:如果你从 URL 中获取参数,它通常会被 URL 编码(例如添加 % 等)。你需要对其进行清理。在 JavaScript 中,我发现 decodeURIComponent 可以解决这个问题。
  2. 一旦你获得了从 Discourse 返回的已清理的 API 密钥,你需要对其进行解码。为此,你可以使用 JavaScript 配合私钥进行解码。基本上,你使用你在第一步中生成的私钥来解码已清理的 API 密钥。我在上面提到的 GitHub Gist 中提供了一些 JavaScript 示例代码:discourse-api-key-generator/src/index.ts at main · KengoTODA/discourse-api-key-generator · GitHub

运行你的解码代码后,你就得到了令牌本身,现在可以代表用户进行经过身份验证的 API 调用了。

第六步:使用该令牌(即最终的、已清理的、已解码的 API 密钥)代表用户发起 API 调用

使用该令牌后,你似乎不需要在 API 调用中输入用户名。我发现,在 GET、POST、PUT 等调用中包含以下头部就足够了:

headers: {
"User-Api-Key": [令牌]
}

至此,你 hopefully 已经拥有一个可用的每用户身份验证方法,用于与 Discourse 进行交互。

7 个赞

将内容添加到 allowed_user_api_auth_redirects 有哪些安全隐患?有人要求添加一个字符串以支持 NextCloud 集成。

1 个赞