branch: externals/url-http-oauth commit e95f6857719d4781a9f51659e3cbd6f65f32ca04 Author: Thomas Fitzsimmons <fitz...@fitzsim.org> Commit: Thomas Fitzsimmons <fitz...@fitzsim.org>
Begin auth-source implementation * url-http-oauth.el (url-http-oauth-register-resource): Add client-secret-required argument. (url-http-response-status): Define variable. (url-http-oauth-get-access-token): Start client-secret implementation. Start auth-source implementation. --- url-http-oauth.el | 99 ++++++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 76 insertions(+), 23 deletions(-) diff --git a/url-http-oauth.el b/url-http-oauth.el index 78ef0dafe1..5257d3b9f5 100644 --- a/url-http-oauth.el +++ b/url-http-oauth.el @@ -61,7 +61,9 @@ URL is either a URL object or a URL string." authorization-url access-token-url client-identifier - scope) + scope + &optional + client-secret-required) "Tell Emacs that to access URL, it needs to use OAuth 2.0. URL will be accessed by Emacs's `url' library with a suitable \"Authorization\" header containing \"Bearer <token>\". @@ -70,17 +72,27 @@ AUTHORIZATION-URL and ACCESS-TOKEN-URL will be used to acquire AUTHORIZATION-URL and ACCESS-TOKEN-URL are either URL objects or URL strings. CLIENT-IDENTIFIER is a string identifying an Emacs library or mode to the server. SCOPE is a string defining the -permissions that the Emacs library or mode is requesting." +permissions that the Emacs library or mode is requesting. +CLIENT-SECRET-REQUIRED is the symbol `prompt' if a client secret +is required, nil otherwise." (unless url-http-oauth--registered-oauth-urls (setq url-http-oauth--registered-oauth-urls (make-hash-table :test #'equal))) (let ((key (url-http-oauth-url-string url)) (authorization (url-http-oauth-url-string authorization-url)) (access-token (url-http-oauth-url-string access-token-url))) - (puthash key (list authorization access-token client-identifier scope) + (puthash key (list authorization access-token client-identifier scope + (cond + ((eq client-secret-required 'prompt) 'prompt) + ((eq client-secret-required nil) nil) + (t (error + "Unrecognized client-secret-required value")))) url-http-oauth--registered-oauth-urls))) -;; (car (auth-source-search :host "https://meta.sr.ht/oauth2/access-token" :user "107ba4a9-2a96-4420-8818-84ec1f112405" :max 1)) +;; (car (auth-source-search :host "https://meta.sr.ht/oauth2/access-token" :login "107ba4a9-2a96-4420-8818-84ec1f112405" :max 1)) + +(defvar url-http-response-status) + (defun url-http-oauth-get-access-token (url code) "Get an access token for URL using CODE." (let* ((url-request-method "POST") @@ -88,16 +100,29 @@ permissions that the Emacs library or mode is requesting." (url-list (url-http-oauth-configuration key-url)) (access-token-url (nth 1 url-list)) (client-identifier (nth 2 url-list)) - (client-secret - (auth-info-password - (car (auth-source-search :host access-token-url - :user client-identifier - :max 1)))) - (authorization (concat "Basic " - (base64-encode-string - (format "%s:%s" client-identifier - client-secret) - t))) + (client-secret-required (nth 4 url-list)) + (client-secret-current (when client-secret-required + (auth-info-password + (car (auth-source-search + :host access-token-url + ;; FIXME: Why doesn't :user + ;; work here, but :login + ;; does? + :login client-identifier + :max 1))))) + (client-secret-read (unless client-secret-current + (when client-secret-required + (read-from-minibuffer + (format "Client secret for %s at %s: " + client-identifier key-url))))) + (authorization (concat + "Basic " + (base64-encode-string + (format "%s:%s" client-identifier + (or client-secret-current client-secret-read + ;; FIXME what to do if not required? + "")) + t))) (url-request-extra-headers (list (cons "Content-Type" "application/x-www-form-urlencoded") (cons "Authorization" authorization))) @@ -122,15 +147,38 @@ permissions that the Emacs library or mode is requesting." ;; :expiry (gethash "expires_in" grant) ;; :create t)))))) (with-current-buffer (url-retrieve-synchronously access-token-url) - (message "BUFFER-STRING: %s" (buffer-string)) - (goto-char (point-min)) - (re-search-forward "\n\n") - (let* ((grant (json-parse-buffer)) - (type (gethash "token_type" grant))) - (message "GRANT: %S" grant) - (unless (equal type "bearer" ) - (error "Unrecognized token type: %s" type)) - (gethash "access_token" grant))))) + (if (eq 'OK (car (alist-get url-http-response-status url-http-codes))) + (progn + (message "BUFFER-STRING: %s" (buffer-string)) ; FIXME: remove after testing. + (goto-char (point-min)) + (re-search-forward "\n\n") + (let* ((grant (json-parse-buffer)) + (type (gethash "token_type" grant))) + (message "GRANT: %S" grant) ; FIXME: remove after testing. + (unless (equal type "bearer" ) + (error "Unrecognized token type %s for %s at %s" type + client-identifier key-url)) + ;; Success, so save client secret, if necessary. + (when (and (not client-secret-current) + client-secret-read) + (let* ((auth-result (auth-source-search + :host access-token-url + ;; FIXME: Why does :user here get + ;; translated to "login" in + ;; authinfo.gpg? + :user client-identifier + :secret client-secret-read + :create t)) + (save-function (plist-get (car auth-result) + :save-function))) + (if (functionp save-function) + (funcall save-function) + (warn "Saving client secret for %s at %s failed" + client-identifier key-url)))) + ;; Return access token string. + (gethash "access_token" grant))) + (error "url-http-oauth: Failed to get access token with %s" + (buffer-string)))))) (defun url-http-oauth-extract-authorization-code (url) "Extract the value of the code parameter in URL." @@ -171,6 +219,11 @@ permissions that the Emacs library or mode is requesting." ;; (funcall (plist-get (car (auth-source-search :host "https://meta.sr.ht/query" :secret "example" :expiry 86399 :create t)) :save-function)) ))) +;; (setq fitzsim-banana (auth-source-search :host "banana" :secret "orange3" :create t)) + +;; Works, but need +;; (when (functionp (plist-get (car fitzsim-banana) :save-function)) (funcall (plist-get (car fitzsim-banana) :save-function))) + ;;(defvar url-http-oauth-testval nil "Test value.") ;;(setq url-http-oauth-testval nil) ;;(setq url-http-oauth-testval (url-http-oauth-get-authorization-code "https://meta.sr.ht/query"))