用户API密钥规范

我发现 allowed_user_api_auth_redirects 的默认值 “discourse://auth_redirect” 过于严格,尤其是因为 “discourse” 似乎不是一个有效的 URI 方案。

请解释一下设置此默认值的考量。谢谢。

我也遇到了同样的问题。如果我从 JS 应用发起 API 请求,那么自动允许的请求头是 User-Api-Key 和 User-Api-Client-Id,尽管我并不需要用户 API 密钥。我只想要一个简单的 API 密钥,但无论如何都无法成功。如果我尝试在请求头中传递 Api-Key,会收到 CORS 错误,因为系统期望的是 User-Api-Key。但当我尝试使用 User-Api-Key 时,又会出现 403 错误。我卡住了。我认为这应该是使用 API 的基本方式。我并没有做任何特殊操作,只是想创建一个新的主题帖子。

2 个赞

这是 DiscourseHub 应用在 iOS 和 Android 上使用的自定义 URI 方案。

6 个赞

我有一个关于“读取令牌”和“写入令牌”的问题。这里的这条评论来自 2016 年,所以这可能已经有所更改?还是默认仍然只是“读取令牌”?

背景:我是一家去中心化社交媒体系统背后的开发者之一。我们已经有了与非联邦系统的连接器。我们的想法是为 Discourse 也编写一个插件。但由于大多数系统很可能不允许用户生成允许发帖的令牌,我们将尝试另一种方法。我们已经有邮件连接器。然后我们将直接使用 Discourse 的邮件列表功能,并尝试增强返回的内容,最后通过 SMTP 发布。

如果您提前请求作用域,就可以写入令牌。

3 个赞

当然,这总是可行的。但我感觉这会成为支持噩梦。我们的软件已有数百个安装实例,用户总数超过1万人。当他们看到有一个连接到 Discourse 的插件时,许多人肯定会想使用它。由于它很可能无法开箱即用,这将引发大量问题,增加我们这边的支持工作量。此外,这也会给各个 Discourse 实例的管理员带来额外工作。而且很可能并非所有管理员都允许使用它,这将导致用户感到沮丧。

因此,我可能最初会专注于集成邮件列表模式的邮件。或者,是否可以将这两者结合起来?也就是说,通过 API 读取帖子,但通过 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 个赞