From mboxrd@z Thu Jan 1 00:00:00 1970 Path: news.gmane.io!.POSTED.blaine.gmane.org!not-for-mail From: Aleksandr Vityazev Newsgroups: gmane.emacs.devel Subject: [ELPA/oauth2] Request for patches review Date: Sun, 28 Nov 2021 12:38:58 +0000 Message-ID: <87wnks315p.fsf@posteo.org> Mime-Version: 1.0 Content-Type: multipart/mixed; boundary="=-=-=" Injection-Info: ciao.gmane.io; posting-host="blaine.gmane.org:116.202.254.214"; logging-data="3752"; mail-complaints-to="usenet@ciao.gmane.io" To: emacs-devel@gnu.org, julien@danjou.info Original-X-From: emacs-devel-bounces+ged-emacs-devel=m.gmane-mx.org@gnu.org Sun Nov 28 14:08:22 2021 Return-path: Envelope-to: ged-emacs-devel@m.gmane-mx.org Original-Received: from lists.gnu.org ([209.51.188.17]) by ciao.gmane.io with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.92) (envelope-from ) id 1mrJun-0000ms-Kl for ged-emacs-devel@m.gmane-mx.org; Sun, 28 Nov 2021 14:08:21 +0100 Original-Received: from localhost ([::1]:44312 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1mrJul-000671-Ru for ged-emacs-devel@m.gmane-mx.org; Sun, 28 Nov 2021 08:08:19 -0500 Original-Received: from eggs.gnu.org ([209.51.188.92]:58268) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1mrJSK-0007Tx-9n for emacs-devel@gnu.org; Sun, 28 Nov 2021 07:38:58 -0500 Original-Received: from mout01.posteo.de ([185.67.36.65]:33681) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1mrJSB-00014l-C7 for emacs-devel@gnu.org; Sun, 28 Nov 2021 07:38:56 -0500 Original-Received: from submission (posteo.de [89.146.220.130]) by mout01.posteo.de (Postfix) with ESMTPS id 0558C24002B for ; Sun, 28 Nov 2021 13:38:42 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=posteo.org; s=2017; t=1638103123; bh=J6O5G95zB8zL1AnKmpkyQ85xiR7Rbbb+ANWj1t/h4sM=; h=From:To:Subject:Date:From; b=Z+myO7L3J6GZANxtIQ/k8hMjj2g75+nN+f1cCuzASMwqvSqzKlH4ebcYWvOQ2UXuV uDzv4SZjuat2rtz3vBFeZJS/114H3+/ERSzJ3vttqvvu/diIXlfMRirEmuwhwVz6Z5 m900e69LIxjp+pBzWT8J1vhYF5A5bb1nTye6bRr7kblJzoe53cfOqbRFjIhJTu+raO 1CSyQ86PHr5MKyU6zoVoEdGqXqdS4hz1KleaxKIfD71j56sWQk+hLkRcH8XYaZMuLB 6WPGBkcEwhbRaQvUH1/Ic1PL4Ue1HMxl39WZBaA0YncoxtzfHGnVKY1K+JxIKVtglj +kuKN22mfwemA== Original-Received: from customer (localhost [127.0.0.1]) by submission (posteo.de) with ESMTPSA id 4J27M527L1z6tpk; Sun, 28 Nov 2021 13:38:40 +0100 (CET) Received-SPF: pass client-ip=185.67.36.65; envelope-from=avityazev@posteo.org; helo=mout01.posteo.de X-Spam_score_int: -43 X-Spam_score: -4.4 X-Spam_bar: ---- X-Spam_report: (-4.4 / 5.0 requ) BAYES_00=-1.9, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, RCVD_IN_DNSWL_MED=-2.3, RCVD_IN_MSPIKE_H2=-0.001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001 autolearn=ham autolearn_force=no X-Spam_action: no action X-Mailman-Approved-At: Sun, 28 Nov 2021 08:07:22 -0500 X-BeenThere: emacs-devel@gnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: "Emacs development discussions." List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: emacs-devel-bounces+ged-emacs-devel=m.gmane-mx.org@gnu.org Original-Sender: "Emacs-devel" Xref: news.gmane.io gmane.emacs.devel:280404 Archived-At: --=-=-= Content-Type: text/plain Hello, In a previous post [1], I mentioned that Elpa's oauth2 does not work correctly for me, so I decided to make changes. Some changes probably cannot be made to Elpa, since my version of oauth2 requires Emacs version 27.1, but at least it will be possible to update the doc string for functions. Therefore, I ask you to check the changes so that I correct them where necessary. Here is the repo with the new version [2]. [1] https://lists.gnu.org/archive/html/emacs-devel/2021-11/msg01275.html [2] https://git.sr.ht/~akagi/oauth2/ -- Best regargds, Aleksandr Vityazev --=-=-= Content-Type: text/x-patch; charset=utf-8 Content-Disposition: attachment; filename=0001-oauth2-Update-version-to-0.17.patch Content-Transfer-Encoding: quoted-printable >From e52ba22450bec9b77afdd943a03d79b2f89870da Mon Sep 17 00:00:00 2001 Message-Id: In-Reply-To: References: From: Aleksandr Vityazev Date: Thu, 25 Nov 2021 10:39:08 +0300 Subject: [PATCH 01/11] * oauth2: Update version to 0.17 * oauth2: Update version to 0.17, require Emacs version 27.1. (oauth2-request-authorization, oauth2-request-access): use cl-defun, update doc string. (oauth2-token): make read-only. (oauth2-request-access): update accordingly to changes (oauth2-auth, oauth2-compute-id, oauth2-url-append-access-token): Update do= c string. (oauth2-auth-and-store): change order of args and update doc string. (oauth2--url-advice): Rename. --- oauth2.el | 244 ++++++++++++++++++++++++++++++++---------------------- 1 file changed, 145 insertions(+), 99 deletions(-) diff --git a/oauth2.el b/oauth2.el index 0099e06..1444890 100644 --- a/oauth2.el +++ b/oauth2.el @@ -1,11 +1,13 @@ ;;; oauth2.el --- OAuth 2.0 Authorization Protocol -*- lexical-binding:t = -*- =20 +;; Copyright =C2=A9 2021 Aleksandr Vityazev ;; Copyright (C) 2011-2021 Free Software Foundation, Inc =20 ;; Author: Julien Danjou -;; Version: 0.16 +;; Version: 0.17 ;; Keywords: comm -;; Package-Requires: ((cl-lib "0.5") (nadvice "0.3")) +;; Package-Requires: ((emacs "27.1")) +;; Homepage: https://git.sr.ht/~akagi/oauth2 =20 ;; This file is part of GNU Emacs. =20 @@ -36,11 +38,19 @@ =20 ;;; Code: =20 -(eval-when-compile (require 'cl-lib)) +(eval-when-compile + (require 'cl-lib) + (require 'subr-x)) + (require 'plstore) (require 'json) (require 'url-http) =20 +(defcustom oauth2-token-file (concat user-emacs-directory "oauth2.plstore") + "File path where store OAuth tokens." + :type 'file + :group 'oauth2) + (defvar url-http-data) (defvar url-http-method) (defvar url-http-extra-headers) @@ -53,15 +63,22 @@ :link '(url-link :tag "Savannah" "http://git.savannah.gnu.org/cgit/emacs= /elpa.git/tree/?h=3Dexternals/oauth2") :link '(url-link :tag "ELPA" "https://elpa.gnu.org/packages/oauth2.html"= )) =20 -(defun oauth2-request-authorization (auth-url client-id &optional scope st= ate redirect-uri) +(cl-defun oauth2-request-authorization (auth-url client-id &optional scope= state + (redirect-uri "urn:ietf:wg:oaut= h:2.0:oob")) "Request OAuth authorization at AUTH-URL by launching `browse-url'. CLIENT-ID is the client id provided by the provider. -It returns the code provided by the service." +The REDIRECT-URI is the registered redirect_uri for your CLIENT-ID, +the default for the desktop applications is \"urn:ietf:wg:oauth:2.0:oob\". +SCOPE identify the resources that your application could access +on the user's behalf. STATE it is a string that your application +uses to maintain state between the request and redirect response. + +Return the code provided by the service." (browse-url (concat auth-url (if (string-match-p "\?" auth-url) "&" "?") "client_id=3D" (url-hexify-string client-id) "&response_type=3Dcode" - "&redirect_uri=3D" (url-hexify-string (or redirect-u= ri "urn:ietf:wg:oauth:2.0:oob")) + "&redirect_uri=3D" (url-hexify-string redirect-uri) (if scope (concat "&scope=3D" (url-hexify-string sco= pe)) "") (if state (concat "&state=3D" (url-hexify-string sta= te)) ""))) (read-string "Enter the code your browser displayed: ")) @@ -84,21 +101,27 @@ It returns the code provided by the service." data)))) =20 (cl-defstruct oauth2-token - plstore - plstore-id - client-id - client-secret - access-token - refresh-token - token-url - access-response) - -(defun oauth2-request-access (token-url client-id client-secret code &opti= onal redirect-uri) + (plstore "" :read-only t) + (plstore-id "" :read-only t) + (client-id "" :read-only t) + (client-secret "" :read-only t) + (access-token "" :read-only t) + (refresh-token "" :read-only t) + (token-url "" :read-only t) + (access-response "" :read-only t)) + +(cl-defun oauth2-request-access (token-url client-id client-secret code &o= ptional + (redirect-uri "urn:ietf:wg:oauth:2.0:= oob")) "Request OAuth access at TOKEN-URL. +CLIENT-ID is the client id provided by the provider. +CLIENT-SECRET is the client secret associated with your CLIENT-ID. +The REDIRECT-URI is the registered redirect_uri for your CLIENT-ID, +the default for the desktop applications is \"urn:ietf:wg:oauth:2.0:oob\". The CODE should be obtained with `oauth2-request-authorization'. -Return an `oauth2-token' structure." + +Return an response from requested service." (when code - (let ((result + (let ((response (oauth2-make-access-request token-url (concat @@ -106,102 +129,125 @@ Return an `oauth2-token' structure." (when client-secret (concat "&client_secret=3D" client-secret)) "&code=3D" code - "&redirect_uri=3D" (url-hexify-string (or redirect-uri "urn:i= etf:wg:oauth:2.0:oob")) + "&redirect_uri=3D" (url-hexify-string redirect-uri) "&grant_type=3Dauthorization_code")))) - (make-oauth2-token :client-id client-id - :client-secret client-secret - :access-token (cdr (assoc 'access_token result)) - :refresh-token (cdr (assoc 'refresh_token result)) - :token-url token-url - :access-response result)))) + response))) =20 ;;;###autoload (defun oauth2-refresh-access (token) "Refresh OAuth access TOKEN. TOKEN should be obtained with `oauth2-request-access'." - (setf (oauth2-token-access-token token) - (cdr (assoc 'access_token - (oauth2-make-access-request - (oauth2-token-token-url token) - (concat "client_id=3D" (oauth2-token-client-id token) - (when (oauth2-token-client-secret token) - (concat "&client_secret=3D" (oauth2-token-c= lient-secret token))) - "&refresh_token=3D" (oauth2-token-refresh-tok= en token) - "&grant_type=3Drefresh_token"))))) - ;; If the token has a plstore, update it - (let ((plstore (oauth2-token-plstore token))) - (when plstore - (plstore-put plstore (oauth2-token-plstore-id token) - nil `(:access-token - ,(oauth2-token-access-token token) - :refresh-token - ,(oauth2-token-refresh-token token) - :access-response - ,(oauth2-token-access-response token) - )) - (plstore-save plstore))) - token) + (let ((plstore (oauth2-token-plstore token)) + (plstore-id (oauth2-token-plstore-id token)) + (client-id (oauth2-token-client-id token)) + (client-secret (oauth2-token-client-secret token)) + (token-url (oauth2-token-token-url token))) + (let* ((response (oauth2-make-access-request + (oauth2-token-token-url token) + (concat "client_id=3D" client-id + (when client-secret + (concat "&client_secret=3D" client-secret)) + "&refresh_token=3D" (oauth2-token-refresh-to= ken token) + "&grant_type=3Drefresh_token"))) + (new-token (make-oauth2-token :plstore plstore + :plstore-id plstore-id + :client-id client-id + :client-secret client-secret + :access-token (cdr (assoc 'access= _token response)) + :refresh-token (cdr (assoc 'refre= sh_token response)) + :token-url token-url + :access-response response))) + (when plstore + (plstore-put plstore plstore-id + nil `(:access-token + ,(oauth2-token-access-token new-token) + :refresh-token + ,(oauth2-token-refresh-token new-token) + :access-response + ,(oauth2-token-access-response new-token))) + (plstore-save plstore)) + new-token))) =20 ;;;###autoload (defun oauth2-auth (auth-url token-url client-id client-secret &optional s= cope state redirect-uri) - "Authenticate application via OAuth2." - (oauth2-request-access - token-url - client-id - client-secret - (oauth2-request-authorization - auth-url client-id scope state redirect-uri) - redirect-uri)) + "Authenticate application via OAuth2 at AUTH-URL and TOKEN-URL. +CLIENT-ID is the client id provided by the provider. +CLIENT-SECRET is the client secret associated with your CLIENT-ID. +The REDIRECT-URI is the registered redirect_uri for your CLIENT-ID. +SCOPE identify the resources that your application could access +on the user's behalf. STATE it is a string that your application +uses to maintain state between the request and redirect response. =20 -(defcustom oauth2-token-file (concat user-emacs-directory "oauth2.plstore") - "File path where store OAuth tokens." - :group 'oauth2 - :type 'file) +Return an `oauth2-token' structure." + + (let ((plstore (plstore-open oauth2-token-file)) + (id (oauth2-compute-id auth-url token-url scope)) + (response (oauth2-request-access + token-url + client-id + client-secret + (oauth2-request-authorization + auth-url client-id scope state redirect-uri) + redirect-uri))) + (make-oauth2-token :plstore plstore + :plstore-id id + :client-id client-id + :client-secret client-secret + :access-token (cdr (assoc 'access_token response)) + :refresh-token (cdr (assoc 'refresh_token response)) + :token-url token-url + :access-response response))) =20 (defun oauth2-compute-id (auth-url token-url scope) - "Compute an unique id based on URLs. + "Compute an unique id based on AUTH-URL, TOKEN-URL and SCOPE. This allows to store the token in an unique way." (secure-hash 'md5 (concat auth-url token-url scope))) =20 ;;;###autoload -(defun oauth2-auth-and-store (auth-url token-url scope client-id client-se= cret &optional redirect-uri state) - "Request access to a resource and store it using `plstore'." - ;; We store a MD5 sum of all URL - (let* ((plstore (plstore-open oauth2-token-file)) - (id (oauth2-compute-id auth-url token-url scope)) - (plist (cdr (plstore-get plstore id)))) - ;; Check if we found something matching this access - (if plist - ;; We did, return the token object - (make-oauth2-token :plstore plstore - :plstore-id id - :client-id client-id - :client-secret client-secret - :access-token (plist-get plist :access-token) - :refresh-token (plist-get plist :refresh-token) - :token-url token-url - :access-response (plist-get plist :access-respo= nse)) - (let ((token (oauth2-auth auth-url token-url - client-id client-secret scope state redire= ct-uri))) - ;; Set the plstore - (setf (oauth2-token-plstore token) plstore) - (setf (oauth2-token-plstore-id token) id) - (plstore-put plstore id nil `(:access-token - ,(oauth2-token-access-token token) - :refresh-token - ,(oauth2-token-refresh-token token) - :access-response - ,(oauth2-token-access-response token= ))) - (plstore-save plstore) - token)))) +(defun oauth2-auth-and-store (auth-url token-url client-id client-secret s= cope &optional state redirect-uri) + "Request access to a resource and store it using `plstore'. +If the token has not yet been saved in plsote, then authenticate +the application via OAuth2 using the AUTH-URL, TOKEN-URL and after that +save the token in the `oauth2-token-file'. +CLIENT-ID is the client id provided by the provider. +CLIENT-SECRET is the client secret associated with your CLIENT-ID. +The REDIRECT-URI is the registered redirect_uri for your CLIENT-ID. +SCOPE identify the resources that your application could access +on the user's behalf. STATE it is a string that your application +uses to maintain state between the request and redirect response. + +Return an `oauth2-token' structure." + (if-let ((plstore (plstore-open oauth2-token-file)) + (id (oauth2-compute-id auth-url token-url scope)) + (plist (cdr (plstore-get plstore id)))) + ;; Check if we found something matching this access + ;; We did, return the token object + (make-oauth2-token :plstore plstore + :plstore-id id + :client-id client-id + :client-secret client-secret + :access-token (plist-get plist :access-token) + :refresh-token (plist-get plist :refresh-token) + :token-url token-url + :access-response (plist-get plist :access-respons= e)) + (let ((token (oauth2-auth auth-url token-url + client-id client-secret scope state redirect-uri))) + (plstore-put plstore id nil `(:access-token + ,(oauth2-token-access-token token) + :refresh-token + ,(oauth2-token-refresh-token token) + :access-response + ,(oauth2-token-access-response token))) + (plstore-save plstore) + token))) =20 (defun oauth2-url-append-access-token (token url) - "Append access token to URL." + "Append access TOKEN to URL." (concat url (if (string-match-p "\?" url) "&" "?") "access_token=3D" (oauth2-token-access-token token))) =20 -(defvar oauth--url-advice nil) +(defvar oauth2--url-advice nil) (defvar oauth--token-data) =20 (defun oauth2-authz-bearer-header (token) @@ -216,7 +262,7 @@ This allows to store the token in an unique way." =20 ;; FIXME: We should change URL so that this can be done without an advice. (defun oauth2--url-http-handle-authentication-hack (orig-fun &rest args) - (if (not oauth--url-advice) + (if (not oauth2--url-advice) (apply orig-fun args) (let ((url-request-method url-http-method) (url-request-data url-http-data) @@ -227,8 +273,8 @@ This allows to store the token in an unique way." url-callback-function url-callback-arguments) ;; This is to make `url' think it's done. - (when (boundp 'success) (setq success t)) ;For URL library in Emacs<= 24.4. - t))) ;For URL library in Emacs= =E2=89=A524.4. + t))) + (advice-add 'url-http-handle-authentication :around #'oauth2--url-http-handle-authentication-hack) =20 @@ -237,7 +283,7 @@ This allows to store the token in an unique way." "Retrieve an URL synchronously using TOKEN to access it. TOKEN can be obtained with `oauth2-auth'." (let* ((oauth--token-data (cons token url))) - (let ((oauth--url-advice t) ;Activate our advice. + (let ((oauth2--url-advice t) ;Activate our advice. (url-request-method request-method) (url-request-data request-data) (url-request-extra-headers @@ -246,14 +292,14 @@ TOKEN can be obtained with `oauth2-auth'." =20 ;;;###autoload (defun oauth2-url-retrieve (token url callback &optional - cbargs - request-method request-data request-extr= a-headers) + cbargs + request-method request-data request-extra-head= ers) "Retrieve an URL asynchronously using TOKEN to access it. TOKEN can be obtained with `oauth2-auth'. CALLBACK gets called with CBARGS when finished. See `url-retrieve'." ;; TODO add support for SILENT and INHIBIT-COOKIES. How to handle this = in `url-http-handle-authentication'. (let* ((oauth--token-data (cons token url))) - (let ((oauth--url-advice t) ;Activate our advice. + (let ((oauth2--url-advice t) ;Activate our advice. (url-request-method request-method) (url-request-data request-data) (url-request-extra-headers --=20 2.34.0 --=-=-= Content-Type: text/x-patch Content-Disposition: attachment; filename=0002-oauth2-oauth2-token-Fix-formatting.patch >From 46f26e03c8b4f42c407077263c0f1e9371e07d4d Mon Sep 17 00:00:00 2001 Message-Id: <46f26e03c8b4f42c407077263c0f1e9371e07d4d.1638101267.git.avityazev@posteo.org> In-Reply-To: References: From: Aleksandr Vityazev Date: Thu, 25 Nov 2021 11:07:13 +0300 Subject: [PATCH 02/11] * oauth2: (oauth2-token): Fix formatting. --- oauth2.el | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/oauth2.el b/oauth2.el index 1444890..fff89af 100644 --- a/oauth2.el +++ b/oauth2.el @@ -101,13 +101,13 @@ Return the code provided by the service." data)))) (cl-defstruct oauth2-token - (plstore "" :read-only t) - (plstore-id "" :read-only t) - (client-id "" :read-only t) - (client-secret "" :read-only t) - (access-token "" :read-only t) - (refresh-token "" :read-only t) - (token-url "" :read-only t) + (plstore "" :read-only t) + (plstore-id "" :read-only t) + (client-id "" :read-only t) + (client-secret "" :read-only t) + (access-token "" :read-only t) + (refresh-token "" :read-only t) + (token-url "" :read-only t) (access-response "" :read-only t)) (cl-defun oauth2-request-access (token-url client-id client-secret code &optional -- 2.34.0 --=-=-= Content-Type: text/x-patch Content-Disposition: attachment; filename=0004-oauth2-Remove-oauth2-url-http-handle-authentication-.patch >From fb0ded3ba9e8b1d3c2cb10942119874d1e8d0251 Mon Sep 17 00:00:00 2001 Message-Id: In-Reply-To: References: From: Aleksandr Vityazev Date: Thu, 25 Nov 2021 15:33:57 +0300 Subject: [PATCH 04/11] * oauth2: Remove oauth2--url-http-handle-authentication-hack (oauth2-auth-and-store): Check the token expiration date. (oauth2-token): Add new slots. --- oauth2.el | 91 +++++++++++++++++++++++++------------------------------ 1 file changed, 41 insertions(+), 50 deletions(-) diff --git a/oauth2.el b/oauth2.el index fff89af..31da145 100644 --- a/oauth2.el +++ b/oauth2.el @@ -108,6 +108,8 @@ Return the code provided by the service." (access-token "" :read-only t) (refresh-token "" :read-only t) (token-url "" :read-only t) + (expires-in "" :read-only t) + (created-at "" :read-only t) (access-response "" :read-only t)) (cl-defun oauth2-request-access (token-url client-id client-secret code &optional @@ -155,6 +157,8 @@ TOKEN should be obtained with `oauth2-request-access'." :client-secret client-secret :access-token (cdr (assoc 'access_token response)) :refresh-token (cdr (assoc 'refresh_token response)) + :expires-in (cdr (assoc 'expires_in response)) + :created-at (cdr (assoc 'created_at response)) :token-url token-url :access-response response))) (when plstore @@ -169,7 +173,8 @@ TOKEN should be obtained with `oauth2-request-access'." new-token))) ;;;###autoload -(defun oauth2-auth (auth-url token-url client-id client-secret &optional scope state redirect-uri) +(defun oauth2-auth + (auth-url token-url client-id client-secret &optional scope state redirect-uri) "Authenticate application via OAuth2 at AUTH-URL and TOKEN-URL. CLIENT-ID is the client id provided by the provider. CLIENT-SECRET is the client secret associated with your CLIENT-ID. @@ -195,6 +200,8 @@ Return an `oauth2-token' structure." :client-secret client-secret :access-token (cdr (assoc 'access_token response)) :refresh-token (cdr (assoc 'refresh_token response)) + :expires-in (cdr (assoc 'expires_in response)) + :created-at (cdr (assoc 'created_at response)) :token-url token-url :access-response response))) @@ -204,7 +211,8 @@ This allows to store the token in an unique way." (secure-hash 'md5 (concat auth-url token-url scope))) ;;;###autoload -(defun oauth2-auth-and-store (auth-url token-url client-id client-secret scope &optional state redirect-uri) +(defun oauth2-auth-and-store + (auth-url token-url client-id client-secret scope &optional state redirect-uri) "Request access to a resource and store it using `plstore'. If the token has not yet been saved in plsote, then authenticate the application via OAuth2 using the AUTH-URL, TOKEN-URL and after that @@ -219,17 +227,25 @@ uses to maintain state between the request and redirect response. Return an `oauth2-token' structure." (if-let ((plstore (plstore-open oauth2-token-file)) (id (oauth2-compute-id auth-url token-url scope)) - (plist (cdr (plstore-get plstore id)))) + (plist (cdr (plstore-get plstore id))) + (expires (cdr (assoc 'expires_in (plist-get plist :access-response)))) + (created (cdr (assoc 'created_at (plist-get plist :access-response)))) + (current-time (time-convert nil 'integer)) + (token (make-oauth2-token :plstore plstore + :plstore-id id + :client-id client-id + :client-secret client-secret + :access-token (plist-get plist :access-token) + :refresh-token (plist-get plist :refresh-token) + :expires-in expires + :created-at created + :token-url token-url + :access-response (plist-get plist :access-response)))) ;; Check if we found something matching this access ;; We did, return the token object - (make-oauth2-token :plstore plstore - :plstore-id id - :client-id client-id - :client-secret client-secret - :access-token (plist-get plist :access-token) - :refresh-token (plist-get plist :refresh-token) - :token-url token-url - :access-response (plist-get plist :access-response)) + (if (< current-time (+ expires created)) + token + (oauth2-refresh-access token)) (let ((token (oauth2-auth auth-url token-url client-id client-secret scope state redirect-uri))) (plstore-put plstore id nil `(:access-token @@ -247,48 +263,25 @@ Return an `oauth2-token' structure." (if (string-match-p "\?" url) "&" "?") "access_token=" (oauth2-token-access-token token))) -(defvar oauth2--url-advice nil) -(defvar oauth--token-data) - (defun oauth2-authz-bearer-header (token) "Return `Authoriztions: Bearer' header with TOKEN." (cons "Authorization" (format "Bearer %s" token))) -(defun oauth2-extra-headers (extra-headers) +(defun oauth2-extra-headers (token extra-headers) "Return EXTRA-HEADERS with `Authorization: Bearer' added." - (cons (oauth2-authz-bearer-header (oauth2-token-access-token (car oauth--token-data))) + (cons (oauth2-authz-bearer-header (oauth2-token-access-token token)) extra-headers)) - -;; FIXME: We should change URL so that this can be done without an advice. -(defun oauth2--url-http-handle-authentication-hack (orig-fun &rest args) - (if (not oauth2--url-advice) - (apply orig-fun args) - (let ((url-request-method url-http-method) - (url-request-data url-http-data) - (url-request-extra-headers - (oauth2-extra-headers url-http-extra-headers))) - (oauth2-refresh-access (car oauth--token-data)) - (url-retrieve-internal (cdr oauth--token-data) - url-callback-function - url-callback-arguments) - ;; This is to make `url' think it's done. - t))) - -(advice-add 'url-http-handle-authentication :around - #'oauth2--url-http-handle-authentication-hack) - ;;;###autoload -(defun oauth2-url-retrieve-synchronously (token url &optional request-method request-data request-extra-headers) +(defun oauth2-url-retrieve-synchronously + (token url &optional request-method request-data request-extra-headers) "Retrieve an URL synchronously using TOKEN to access it. TOKEN can be obtained with `oauth2-auth'." - (let* ((oauth--token-data (cons token url))) - (let ((oauth2--url-advice t) ;Activate our advice. - (url-request-method request-method) - (url-request-data request-data) - (url-request-extra-headers - (oauth2-extra-headers request-extra-headers))) - (url-retrieve-synchronously url)))) + (let ((url-request-method request-method) + (url-request-data request-data) + (url-request-extra-headers + (oauth2-extra-headers token request-extra-headers))) + (url-retrieve-synchronously url))) ;;;###autoload (defun oauth2-url-retrieve (token url callback &optional @@ -298,13 +291,11 @@ TOKEN can be obtained with `oauth2-auth'." TOKEN can be obtained with `oauth2-auth'. CALLBACK gets called with CBARGS when finished. See `url-retrieve'." ;; TODO add support for SILENT and INHIBIT-COOKIES. How to handle this in `url-http-handle-authentication'. - (let* ((oauth--token-data (cons token url))) - (let ((oauth2--url-advice t) ;Activate our advice. - (url-request-method request-method) - (url-request-data request-data) - (url-request-extra-headers - (oauth2-extra-headers request-extra-headers))) - (url-retrieve url callback cbargs)))) + (let ((url-request-method request-method) + (url-request-data request-data) + (url-request-extra-headers + (oauth2-extra-headers token request-extra-headers))) + (url-retrieve url callback cbargs))) (provide 'oauth2) -- 2.34.0 --=-=-= Content-Type: text/x-patch Content-Disposition: attachment; filename=0005-oauth2-oauth2-extra-headers-Update-doc-string.patch >From 2224924ea465450c0a9156d101a8552df48e6178 Mon Sep 17 00:00:00 2001 Message-Id: <2224924ea465450c0a9156d101a8552df48e6178.1638101267.git.avityazev@posteo.org> In-Reply-To: References: From: Aleksandr Vityazev Date: Fri, 26 Nov 2021 16:09:02 +0300 Subject: [PATCH 05/11] * oauth2 (oauth2-extra-headers): Update doc string. --- oauth2.el | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/oauth2.el b/oauth2.el index 31da145..f77ae6a 100644 --- a/oauth2.el +++ b/oauth2.el @@ -268,7 +268,7 @@ Return an `oauth2-token' structure." (cons "Authorization" (format "Bearer %s" token))) (defun oauth2-extra-headers (token extra-headers) - "Return EXTRA-HEADERS with `Authorization: Bearer' added." + "Return EXTRA-HEADERS with `Authorization: Bearer' header with TOKEN added." (cons (oauth2-authz-bearer-header (oauth2-token-access-token token)) extra-headers)) -- 2.34.0 --=-=-= Content-Type: text/x-patch Content-Disposition: attachment; filename=0006-oauth2-Use-rx-with-string-match-p.patch >From a0affb78c3536c02189f291549553bcb3ae7d76c Mon Sep 17 00:00:00 2001 Message-Id: In-Reply-To: References: From: Aleksandr Vityazev Date: Fri, 26 Nov 2021 16:14:43 +0300 Subject: [PATCH 06/11] * oauth2: Use rx with string-match-p. --- oauth2.el | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/oauth2.el b/oauth2.el index f77ae6a..3dab614 100644 --- a/oauth2.el +++ b/oauth2.el @@ -75,7 +75,7 @@ uses to maintain state between the request and redirect response. Return the code provided by the service." (browse-url (concat auth-url - (if (string-match-p "\?" auth-url) "&" "?") + (if (string-match-p (rx "?") auth-url) "&" "?") "client_id=" (url-hexify-string client-id) "&response_type=code" "&redirect_uri=" (url-hexify-string redirect-uri) @@ -260,7 +260,7 @@ Return an `oauth2-token' structure." (defun oauth2-url-append-access-token (token url) "Append access TOKEN to URL." (concat url - (if (string-match-p "\?" url) "&" "?") + (if (string-match-p (rx "?") url) "&" "?") "access_token=" (oauth2-token-access-token token))) (defun oauth2-authz-bearer-header (token) -- 2.34.0 --=-=-= Content-Type: text/x-patch Content-Disposition: attachment; filename=0007-oauth2-oauth2-epoch-time-New-function.patch >From 1272151cb165831170834a2c34cc646c09a0b4c6 Mon Sep 17 00:00:00 2001 Message-Id: <1272151cb165831170834a2c34cc646c09a0b4c6.1638101267.git.avityazev@posteo.org> In-Reply-To: References: From: Aleksandr Vityazev Date: Sat, 27 Nov 2021 15:42:47 +0300 Subject: [PATCH 07/11] * oauth2 (oauth2--epoch-time): New function. --- oauth2.el | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/oauth2.el b/oauth2.el index 3dab614..4bb886e 100644 --- a/oauth2.el +++ b/oauth2.el @@ -112,6 +112,10 @@ Return the code provided by the service." (created-at "" :read-only t) (access-response "" :read-only t)) +(defun oauth2--epoch-time () + "Return seconds since the Epoch." + (time-convert (current-time) 'integer)) + (cl-defun oauth2-request-access (token-url client-id client-secret code &optional (redirect-uri "urn:ietf:wg:oauth:2.0:oob")) "Request OAuth access at TOKEN-URL. @@ -158,7 +162,8 @@ TOKEN should be obtained with `oauth2-request-access'." :access-token (cdr (assoc 'access_token response)) :refresh-token (cdr (assoc 'refresh_token response)) :expires-in (cdr (assoc 'expires_in response)) - :created-at (cdr (assoc 'created_at response)) + :created-at (let ((it (cdr (assoc 'created_at response)))) + (if it it (oauth2--epoch-time))) :token-url token-url :access-response response))) (when plstore @@ -201,7 +206,8 @@ Return an `oauth2-token' structure." :access-token (cdr (assoc 'access_token response)) :refresh-token (cdr (assoc 'refresh_token response)) :expires-in (cdr (assoc 'expires_in response)) - :created-at (cdr (assoc 'created_at response)) + :created-at (let ((it (cdr (assoc 'created_at response)))) + (if it it (oauth2--epoch-time))) :token-url token-url :access-response response))) @@ -230,7 +236,7 @@ Return an `oauth2-token' structure." (plist (cdr (plstore-get plstore id))) (expires (cdr (assoc 'expires_in (plist-get plist :access-response)))) (created (cdr (assoc 'created_at (plist-get plist :access-response)))) - (current-time (time-convert nil 'integer)) + (current-time (oauth2--epoch-time)) (token (make-oauth2-token :plstore plstore :plstore-id id :client-id client-id -- 2.34.0 --=-=-= Content-Type: text/x-patch Content-Disposition: attachment; filename=0008-oauth2-oauth2-created-at-New-function.patch >From 111c0d7c6b928c632d12870de0b531bb6b32e06f Mon Sep 17 00:00:00 2001 Message-Id: <111c0d7c6b928c632d12870de0b531bb6b32e06f.1638101267.git.avityazev@posteo.org> In-Reply-To: References: From: Aleksandr Vityazev Date: Sun, 28 Nov 2021 02:19:08 +0300 Subject: [PATCH 08/11] * oauth2 (oauth2--created-at): New function. --- oauth2.el | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/oauth2.el b/oauth2.el index 4bb886e..7ca6452 100644 --- a/oauth2.el +++ b/oauth2.el @@ -116,6 +116,14 @@ Return the code provided by the service." "Return seconds since the Epoch." (time-convert (current-time) 'integer)) +(defun oauth2--created-at (response) + "Return epoch time from RESPONSE. +If there no created_at key in RESPONSE, add +\(cons 'created_at . epoch-time\) to RESPONSE." + (let ((it (cdr (assoc 'created_at response)))) + (if it it (prog1 (oauth2--epoch-time) + (cl-pushnew (cons 'created_at (oauth2--epoch-time)) response))))) + (cl-defun oauth2-request-access (token-url client-id client-secret code &optional (redirect-uri "urn:ietf:wg:oauth:2.0:oob")) "Request OAuth access at TOKEN-URL. @@ -162,8 +170,7 @@ TOKEN should be obtained with `oauth2-request-access'." :access-token (cdr (assoc 'access_token response)) :refresh-token (cdr (assoc 'refresh_token response)) :expires-in (cdr (assoc 'expires_in response)) - :created-at (let ((it (cdr (assoc 'created_at response)))) - (if it it (oauth2--epoch-time))) + :created-at (oauth2--created-at response) :token-url token-url :access-response response))) (when plstore @@ -206,8 +213,7 @@ Return an `oauth2-token' structure." :access-token (cdr (assoc 'access_token response)) :refresh-token (cdr (assoc 'refresh_token response)) :expires-in (cdr (assoc 'expires_in response)) - :created-at (let ((it (cdr (assoc 'created_at response)))) - (if it it (oauth2--epoch-time))) + :created-at (oauth2--created-at response) :token-url token-url :access-response response))) -- 2.34.0 --=-=-= Content-Type: text/x-patch Content-Disposition: attachment; filename=0009-oauth2-oauth2-auth-and-store-Remove-comment.patch >From 4b71a470af257faf31d27987b6d4bffb6ce67e00 Mon Sep 17 00:00:00 2001 Message-Id: <4b71a470af257faf31d27987b6d4bffb6ce67e00.1638101267.git.avityazev@posteo.org> In-Reply-To: References: From: Aleksandr Vityazev Date: Sun, 28 Nov 2021 14:38:35 +0300 Subject: [PATCH 09/11] * oauth2 (oauth2-auth-and-store): Remove comment. --- oauth2.el | 2 -- 1 file changed, 2 deletions(-) diff --git a/oauth2.el b/oauth2.el index 7ca6452..9906e8a 100644 --- a/oauth2.el +++ b/oauth2.el @@ -253,8 +253,6 @@ Return an `oauth2-token' structure." :created-at created :token-url token-url :access-response (plist-get plist :access-response)))) - ;; Check if we found something matching this access - ;; We did, return the token object (if (< current-time (+ expires created)) token (oauth2-refresh-access token)) -- 2.34.0 --=-=-= Content-Type: text/x-patch Content-Disposition: attachment; filename=0010-oauth2-oauth2-created-at-Use-if-let.patch >From 09e8df6d652d87d04b75d0bbea8f5e7494df991c Mon Sep 17 00:00:00 2001 Message-Id: <09e8df6d652d87d04b75d0bbea8f5e7494df991c.1638101267.git.avityazev@posteo.org> In-Reply-To: References: From: Aleksandr Vityazev Date: Sun, 28 Nov 2021 14:46:18 +0300 Subject: [PATCH 10/11] * oauth2 (oauth2--created-at): Use if-let. --- oauth2.el | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/oauth2.el b/oauth2.el index 9906e8a..49a12bc 100644 --- a/oauth2.el +++ b/oauth2.el @@ -120,9 +120,10 @@ Return the code provided by the service." "Return epoch time from RESPONSE. If there no created_at key in RESPONSE, add \(cons 'created_at . epoch-time\) to RESPONSE." - (let ((it (cdr (assoc 'created_at response)))) - (if it it (prog1 (oauth2--epoch-time) - (cl-pushnew (cons 'created_at (oauth2--epoch-time)) response))))) + (if-let ((it (cdr (assoc 'created_at response)))) + it + (prog1 (oauth2--epoch-time) + (cl-pushnew (cons 'created_at (oauth2--epoch-time)) response)))) (cl-defun oauth2-request-access (token-url client-id client-secret code &optional (redirect-uri "urn:ietf:wg:oauth:2.0:oob")) -- 2.34.0 --=-=-= Content-Type: text/x-patch Content-Disposition: attachment; filename=0011-oauth2-oauth2-authz-bearer-header-Change-arg-name.patch >From 9c6c00c0abde2a17f0833e074f20114636a3341f Mon Sep 17 00:00:00 2001 Message-Id: <9c6c00c0abde2a17f0833e074f20114636a3341f.1638101267.git.avityazev@posteo.org> In-Reply-To: References: From: Aleksandr Vityazev Date: Sun, 28 Nov 2021 15:07:20 +0300 Subject: [PATCH 11/11] * oauth2 (oauth2-authz-bearer-header): Change arg name. --- oauth2.el | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/oauth2.el b/oauth2.el index 49a12bc..b8227a4 100644 --- a/oauth2.el +++ b/oauth2.el @@ -274,9 +274,9 @@ Return an `oauth2-token' structure." (if (string-match-p (rx "?") url) "&" "?") "access_token=" (oauth2-token-access-token token))) -(defun oauth2-authz-bearer-header (token) - "Return `Authoriztions: Bearer' header with TOKEN." - (cons "Authorization" (format "Bearer %s" token))) +(defun oauth2-authz-bearer-header (access-token) + "Return `Authoriztions: Bearer' header with ACCESS-TOKEN." + (cons "Authorization" (format "Bearer %s" access-token))) (defun oauth2-extra-headers (token extra-headers) "Return EXTRA-HEADERS with `Authorization: Bearer' header with TOKEN added." -- 2.34.0 --=-=-=--