Subject: bug#52476: closed (Re: bug#52476: Fwd: [ELPA/oauth2] Request for patches review)

From: [email-masked] (GNU bug Tracking System)
Date: Sat, 09 Aug 2025 09:30:02 +0000

Your bug report

#52476: Fwd: [ELPA/oauth2] Request for patches review

which was filed against the emacs package, has been closed.

The explanation is attached below, along with your original report.
If you require more details, please reply to [email-masked].

-- 
52476: https://debbugs.gnu.org/cgi/bugreport.cgi?bug=52476
GNU Bug Tracking System
Contact [email-masked] with problems

Subject: Re: bug#52476: Fwd: [ELPA/oauth2] Request for patches review
From: Sean Whitton <[email-masked]>
To: [email-masked], [email-masked], Xiyue Deng <[email-masked]>, Aleksandr Vityazev <[email-masked]>
Date: Sat, 09 Aug 2025 10:29:31 +0100
List-Id: <debbugs-submit.debbugs.gnu.org>

Hello,

On Sun 23 Feb 2025 at 01:39am GMT, Stefan Kangas wrote:

> Stefan Kangas <[email-masked]> writes:
>
>> I have seen no followups to this within two weeks, so I'm forwarding
>> this to the bug tracker so that we don't lose track of it.
>>
>> -------------------- Start of forwarded message --------------------
>> From: Aleksandr Vityazev <[email-masked]>
>> To: [email-masked], [email-masked]
>> Subject: [ELPA/oauth2] Request for patches review
>> Date: Sun, 28 Nov 2021 12:38:58 +0000
>
> Xiyue, since you have looked into oauth2 recently, perhaps you would be
> willing to take a look at this?  I'm not sure how relevant any of this
> is with your recent changes.
>
> You can find the patches here:
> https://debbugs.gnu.org/cgi/bugreport.cgi?bug=52476

Xiyue prepared a series based on these changes.  I'm going to go ahead
and close the bug on the assumption that everything most important to
incorporate has been incorporated, now.  Aleksandr, if you think
something is missing, please feel free to let us know and we can reopen.

-- 
Sean Whitton

Subject: Fwd: [ELPA/oauth2] Request for patches review
From: Stefan Kangas <[email-masked]>
To: [email-masked]
Date: Mon, 13 Dec 2021 18:39:21 -0800
List-Id: <debbugs-submit.debbugs.gnu.org>

I have seen no followups to this within two weeks, so I'm forwarding
this to the bug tracker so that we don't lose track of it.

-------------------- Start of forwarded message --------------------
From: Aleksandr Vityazev <[email-masked]>
To: [email-masked], [email-masked]
Subject: [ELPA/oauth2] Request for patches review
Date: Sun, 28 Nov 2021 12:38:58 +0000
From e52ba22450bec9b77afdd943a03d79b2f89870da Mon Sep 17 00:00:00 2001
Message-Id: <e[email-masked]>
In-Reply-To: <[email-masked]>
References: <[email-masked]>
From: Aleksandr Vityazev <[email-masked]>
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 doc 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 -*-
 
+;; Copyright © 2021 Aleksandr Vityazev <[email-masked]>
 ;; Copyright (C) 2011-2021 Free Software Foundation, Inc
 
 ;; Author: Julien Danjou <[email-masked]>
-;; 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
 
 ;; This file is part of GNU Emacs.
 
@@ -36,11 +38,19 @@
 
 ;;; Code:
 
