SE在Emacs中的OAuth工作流程

时间:2014-11-03 00:05:46

标签: emacs oauth-2.0 elisp stackexchange-api

StackMode, an Emacs client for StackExchange正在快速取得进展,现在我们需要能够对API进行authenticated requests以继续测试。 (300请求限制开始限制我一天可以做多少测试。)

免责声明:我知道非常关于网络开发;这是我正在专业领域的一个领域。如果我滥用任何条款,请随时在评论中纠正我,请原谅。谢谢!

StackExchange API使用OAuth 2.0身份验证。由于这是具有客户端授权的本地客户端应用程序。我有StackExchange提供给我的以下信息:

  • 客户ID
  • 客户端密钥(不得共享,因此在此流程中不需要)
  • 关键
  • 说明(与OAuth无关)
  • OAuth域
  • Application Website(与OAuth无关)
  • 应用程序图标(与OAuth无关)
  • Stack Apps Post(与OAuth无关)

提供以下额外信息:

  • 启用客户端流程
  • 桌面OAuth重定向Uri已启用

为了保持一般和明确的任何答案,您可以使用my-client-id(等)来表示值。我认为我可以分享的实际值是available on 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请求的唯一事情(从上面)显然是

  • 客户ID
  • 关键
  • OAuth域

如何在Elisp中实现此流程?


当前'流量'

  1. 执行oauth2-auth-and-store并设置适当的变量。
  2. 打开

    auth

  3. 点击“批准”
  4. 打开

    page

    使用此网址

    url

  5. 已成功添加应用

    added

  6. 但我没有提供oauth2

    的代码

    prompt

  7. 除了答案之外,当然也欢迎PR。

2 个答案:

答案 0 :(得分:3)

这是一个简单的例子。简而言之,这将在客户端浏览器中打开auth url,要求用户允许该应用,然后重定向到docs(隐式身份验证)中所述的/oauth/login_success网址。

此代码提示用户粘贴login_success URL完成,然后解析并保存access_token,然后可以将其用于后续调用api。定义了两个交互式函数:so-authenticate执行上述身份验证步骤,so-read-inbox获取经过身份验证的用户收件箱的api数据并将其转储到消息缓冲区。


警告,此示例没有错误处理!

至少,您需要添加对身份验证失败,API请求失败和令牌过期的检查。您可以在致电so-read-inbox之前尝试致电so-authenticate来查看示例api错误。


要运行,请将以下内容粘贴到缓冲区中,然后设置so--client-idso--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)))

现在很有趣解析响应! :)

答案 1 :(得分:2)

我会尽可能多地回答这个问题。我对Lisp一无所知,但我对Stack Exchange API和授权流程非常熟悉。

  

“300请求限制开始限制我一天可以做多少测试。”

您可以将API密钥附加到方法网址(&key=...)的查询字符串中,将此限制升级为10,000个查询/天。

  

“实际值 - 我认为我可以分享的那些,可以在GitHub上找到。”

是的,您可以安全地分享这些内容,因为任何带有这些值的应用程序都可以轻松地进行逆向工程或反编译以提取值。

  

“4。使用此URL”

打开[...]页面[...]

这是预期的行为。在屏幕截图中,授权成功,URL的哈希包含访问令牌。您需要此令牌才能访问某些方法,例如/inbox

您可能想要做的事情看起来像这样:

  1. 继续前进,直至到达示例中步骤#4的末尾。
  2. 在Emacs中提示用户显示当前显示的网址。他们会按原样复制并粘贴它。
  3. 提取哈希值(最右边的'#'后面的所有内容)并像查询字符串一样解析它。 access_token参数包含您需要的值。
  4. 每次调用受保护的方法时,请使用access_tokenkey(API密钥)参数。