用户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
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)
Secure way of encrypting payload in Javascript for user authentification
API CORS Headers Incorrect
Generating User Api Keys with REST API
Get back Username with API Key?
Automatic Login from iOS/Android app
Is there any documentation on the User/Mobile API?
Delegated authentication for Discourse Mobile app
Generating User Api Keys with REST API
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?
How can I allow users to like posts using RESTapi
'Clip To Discourse' Chrome Extension
Dexo - A Native iOS Client for Discourse
'Clip To Discourse' Chrome Extension
"Api-Key" and "Api-Username" for try.discourse.org?
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

Hi guys

Apologies if this is duplication but I am having trouble digesting the steps there even though they are very well put together

I have a discourse installation on https://forum.domain.com and a site on https://development.domain.com from which I need to make calls to the discourse installation to pull and set some data.

All requests work from postman with Api-Username and Api-Key - no problem

However for cross origin requests using JS discourse doesnt allow the Api-Username leading me down the path of the flow described in this thread i.e. using the User-Api-Key and the User-Api-Client-Id

I basically require a description of paramaters PUBLIC_KEY,NONCE,CLIENTID used in the sample request below and where I can get them…

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

Finally will this flow allow for seamless api requests from my https://development.domain.com to https://forum.domain.com without the need for authentication.

PS I have SSO set up between the forum and the site from which the requests are being made so the user will be logged in…

Thanks for any guidance

One sec, is this a per user thing? Are you allowing arbitrary users to do this?

Our server API supports header based auth these days so it can work with CORS just like the user api keys do. You would use the user api keys if you want to restrict scopes and allow end users to generate the keys vs admins.

2 个赞

I wouldn’t see each user requiring a separate key no… One admin key should do what I need.

I can consume the api no problem through postman. For example a GET to /notifications.json?username=alanmurphy returns the data no problem using just api-key as a header.

If I trigger that request from the console of a discourse installation I get data back no problem also i.e.

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();

All Good:+1:

However, if I do this from the sub-domain I am wishing to contact discourse from, it tells me that it has been blocked by CORS policy: Request header field api-key is not allowed by Access-Control-Allow-Headers.

The allowed headers are:

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

If I could just get guidance on what auth params to pass for cross origin requests and where to get them that would be very helpful.

Ps. My discourse is set up to allow Cross Origin requests from the domain so I believe it is purely a headers issue

Thanks

1 个赞