Спецификация ключей API пользователя

Discourse содержит систему для генерации ключей API для каждого пользователя, если соблюдается очень специфичный протокол. Эта функция облегчает доступ «приложений» к экземплярам Discourse без необходимости привлечения модераторов.

Краткое описание

В общих чертах:

  1. Клиент (настольное приложение, браузерное расширение, мобильное приложение) генерирует пару закрытого/открытого ключей и URL-адрес возврата.

  2. Клиент перенаправляется на маршрут в Discourse, предоставляя ему свой открытый ключ.

  3. Discourse получает одобрение пользователя на использование приложения.

  4. Discourse генерирует пользовательский ключ API.

  5. Discourse перенаправляет обратно на URL-адрес возврата с зашифрованной полезной нагрузкой, используя открытый ключ API, содержащий пользовательский ключ API.

Детали

Сценарии использования:

  1. Настольные приложения, которые опрашивают сайты Discourse от имени конечных пользователей, чтобы получить количество уведомлений на нескольких сайтах.

  2. Мобильные приложения, которые опрашивают сайты Discourse от имени конечных пользователей и обрабатывают push-уведомления.

  3. Веб-приложения, которые предоставляют панель управления для конечных пользователей по различным сайтам Discourse.

  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: список сайтов, которые могут быть целевыми для push-уведомлений.

  • allowed_user_api_auth_redirects: разрешенные места перенаправления после генерации пользовательского ключа API.

Глобальные настройки

  • max_user_api_reqs_per_minute: 50
  • max_user_api_reqs_per_day: 4000

Элементы пользовательского интерфейса

Если были выданы какие-либо пользовательские ключи API, Discourse отображает вкладку приложения на странице пользователя.

Вкладка приложения будет содержать:

  • Название приложения, например: («Discourse Notifier»)
  • Дата последнего использования
  • Дата одобрения
  • Список предоставленных областей доступа
  • Кнопка отозвать доступ, чтобы вы могли легко отозвать любые ключи

Пользовательский интерфейс авторизации ключа API

Каждый ключ должен быть явно авторизован конечными пользователями на странице, которая четко объясняет, что происходит, например:

«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: название приложения, выполняющего запрос (будет отображаться на вкладке «Приложения» учетной записи пользователя).
  • client_id: уникальный идентификатор клиента.
  • nonce: уникальный nonce, сгенерированный клиентом. Он будет возвращен в зашифрованной полезной нагрузке, чтобы клиент мог проверить подлинность ответа.
  • scopes: список областей доступа, разрешенных для ключа, разделенный запятыми, см. allow user api key scopes для полного списка доступных областей.
  • push_url: URL-адрес для отправки push-уведомлений (требуется и действителен только если в областях указаны push или notifications).
  • public_key: открытая часть пары ключей, сгенерированной клиентом.
  • padding (необязательно): режим заполнения RSA для шифрования полезной нагрузки. Принимает значения pkcs1 (по умолчанию) или oaep. OAEP рекомендуется для новых приложений.

После вызова /user-api-key/new с правильными параметрами может произойти одно из двух событий:

  1. Если пользователь не вошел в систему, мы перенаправим его на страницу входа (после входа мы возобновим авторизацию).
  2. После входа пользователя ему будет представлен пользовательский интерфейс авторизации.

После разрешения авторизации система перенаправит обратно на URL-адрес, определенный в auth_redirect, и включит зашифрованный параметр payload, содержащий JSON-объект с сгенерированным пользовательским ключом API (key), nonce, статусом push (push) и версией API (api). Если была запрошена область one_time_password, также будет включен отдельный зашифрованный параметр запроса oneTimePassword. client_id не возвращается для дополнительной безопасности.

Проверка версии API

API ключей пользователей в Discourse имеет версии. Клиенты могут проверить версию API сайта Discourse, отправив HEAD-запрос на https://sitename.com/user-api-key/new. Ответ будет содержать заголовок с именем Auth-Api-Version, содержащий номер версии API сайта.

Использование API

Использование клиентского API будет несколько отличаться от текущего административного API.

Клиент может указать 2 заголовка:

