emacs-elpa-diffs
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[elpa] externals/oauth2 9e3794c 01/23: New package oauth2


From: Stefan Monnier
Subject: [elpa] externals/oauth2 9e3794c 01/23: New package oauth2
Date: Tue, 1 Dec 2020 16:31:57 -0500 (EST)

branch: externals/oauth2
commit 9e3794cc9b24a27122191f0689886e514ea81c74
Author: Julien Danjou <julien@danjou.info>
Commit: Julien Danjou <julien@danjou.info>

    New package oauth2
---
 oauth2.el | 188 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 188 insertions(+)

diff --git a/oauth2.el b/oauth2.el
new file mode 100644
index 0000000..2aea064
--- /dev/null
+++ b/oauth2.el
@@ -0,0 +1,188 @@
+;;; oauth2.el --- OAuth 2.0 Authorization Protocol
+
+;; Copyright (C) 2011 Free Software Foundation, Inc
+
+;; Author: Julien Danjou <julien@danjou.info>
+;; Version: 0.1
+;; Keywords: comm
+
+;; This file is part of GNU Emacs.
+
+;; GNU Emacs is free software: you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
+
+;; GNU Emacs is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.
+
+;;; Commentary:
+
+;; Implementation of the OAuth 2.0 draft.
+;;
+;; The main entry point is `oauth2-auth-and-store' which will return a token
+;; structure. This token structure can be then used with
+;; `oauth2-url-retrieve-synchronously' to retrieve any data that need OAuth
+;; authentication to be accessed.
+;;
+;; If the token needs to be refreshed, the code handles it automatically and
+;; store the new value of the access token.
+
+;;; Code:
+
+(require 'plstore)
+
+(defun oauth2-request-authorization (auth-url client-id &optional scope state)
+  "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."
+  (browse-url (concat auth-url
+                      (if (string-match-p "\?" auth-url) "&" "?")
+                      "client_id=" client-id
+                      
"&response_type=code&redirect_uri=urn:ietf:wg:oauth:2.0:oob"
+                      (if scope (concat "&scope=" (url-hexify-string scope)) 
"")
+                      (if state (concat "&state=" state) "")))
+  (read-string "Enter the code your browser displayed: "))
+
+(defun oauth2-request-access-parse ()
+  "Parse the result of an OAuth request."
+  (goto-char (point-min))
+  (when (search-forward-regexp "^$" nil t)
+    (json-read)))
+
+(defun oauth2-make-access-request (url data)
+  "Make an access request to URL using DATA in POST."
+  (let ((url-request-method "POST")
+        (url-request-data data)
+        (url-request-extra-headers '(("Content-Type" . 
"application/x-www-form-urlencoded"))))
+    (with-current-buffer (url-retrieve-synchronously url)
+      (let ((data (oauth2-request-access-parse)))
+        (kill-buffer (current-buffer))
+        data))))
+
+(defstruct oauth2-token
+  plstore
+  plstore-id
+  client-id
+  client-secret
+  access-token
+  refresh-token
+  token-url)
+
+(defun oauth2-request-access (token-url client-id client-secret code)
+  "Request OAuth access at TOKEN-URL.
+The CODE should be obtained with `oauth2-request-authorization'.
+Return an `oauth2-token' structure."
+  (when code
+    (let ((result
+           (oauth2-make-access-request token-url
+                                       (concat "client_id=" client-id
+                                               "&client_secret=" client-secret
+                                               "&code=" code
+                                               
"&redirect_uri=urn:ietf:wg:oauth:2.0:oob&grant_type=authorization_code"))))
+      (make-oauth2-token :client-id client-id
+                         :client-secret client-secret
+                         :access-token (aget result 'access_token)
+                         :refresh-token (aget result 'refresh_token)
+                         :token-url token-url))))
+
+;;;###autoload
+(defun oauth2-refresh-access (token)
+  "Refresh OAuth access TOKEN.
+TOKEN should be obtained with `oauth2-request-access'."
+  (setf (oauth2-token-access-token token)
+        (aget (oauth2-make-access-request (oauth2-token-token-url token)
+                                          (concat "client_id=" 
(oauth2-token-client-id token)
+                                                  "&client_secret=" 
(oauth2-token-client-secret token)
+                                                  "&refresh_token=" 
(oauth2-token-refresh-token token)
+                                                  "&grant_type=refresh_token"))
+              'access_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)))
+      (plstore-save plstore)))
+  token)
+
+;;;###autoload
+(defun oauth2-auth (auth-url token-url client-id client-secret &optional scope 
state)
+  "Authenticate application via OAuth2."
+  (oauth2-request-access
+   token-url
+   client-id
+   client-secret
+   (oauth2-request-authorization
+    auth-url client-id scope state)))
+
+(defcustom oauth2-token-file (concat user-emacs-directory "oauth2.plstore")
+  "File path where store OAuth tokens."
+  :group 'oauth2
+  :type 'file)
+
+(defun oauth2-compute-id (auth-url token-url resource-url)
+  "Compute an unique id based on URLs.
+This allows to store the token in an unique way."
+  (secure-hash 'md5 (concat auth-url token-url resource-url)))
+
+;;;###autoload
+(defun oauth2-auth-and-store (auth-url token-url resource-url client-id 
client-secret)
+  "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 resource-url))
+         (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)
+      (let ((token (oauth2-auth auth-url token-url
+                                client-id client-secret resource-url)))
+        ;; 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)))
+        (plstore-save plstore)
+        token))))
+
+(defun oauth2-url-append-access-token (token url)
+  "Append access token to URL."
+  (concat url
+          (if (string-match-p "\?" url) "&" "?")
+          "access_token=" (oauth2-token-access-token token)))
+
+;;;###autoload
+(defun oauth2-url-retrieve-synchronously (token url)
+  "Retrieve an URL synchronously using TOKENS to access it.
+TOKENS can be obtained with `oauth2-auth'."
+  (let (tokens-need-renew)
+    (flet ((url-http-handle-authentication (proxy)
+                                           (setq tokens-need-renew t)
+                                           ;; This is to make `url' think it's 
done.
+                                           (setq success t)))
+      (let ((url-buffer (url-retrieve-synchronously
+                         (oauth2-url-append-access-token token url))))
+        (if tokens-need-renew
+            (oauth2-url-retrieve-synchronously (oauth2-refresh-access token) 
url)
+          url-buffer)))))
+
+(provide 'oauth2)
+
+;;; oauth2.el ends here



reply via email to

[Prev in Thread] Current Thread [Next in Thread]