用户API密钥规范

Discourse 包含一个系统,如果遵循非常具体的协议,可以为每个用户生成 API 密钥。此功能有助于应用程序访问 Discourse 实例,而无需涉及版主。

总体描述

从高层次来看:

  1. 客户端(桌面应用、浏览器插件、移动应用)生成私钥/公钥对和返回 URL

  2. 客户端重定向到 Discourse 上的一个路由,向 Discourse 提供其公钥

  3. Discourse 获得用户使用该应用的批准

  4. Discourse 生成一个用户 API 密钥

  5. Discourse 使用公钥(包含用户 API 密钥)进行加密的有效载荷重定向回返回 URL

详细信息

用例:

  1. 代表最终用户轮询 Discourse 站点的桌面应用程序,以获取跨多个站点的通知计数。

  2. 代表最终用户轮询 Discourse 站点并处理推送通知的移动应用程序

  3. 为最终用户提供有关各种 Discourse 站点仪表板的 Web 应用程序。

  4. 与第三方应用程序的自定义集成,这些应用程序将 Discourse 作为通用公司应用的一部分来使用。例如:将 Discourse 社区通知集成到 hopscotch 应用中。

设计:

站点设置

  • allow_user_api_key_scopes:允许用户 API 密钥的访问范围。范围在此处定义。可用的内置范围包括:read(读取)、write(写入)、message_bus(消息总线)、push(推送)、one_time_password(一次性密码)、notifications(通知)、session_info(会话信息)、bookmarks_calendar(书签日历)、user_status(用户状态)(插件可能会注册其他范围)。

  • user_api_key_allowed_groups:控制哪些组被允许生成用户 API 密钥(默认为管理员、版主和 trust_level_0)

  • allowed_user_api_push_urls:可以是推送通知目标的站点列表

  • allowed_user_api_auth_redirects:用户 API 密钥生成后允许重定向的目标

全局设置

  • max_user_api_reqs_per_minute:20
  • max_user_api_reqs_per_day:2880

用户体验元素

如果已授予任何用户 API 密钥,Discourse 会在用户页面中显示一个 apps(应用)选项卡。

apps 选项卡将列出:

  • 应用程序的名称,例如(“Discourse Notifier”)
  • 最后使用日期
  • 批准日期
  • 已授予的访问范围列表
  • 一个 revoke access(撤销访问权限)按钮,以便您可以轻松撤销任何密钥

API 密钥授权 UI

每个密钥都必须由最终用户在一个清楚说明情况的页面中明确授权,例如:

“Discourse Notifier” 正在请求对您的帐户执行以下访问:

  • 读取和清除通知
  • 读取用户会话信息
  • 创建一次性登录令牌

[授权]

API 密钥生成流程

API 只要求用户端进行一次 GET 请求。

https://sitename.com/user-api-key/new

:exclamation: 从 Discourse 2.1 开始,如果用户 API 密钥长时间未被使用,它们现在会自动过期。默认情况下,站点设置:revoke user api keys unused days 设置为 180

参数:

  • auth_redirect:重定向回生成的令牌的 URL
  • application_name:发起请求的应用程序的名称(将显示在用户帐户的“Apps”选项卡中)
  • client_id:客户端的唯一标识符
  • nonce:客户端生成的唯一随机数。它将在加密的有效载荷中被回显,以便客户端可以验证响应的真实性
  • scopes:允许密钥的访问范围的逗号分隔列表,请参阅 allow user api key scopes 查看可用范围的完整列表
  • push_url:推送通知的目标 URL(如果作用域中包含 pushnotifications,则为必需且有效)
  • public_key:客户端生成的密钥对的公有部分
  • padding(可选):用于加密有效载荷的 RSA 填充模式。可接受的值为 pkcs1(默认)或 oaep。推荐新应用程序使用 OAEP。

调用 /user-api-key/new 并带有正确参数后,可能会发生两件事:

  1. 如果用户未登录,我们将重定向到登录(登录后我们将恢复授权)
  2. 用户登录后,将向他们展示授权 UI

授权被允许后,系统将重定向回 auth_redirect 中定义的 URL,并包含一个加密的 payload 参数,其中包含一个 JSON 对象,其中包含生成的用户 API 密钥(key)、nonce、推送状态(push)和 API 版本(api)。如果请求了 one_time_password 作用域,还会包含一个单独的加密的 oneTimePassword 查询参数。出于额外的安全考虑,client_id 不会被回显。

检查 API 版本

Discourse 中的使用密钥 API 是版本化的。客户端可以通过向 https://sitename.com/user-api-key/new 发出 HEAD 请求来检查 Discourse 站点的 API 版本。响应将包含一个名为 Auth-Api-Version 的标头,其中包含站点 API 的版本号。

使用 API

使用客户端 API 将与当前的管理 API 略有不同。

客户端可以指定 2 个标头:

User-Api-Key(必需):生成的密钥

User-Api-Client-Id(可选):提供此标头以更新数据库中此 api_key 存储的“客户端 ID”。

指定这些标头后,客户端就可以像往常一样对 API 执行请求。

生成一次性登录密码