User-Api-Key (обязательно): сгенерированный ключ

и

User-Api-Client-Id (необязательно): укажите это, чтобы обновить ‘client id’, сохраненный для этого ключа API в базе данных.

После указания этих заголовков клиент может выполнять запросы к API, как обычно.

Генерация одноразового пароля для входа

Начиная с версии 4, API включает специальную область: область one_time_password, которая позволяет клиентам использовать пользовательский ключ API для генерации одноразового пароля. Если клиент включает эту область при генерации ключа API, следуя приведенным выше шагам, зашифрованный oneTimePassword будет включен как отдельный параметр запроса при перенаправлении обратно клиенту.

Альтернативно, клиент может отправить GET-запрос на /user-api-key/otp со следующими параметрами:

  • auth_redirect
  • application_name
  • public_key
  • padding (необязательно)

и с заголовком User-Api-Key.

Этот запрос перенаправит на экран в Discourse, который попросит пользователя разрешить приложению доступ к сайту. Если пользователь одобрит, сайт перенаправит обратно на URL-адрес, определенный в auth_redirect, и включит зашифрованный параметр oneTimePassword с одноразовым паролем, который клиент может использовать для входа на сайт, запросив https://sitename.com/session/otp/ONE-TIME-PASSWORD. (Одноразовый пароль действителен только в течение 10 минут.)

Отзыв ключей API

Чтобы отозвать ключ API, отправьте POST-запрос с заголовком User-Api-Key и без параметров на /user-api-key/revoke.


Последний обзор выполнен @SaraDev 2022-07-13T00:00:00Z

31 лайк
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
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
Can non-admin user issue their own API key?
Automatic Login from iOS/Android app
Delegated authentication for Discourse Mobile app
Generating User Api Keys with REST API
Generating User Api Keys with REST API
API CORS Headers Incorrect
Get back Username with API Key?
Is there any documentation on the User/Mobile 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?
"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
Acess-Control-Allow-Headers CORS Error with API after updating discourse
CORS error accessing API from javascript application
Clip To Discourse Chrome Extension - now with User API capability!
Unable to create "Single User" level API key, always defaults to "All Users"
Discourse index
Clip To Discourse Chrome Extension - now with User API capability!
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
Create apikey for user programmatically as admin
How to fix 'Cannot read property '_links' of undefined'
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

Всем привет

Извините, если это дубликат, но я с трудом разбираюсь в описанных шагах, хотя они очень хорошо структурированы.

У меня установлена Discourse по адресу https://forum.domain.com, а сайт работает по адресу https://development.domain.com. С сайта мне нужно выполнять запросы к установке Discourse для получения и установки некоторых данных.

Все запросы из Postman работают без проблем с использованием Api-Username и Api-Key.

Однако при кросс-доменных запросах через JS 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

И наконец, позволит ли этот сценарий выполнять бесшовные API-запросы с https://development.domain.com на https://forum.domain.com без необходимости дополнительной аутентификации?

P.S. У меня настроен SSO между форумом и сайтом, с которого выполняются запросы, поэтому пользователь будет авторизован.

Спасибо за любые рекомендации.

Подождите секунду, это настраивается для каждого пользователя? Вы разрешаете произвольным пользователям это делать?

Наш серверный API сейчас поддерживает аутентификацию на основе заголовков, поэтому он может работать с CORS так же, как и пользовательские API-ключи. Вы будете использовать пользовательские API-ключи, если хотите ограничить области доступа и позволить конечным пользователям генерировать ключи, а не администраторам.

2 лайка

Я не думаю, что каждому пользователю нужен отдельный ключ… Мне достаточно одного административного ключа.

Я без проблем могу использовать API через Postman. Например, GET-запрос к /notifications.json?username=alanmurphy возвращает данные без проблем, используя только заголовок api-key.

Если я запускаю этот запрос из консоли установки 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

Если бы вы могли подсказать, какие параметры аутентификации передавать для междоменных запросов и где их получить, это было бы очень полезно.

P.S. Мой Discourse настроен на разрешение междоменных запросов с этого домена, поэтому, полагаю, проблема исключительно в заголовках.

Спасибо.

1 лайк