-(eval-when-compile (require 'cl-lib))
+(eval-when-compile
+  (require 'cl-lib)
+  (require 'subr-x))
+
 (require 'plstore)
 (require 'json)
 (require 'url-http)
 
+(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=externals/oauth2")
   :link '(url-link :tag "ELPA" "https://elpa.gnu.org/packages/oauth2.html"))
 
-(defun oauth2-request-authorization (auth-url client-id &optional scope state redirect-uri)
+(cl-defun oauth2-request-authorization (auth-url client-id &optional scope state
+                                           (redirect-uri "urn:ietf:wg:oauth: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=" (url-hexify-string client-id)
                       "&response_type=code"
-                      "&redirect_uri=" (url-hexify-string (or redirect-uri "urn:ietf:wg:oauth:2.0:oob"))
+                      "&redirect_uri=" (url-hexify-string redirect-uri)
                       (if scope (concat "&scope=" (url-hexify-string scope)) "")
                       (if state (concat "&state=" (url-hexify-string state)) "")))
   (read-string "Enter the code your browser displayed: "))
@@ -84,21 +101,27 @@ It returns the code provided by the service."
         data))))
 
 (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 &optional 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 &optional
+                                     (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=" client-secret))
              "&code=" code
-             "&redirect_uri=" (url-hexify-string (or redirect-uri "urn:ietf:wg:oauth:2.0:oob"))
+             "&redirect_uri=" (url-hexify-string redirect-uri)
              "&grant_type=authorization_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)))
 
 ;;;###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=" (oauth2-token-client-id token)
-			     (when (oauth2-token-client-secret token)
-                               (concat "&client_secret=" (oauth2-token-client-secret token)))
-                             "&refresh_token=" (oauth2-token-refresh-token token)
-                             "&grant_type=refresh_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=" client-id
+			      (when client-secret
+                                (concat "&client_secret=" client-secret))
+                              "&refresh_token=" (oauth2-token-refresh-token token)
+                              "&grant_type=refresh_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 'refresh_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)))
 
 ;;;###autoload
 (defun oauth2-auth (auth-url token-url client-id client-secret &optional scope 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.
 
-(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)))
 
 (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)))
 
 ;;;###autoload
-(defun oauth2-auth-and-store (auth-url token-url scope client-id client-secret &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-response))
-      (let ((token (oauth2-auth auth-url token-url
-                                client-id client-secret scope state redirect-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 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
+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-response))
+    (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)))
 
 (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=" (oauth2-token-access-token token)))
 
-(defvar oauth--url-advice nil)
+(defvar oauth2--url-advice nil)
 (defvar oauth--token-data)
 
 (defun oauth2-authz-bearer-header (token)
@@ -216,7 +262,7 @@ This allows to store the token in an unique way."
 
 ;; 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≥24.4.
+      t)))
+
 (advice-add 'url-http-handle-authentication :around
             #'oauth2--url-http-handle-authentication-hack)
 
@@ -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'."
 
 ;;;###autoload
 (defun oauth2-url-retrieve (token url callback &optional
-                                  cbargs
-                                  request-method request-data request-extra-headers)
+                            cbargs
+                            request-method request-data request-extra-headers)
   "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
-- 
2.34.0

From 46f26e03c8b4f42c407077263c0f1e9371e07d4d Mon Sep 17 00:00:00 2001
Message-Id: <4[email-masked]>
In-Reply-To: <[email-masked]>
References: <[email-masked]>
From: Aleksandr Vityazev <[email-masked]>
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

From fb0ded3ba9e8b1d3c2cb10942119874d1e8d0251 Mon Sep 17 00:00:00 2001
Message-Id: <f[email-masked]>
In-Reply-To: <[email-masked]>
References: <[email-masked]>
From: Aleksandr Vityazev <[email-masked]>
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

From 2224924ea465450c0a9156d101a8552df48e6178 Mon Sep 17 00:00:00 2001
Message-Id: <2[email-masked]>
In-Reply-To: <[email-masked]>
References: <[email-masked]>
From: Aleksandr Vityazev <[email-masked]>
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

From a0affb78c3536c02189f291549553bcb3ae7d76c Mon Sep 17 00:00:00 2001
Message-Id: <a[email-masked]>
In-Reply-To: <[email-masked]>
References: <[email-masked]>
From: Aleksandr Vityazev <[email-masked]>
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

From 1272151cb165831170834a2c34cc646c09a0b4c6 Mon Sep 17 00:00:00 2001
Message-Id: <1[email-masked]>
In-Reply-To: <[email-masked]>
References: <[email-masked]>
From: Aleksandr Vityazev <[email-masked]>
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

From 111c0d7c6b928c632d12870de0b531bb6b32e06f Mon Sep 17 00:00:00 2001
Message-Id: <1[email-masked]>
In-Reply-To: <[email-masked]>
References: <[email-masked]>
From: Aleksandr Vityazev <[email-masked]>
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

From 4b71a470af257faf31d27987b6d4bffb6ce67e00 Mon Sep 17 00:00:00 2001
Message-Id: <4[email-masked]>
In-Reply-To: <[email-masked]>
References: <[email-masked]>
From: Aleksandr Vityazev <[email-masked]>
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

From 09e8df6d652d87d04b75d0bbea8f5e7494df991c Mon Sep 17 00:00:00 2001
Message-Id: <0[email-masked]>
In-Reply-To: <[email-masked]>
References: <[email-masked]>
From: Aleksandr Vityazev <[email-masked]>
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

From 9c6c00c0abde2a17f0833e074f20114636a3341f Mon Sep 17 00:00:00 2001
Message-Id: <9[email-masked]>
In-Reply-To: <[email-masked]>
References: <[email-masked]>
From: Aleksandr Vityazev <[email-masked]>
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