从 4 版开始,API 包含一个特殊的作用域:one_time_password 作用域,它允许客户端使用用户 API 密钥生成一次性密码。如果客户端在生成 API 密钥时包含此作用域(遵循上述步骤),则加密的 oneTimePassword 将作为单独的查询参数包含在重定向回客户端的链接中。

或者,客户端可以向 /user-api-key/otp 发出 GET 请求,并附带以下参数:

  • auth_redirect
  • application_name
  • public_key
  • padding(可选)

并带有 User-Api-Key 标头。

此请求将重定向到 Discourse 中的一个屏幕,该屏幕将要求用户允许应用程序访问该站点。如果用户批准,该站点将重定向回 auth_redirect 中定义的 URL,并包含一个加密的 oneTimePassword 参数,其中包含一个一次性密码,客户端可以使用该密码通过请求 https://sitename.com/session/otp/ONE-TIME-PASSWORD 登录到该站点。(该一次性密码仅有效 10 分钟。)

撤销 API 密钥

要撤销 API 密钥,请向 /user-api-key/revoke 发送带有 User-Api-Key 标头且不带任何参数的 POST 请求。


上次由 @SaraDev2022-07-13T00:00:00Z 审阅

30 个赞
Generating User Api Keys with REST API
Discourse Login & Registeration
Discourse sso login using Rest API
Custom Push Notifications: allowed user api push urls
Beta testing the iOS mobile app
Secure way of encrypting payload in Javascript for user authentification
Can non-admin user issue their own API key?
How can I get user details via the user api key?
Authorization from a desktop application (and base domain site)
Is there any documentation on the User/Mobile API?
Get back Username with API Key?
Generating User Api Keys with REST API
Generating User Api Keys with REST API
API CORS Headers Incorrect
Automatic Login from iOS/Android app
Delegated authentication for Discourse Mobile app
Passing draft text into a new response
Discourse Hub "connect"
Generate User API Keys for testing
Consolidated API Requests in the thousands yet our site has no active API keys listed, is this a concern?
Having issue accessing the Discourse APIs from react app
Discourse REST API Documentation
Having issue accessing the Discourse APIs from react app
CORS error accessing API from javascript application
Does Discourse have support for PAT tokens?
API key creation
Access via Discourse API, key and/or user rejected
Feasibility of allowing a User Api Key client to register a valid auth_redirect
Implement discourse Apple login using API
Thousands of user api requests and invalidation
Are User API Keys Not Generated When Logging in via Login Link?
Confusion about API Authenticated User
Confusion about API Authenticated User
Generate User Api Key Without User Approval
How to use discourse api in react native?
"Api-Key" and "Api-Username" for try.discourse.org?
How can I allow users to like posts using RESTapi
Allow_user_api_key这个设置在哪开启
Dexo - A Native iOS Client for Discourse
'Clip To Discourse' Chrome Extension
'Clip To Discourse' Chrome Extension
Unable to create "Single User" level API key, always defaults to "All Users"
Discourse index
Authentication Protocol re: App Integration
Embed Discourse in a native app?
Request header field User-Api-Key is not allowed by Access-Control-Allow-Headers
Request header field User-Api-Key is not allowed by Access-Control-Allow-Headers
Request header field Content-Type is not allowed by Access-Control-Allow-Headers
Improve BAD CSRF error message when making API calls with content-type application/json
Create apikey for user programmatically as admin
How to fix 'Cannot read property '_links' of undefined'
Acess-Control-Allow-Headers CORS Error with API after updating discourse
Allow API use by regular users, not just admins
The purpose of the 2 Discourse API systems
Bot writer's tip: Processing every post
Using Zapier without being admin
Per User API Keys Not Working
Acess-Control-Allow-Headers CORS Error with API after updating discourse
Work with discourse users on an SPA

There are places I’ve strongly considered writing a delayed post tool, so I can compose a bunch of “word of the day” type things to keep going while I’m on vacation. Doing it without proper API access and pretending to be a browser seems somewhat more complicated than on other forum software.

(Just as an example of another use case.)

That is a good example, but keep in mind, our default will be only to enable read tokens, not write ones. Site admins will have to opt to allow write tokens.

Completely agree that the _t cookie hacks are a huge problem.

2 个赞

Does max_user_api_calls_per_key_day setting apply to admin-created keys at /admin/api?

1 个赞

Nope only to user api keys, we can look at adding extra limits for standard Api keys, it is a good safeguard

1 个赞

To be clear, this is not implemented yet, correct?

No, it’s there (for almost 2,5 years now) - Admin - Settings - User API.

4 个赞

As of Discourse 2.1 these user api keys now auto-expire if left unused for long periods of time, correct @sam?

2 个赞

Correct the site setting: expire user api keys days is set to 180 out of the box.

2 个赞

Okay, but you have to have admin access to see that page yes? And as an admin, it seems I can only create 1 key, so can’t create many keys for different users. confused.

1 个赞

What are you trying to accomplish? It’s not clear because there is this (user api keys) and regular API tokens (which is what I think you are after) that admins can create for multiple users, but you have to do that from the individual user’s page inside of the admin dashboard by clicking on the “generate” button on the “API Key” field.

