Рабочий процесс SE OAuth в Emacs
Прогресс ускоряется в StackMode, клиент Emacs для StackExchange, и теперь мы должны иметь возможность сделать аутентифицированные запросы API для продолжения тестирования. (Предел 300-запросов начинает ограничивать количество тестов, которые я могу выполнить за один день.)
Отказ от ответственности: Я мало что знаю о веб-разработке; это одна из областей, над которыми я работаю профессионально. Пожалуйста, извините меня, если я злоупотребляю любыми условиями и не стесняюсь исправить меня в комментариях. Спасибо!
API StackExchange использует аутентификацию OAuth 2.0. Поскольку это локальное клиентское приложение с авторизацией клиента. У меня есть следующие сведения, предоставленные мне StackExchange:
- Идентификатор клиента
- Клиентский секрет (не должен делиться, поэтому в этом потоке не нужно)
- Key
- Описание (не связанное с OAuth)
- Домен OAuth
- Веб-сайт приложения (не связанный с OAuth)
- Значок приложения (не связанный с OAuth)
- Сообщение о публикациях в стеке (не связанное с OAuth)
со следующими дополнительными сведениями:
- Поток клиентской стороны включен
- Доступен OAuth Redirect Uri
Чтобы сохранить любой ответ как общий, так и явный, вы можете использовать my-client-id
(и т.д.) для значений. Фактические значения - те, которые, я думаю, согласны, - доступные на GitHub.
Я изучаю это в течение половины дня, но я не очень-то ближе к решению, чем когда начал. Самое близкое, что я получил, это небольшой фрагмент кода:
(require 'oauth2) ; available via GNU ELPA
(defconst stack-auth-token
(make-oauth2-token
:client-id stack-auth--client-id
:client-secret stack-auth--key))
;; this doesn't use the above, but it does open an auth page on SE
(oauth2-auth-and-store
"https://stackexchange.com/oauth/dialog"
nil nil
stack-auth--client-id
stack-auth--key
"https://stackexchange.com/oauth/login_success")
Единственное, что я могу предложить для запроса OAuth2 (сверху), по-видимому,
- Идентификатор клиента
- Key
- Домен OAuth
Как я могу реализовать этот поток в Elisp?
Текущий "поток"
В дополнение к ответам, PR также приветствуются, конечно.
Ответы
Ответ 1
Я постараюсь ответить как можно больше. Я абсолютно ничего не знаю о Lisp, но я очень хорошо знаком с API-интерфейсом Stack Exchange и потоками авторизации.
"Предел 300-запросов начинает ограничивать количество тестов, которые я могу выполнить за один день".
Вы можете обновить это ограничение до 10 000 запросов в день, добавив ключ API в строку запроса URL-адресов методов (&key=...
).
"Фактические значения - те, которые, я думаю, я в порядке, чтобы поделиться, доступны на GitHub".
Yup, вы можете их разделить, поскольку любое приложение, отправляющее эти значения, может быть легко реконструировано или декомпилировано для извлечения значений в любом случае.
"4. Открывает [...] страницу [...] с этим URL-адресом"
Это предполагаемое поведение. На скриншоте авторизация прошла успешно, а хэш URL-адресов содержит токен доступа. Этот токен вам понадобится для доступа к определенным методам, таким как /inbox
.
Что вы, вероятно, хотите сделать, выглядит примерно так:
- Продолжайте, как вы делали, пока не достигнете конца шага №4 в вашем примере.
- Запросить пользователя в Emacs для отображаемого URL-адреса. Они будут копировать и вставлять его как есть.
- Извлеките хэш (все после самого правого '#') и проанализируйте его так же, как и строку запроса. Параметр access_token содержит нужное значение.
- Используйте параметры
access_token
и key
(API) при вызове защищенных методов.
Ответ 2
Вот краткий пример. Короче говоря, это откроет auth url в клиентском браузере, попросит пользователя разрешить приложение, а затем перенаправит URL-адрес /oauth/login_success
, как описано в docs (неявный auth).
В этом коде предлагается пользователю вставить URL-адрес login_success
, затем проанализировать и сохранить access_token
, который затем можно будет использовать для последующих вызовов в api. Определены две интерактивные функции: so-authenticate
, которая выполняет описанные выше шаги аутентификации, и so-read-inbox
, которая извлекает данные api для входящих почтовых ящиков и отправляет их в буфер сообщений.
Предупреждение. В этом примере нет обработки ошибок!
По крайней мере, вы захотите добавить проверки на неудачу аутентификации, сбои запроса api и истечение срока действия токена. Вы можете увидеть пример ошибки api, попробовав вызвать so-read-inbox
перед вызовом so-authenticate
.
Чтобы запустить, вставьте следующее в буфер, установите переменные so--client-id
и so--client-key
, затем M-x eval-buffer
.
Затем вы можете использовать M-x so-authenticate
для аутентификации и M-x so-read-inbox
, чтобы сбрасывать ответ входящих сообщений.
(require 'json)
(defvar so--client-id "") ; SET THIS
(defvar so--client-key "") ; AND THIS
(defvar so--auth-url "https://stackexchange.com/oauth/dialog?")
(defvar so--redirect-url "https://stackexchange.com/oauth/login_success")
(defvar so--api-inbox-url "https://api.stackexchange.com/inbox?")
(defvar so--current-token nil) ; this will get set after authentication
(defun so-authenticate ()
(interactive)
(so--open-auth))
(defun so-read-inbox()
(interactive)
(so--retrieve-inbox))
;; Open auth url in browser and call so--get-save-token.
(defun so--open-auth ()
(let ((auth-url
(concat so--auth-url (url-build-query-string
`((client_id ,so--client-id)
(scope "read_inbox")
(redirect_uri ,so--redirect-url))))))
(browse-url auth-url))
(so--get-save-token))
;; Prompt user for callback URL, extract token and save in so--current-token
(defun so--get-save-token ()
(let* ((post-auth-url-string (read-string "Enter URL from your browser: "))
(token (nth 2 (split-string post-auth-url-string "[[#=&]"))))
(setq so--current-token token)
(message "Saved token: %S" token)))
;; Make a request for our inbox data
(defun so--retrieve-inbox()
(let ((inbox-url (concat so--api-inbox-url
(url-build-query-string
`((access_token ,so--current-token) ; the token from auth
(key ,so--client-key)))))) ; your client key
(url-retrieve inbox-url 'so--retrieve-inbox-cb)))
;; Parse json response for inbox request.
;; This simply dumps the parsed data to your messages buffer.
(defun so--retrieve-inbox-cb (status)
(goto-char (point-min))
(re-search-forward "^$")
(let ((inbox-data (json-read)))
(message "inbox data: %S" inbox-data)))
Теперь получайте удовольствие от анализа ответа!:)