branch: externals/url-http-oauth
commit 86489302c3963884e48c7261c417f2558844ff58
Author: Joe Humphreys <[email protected]>
Commit: Thomas Fitzsimmons <[email protected]>
Improve compatibility with certain oauth servers
* url-http-oauth.el (url-http-oauth--secret-auth-source): New
function.
(url-http-oauth--basic-auth): New function.
(url-http-oauth--get-access-token-grant): Make use of new
functions.
(url-http-oauth--save-bearer): Add optional argument for the
existing refresh token, and preserve it if a new refresh token is
not issued.
(url-http-oauth--refresh-access-token-grant): Include an
Authorization header with refresh requests.
(url-http-oauth--get-bearer): Retrieve the existing refresh token,
and pass it to url-http-oauth--save-bearer.
(url-oauth-auth): Encode the bearer token as utf-8 to fix an error
when making binary POST requests.
---
url-http-oauth.el | 72 +++++++++++++++++++++++++++++++++++++++----------------
1 file changed, 51 insertions(+), 21 deletions(-)
diff --git a/url-http-oauth.el b/url-http-oauth.el
index d53d5267db..d280e07712 100644
--- a/url-http-oauth.el
+++ b/url-http-oauth.el
@@ -238,6 +238,19 @@ server to receive a new access token."
(let ((auth-source-do-cache nil)) ; Do not cache nil result.
(apply #'auth-source-search spec)))))
+(defun url-http-oauth--secret-auth-source (url-settings &optional prompt)
+ "Find the `auth-source' entry for the token endpoint in URL-SETTINGS.
+This entry should hold the client secret. Arrange for the entry to be
+created if it is not already saved in one of `auth-sources', using
+PROMPT to ask for the client secret."
+ (let* ((access-token-url (cdr (assoc "access-token-endpoint" url-settings)))
+ (client-identifier (cdr (assoc "client-identifier" url-settings)))
+ (client-secret-method (cdr (assoc "client-secret-method"
+ url-settings))))
+ (when client-secret-method
+ (url-http-oauth--auth-source-search
+ access-token-url client-identifier nil prompt))))
+
(defun url-http-oauth--parse-grant ()
"Parse the JSON grant structure in the current buffer.
Return the parsed JSON object."
@@ -259,21 +272,12 @@ endpoint."
(let* ((url-request-method "POST")
(access-token-url (cdr (assoc "access-token-endpoint" url-settings)))
(client-identifier (cdr (assoc "client-identifier" url-settings)))
- (client-secret-method (cdr (assoc "client-secret-method"
- url-settings)))
- (auth-result
- (when client-secret-method
- (url-http-oauth--auth-source-search
- access-token-url client-identifier nil
- "Client secret for %u at %h: ")))
+ (auth-result (url-http-oauth--secret-auth-source
+ url-settings
+ "Client secret for %u at %h: "))
(client-secret (url-http-oauth--auth-info-password auth-result))
(save-function (plist-get auth-result :save-function))
- (authorization (when client-secret
- (concat
- "Basic "
- (base64-encode-string
- (format "%s:%s" client-identifier client-secret)
- t))))
+ (authorization (url-http-oauth--basic-auth client-identifier
client-secret))
(url-request-extra-headers
(apply #'list
(cons "Content-Type" "application/x-www-form-urlencoded")
@@ -303,6 +307,18 @@ endpoint."
(error "url-http-oauth: Failed to get access token with %s"
(buffer-string))))))
+(defun url-http-oauth--basic-auth (client-identifier client-secret)
+ "Return a header value for doing basic authentication.
+CLIENT-IDENTIFIER and CLIENT-SECRET are credentials for the Emacs
+library or mode. The returned value can be used in an Authorization
+header to authenticate to the authorization server."
+ (when client-secret
+ (concat
+ "Basic "
+ (base64-encode-string
+ (format "%s:%s" client-identifier client-secret)
+ t))))
+
(defun url-http-oauth--expiry-string (grant)
"Return as a string a number representing the expiry time of GRANT.
The time is in seconds since the epoch."
@@ -474,17 +490,20 @@ The entry is cleared from the `password-data' cache after
the
:port (or port t)))
nil)))))))))))
-(defun url-http-oauth--save-bearer (url grant)
+(defun url-http-oauth--save-bearer (url grant &optional old-refresh-token)
"Save bearer access token for URL from GRANT.
URL is a string or an object. GRANT is a parsed JSON object.
-Save the bearer token to `auth-sources' then return it."
+OLD-REFRESH-TOKEN is a string containing the previously issued refresh
+token, if any. Save the bearer token to `auth-sources' then return it."
(url-http-oauth--netrc-delete url)
(let* ((bearer-retrieved (gethash "access_token" grant))
(auth-result
(url-http-oauth--auth-source-search
url nil bearer-retrieved nil
(url-http-oauth--expiry-string grant)
- (url-http-oauth--refresh-token-string grant)))
+ (or
+ (url-http-oauth--refresh-token-string grant)
+ old-refresh-token)))
(save-function (plist-get auth-result :save-function)))
(when (functionp save-function)
(funcall save-function))
@@ -503,6 +522,15 @@ Save the bearer token to `auth-sources' then return it."
"Refresh access token using URL-SETTINGS."
(let* ((url-request-method "POST")
(access-token-url (cdr (assoc "access-token-endpoint" url-settings)))
+ (client-identifier (cdr (assoc "client-identifier" url-settings)))
+ (auth-result (url-http-oauth--secret-auth-source url-settings))
+ (client-secret (url-http-oauth--auth-info-password auth-result))
+ (authorization (url-http-oauth--basic-auth client-identifier
client-secret))
+ (url-request-extra-headers
+ (apply #'list
+ (cons "Content-Type" "application/x-www-form-urlencoded")
+ (when authorization
+ (list (cons "Authorization" authorization)))))
(url-request-data (url-http-oauth--url-build-refresh url-settings)))
(with-current-buffer (url-retrieve-synchronously access-token-url)
(if (eq 'OK (car (alist-get url-http-response-status url-http-codes)))
@@ -534,13 +562,15 @@ Save the bearer token to `auth-sources' upon success."
"Prompt the user with the authorization endpoint for URL.
URL is a parsed object."
(let* ((url-settings (url-http-oauth--settings url))
- (resource-url (cdr (assoc "resource-url" url-settings))))
- (let ((expiry (plist-get (url-http-oauth--auth-source-search resource-url)
- :expiry)))
+ (resource-url (cdr (assoc "resource-url" url-settings)))
+ (auth-source (url-http-oauth--auth-source-search resource-url)))
+ (let ((expiry (plist-get auth-source :expiry))
+ (refresh-token (plist-get auth-source :refresh-token)))
(when (and expiry (> (time-to-seconds) (string-to-number expiry)))
(url-http-oauth--save-bearer
resource-url
- (url-http-oauth--refresh-access-token-grant url-settings))))
+ (url-http-oauth--refresh-access-token-grant url-settings)
+ refresh-token)))
(let ((bearer-current (url-http-oauth--auth-info-password
(url-http-oauth--auth-source-search resource-url))))
(or bearer-current
@@ -557,7 +587,7 @@ permissions that the caller is requesting."
(when (url-http-oauth-interposed-p url)
(let ((bearer (url-http-oauth--get-bearer url)))
(if bearer
- (concat "Bearer " bearer)
+ (concat "Bearer " (encode-coding-string bearer 'utf-8))
(error "url-http-oauth: Bearer retrieval failed for %s" url)))))
;;; Register `url-oauth-auth' HTTP authentication method.