4 个赞

Thanks! Yes, “regular API tokens” for users is what i was after. I’m sure I’d seen that a million times, and never realized it was there. :heavy_check_mark:

3 个赞

Hey everyone I’m not sure if this was resolved? I see that it’s possible to create an “All Users” and (per user) level API token as an admin, but I’m interested in giving a user the power to generate his/her own token. Has there been work done on this? It sounds like the original thread was getting at this idea, and then it was lost. Thanks!

Download the mobile App and then add a site, that uses the user api key system you have to follow a very strict workflow, no plans to expose arbitrary generation of keys in user prefs

3 个赞

Thank you! I downloaded the app, and then realized that the sites I belong to don’t have this enabled (I’ve been testing on my local machine with the Dockerized discourse). Just to make sure we are talking about the same thing - I’m interested in generating tokens to use just a scoped subset of functions (and not global API keys to do all the things). Is this what you are talking about?

It seems like, given that there is an endpoint to generate tokens, it would be logical to provide this function (to users with a certain trust level, for example) from within the site. Otherwise, it would need to be the case that the site generates some external page (with a server to hide an admin token) to generate the keys for the user. Is it the case that 1. there is no internal generation of tokens for the user (and why not?) and 2. Nobody has created some external app (javascript, flask, anything really) to perform this action?

1 个赞

Yes.

Possibly, conceptually

  1. This is tricky to consume cause you need to use custom HTTP headers (by design)

  2. It would be very hard to educate even TL3 users about what the point is of this thing.

I prefer not to ship UI for this in core, but maybe you should write a plugin for your specific use case?

Can you explain a bit more about the “why” here?

The design of the system centers around “I have an external dedicated program”, “this program knows how to follow Discourse protocol”, “It asks for token”

API keys (non user ones) are much easier to consume cause you just append them after the ?

4 个赞

Can I pass the API key generated from the user page on this? I was trying to use that API key and seeing ‘You are not permitted to view the requested resource.’ error on /categories.json API request

大家好

如果这是重复提问,敬请见谅。尽管相关步骤编写得非常清晰,但我仍难以完全理解。

我在 https://forum.domain.com 上部署了 Discourse,并在 https://development.domain.com 上有一个网站,需要调用 Discourse 接口来拉取和设置部分数据。

所有请求通过 Postman 使用 Api-Username 和 Api-Key 都能正常发送,没有问题。

但在通过 JavaScript 进行跨域请求时,Discourse 不允许使用 Api-Username,因此我不得不按照该线程中描述的流程操作,即使用 User-Api-Key 和 User-Api-Client-Id。

我主要需要了解以下参数在下方示例请求中的含义以及获取方式:PUBLIC_KEY、NONCE、CLIENTID。

https://forum.domain.com/user-api-key/new?public_key=PUBLIC_KEY&nonce=NONCE&scopes=SCOPES&client_id=CLIENTID&application_name=DEVELOP&auth_redirect=XXX

最后,这种流程是否允许从 https://development.domain.comhttps://forum.domain.com 发起无缝的 API 请求,而无需额外进行身份验证?

附注:论坛与发起请求的网站之间已设置 SSO,因此用户将处于登录状态。

感谢任何指导。

稍等,这是按用户设置的功能吗?你们是否允许任意用户执行此操作?

我们的服务器 API 目前支持基于请求头的身份验证,因此可以像用户 API 密钥一样与 CORS 协同工作。如果您希望限制权限范围,并允许最终用户自行生成密钥(而非由管理员生成),则应使用用户 API 密钥。

2 个赞

我认为不需要为每个用户单独分配一个密钥……一个管理员密钥就足以满足我的需求。

通过 Postman 调用 API 完全没有问题。例如,使用 api-key 作为请求头发起对 /notifications.json?username=alanmurphy 的 GET 请求,可以正常返回数据。

如果我在 Discourse 安装的控制台中触发该请求,同样可以正常获取数据,如下所示:

var xhr = new XMLHttpRequest();
xhr.addEventListener(“readystatechange”, function () {
if (this.readyState === 4) {
console.log(this.responseText);
}
});
xhr.open(“GET”, “https://**********.com/notifications.json?username=alanmurphy”);
xhr.setRequestHeader(“api-key”, “d06ca53322d1fbaf383a6394d6c229e56871342d2cad953a0fe26c19df7645ba”);
xhr.setRequestHeader(“api-userame”, “system”);
xhr.send();

一切正常:+1:

但是,如果我从希望联系 Discourse 的子域名发起该请求,系统会提示被 CORS 策略阻止:请求头字段 api-key 未被 Access-Control-Allow-Headers 允许。

允许的请求头包括:

Content-Type, Cache-Control, X-Requested-With, X-CSRF-Token, Discourse-Visible, User-Api-Key, User-Api-Client-Id

如果能得到关于跨域请求应传递哪些认证参数以及这些参数获取位置的指导,将非常有帮助。

附:我的 Discourse 已配置为允许来自该域名的跨域请求,因此我认为问题纯粹出在请求头上。

谢谢

1 个赞