aboutsummaryrefslogtreecommitdiff
path: root/lisp/mastodon-auth.el
diff options
context:
space:
mode:
authormartianh <martianh@noreply.codeberg.org>2022-03-23 08:04:07 +0100
committermartianh <martianh@noreply.codeberg.org>2022-03-23 08:04:07 +0100
commitcecd5de060a56f13f7f7eb4528b341027812faab (patch)
treeb612438cd9e5b46c0300c2ae1dc1b007698226bb /lisp/mastodon-auth.el
parentc7b475160d2e7712e339e15adf168529f71b52c6 (diff)
parent56fa25df379623e79261b535cd724db3ed979d44 (diff)
Merge pull request '2FA login support' (#255) from Red_Starfish/mastodon-up.el:login into develop
Reviewed-on: https://codeberg.org/martianh/mastodon.el/pulls/255
Diffstat (limited to 'lisp/mastodon-auth.el')
-rw-r--r--lisp/mastodon-auth.el201
1 files changed, 130 insertions, 71 deletions
diff --git a/lisp/mastodon-auth.el b/lisp/mastodon-auth.el
index e582e4f..582f7bb 100644
--- a/lisp/mastodon-auth.el
+++ b/lisp/mastodon-auth.el
@@ -1,6 +1,7 @@
;;; mastodon-auth.el --- Auth functions for mastodon.el -*- lexical-binding: t -*-
;; Copyright (C) 2017-2019 Johnson Denen
+;; Copyright (C) 2021 Abhiseck Paira <abhiseckpaira@disroot.org>
;; Author: Johnson Denen <johnson.denen@gmail.com>
;; Maintainer: Marty Hiatt <martianhiatus@riseup.net>
;; Version: 0.10.0
@@ -39,21 +40,25 @@
(autoload 'mastodon-http--api "mastodon-http")
(autoload 'mastodon-http--get-json "mastodon-http")
(autoload 'mastodon-http--post "mastodon-http")
+(autoload 'mastodon-http--append-query-string "mastodon-http")
+(autoload 'mastodon-client--store-access-token "mastodon-client")
+(autoload 'mastodon-client--active-user "mastodon-client")
+(autoload 'mastodon-client--make-user-active "mastodon-client")
+(autoload 'mastodon-client--form-user-from-vars "mastodon-client")
(defvar mastodon-instance-url)
+(defvar mastodon-client-scopes)
+(defvar mastodon-client-redirect-uri)
+(defvar mastodon-active-user)
(defgroup mastodon-auth nil
"Authenticate with Mastodon."
:prefix "mastodon-auth-"
:group 'mastodon)
-(defcustom mastodon-auth-source-file ""
- "Filename to use to store user names and passwords.
-
-Leave empty to not permanently store any secrets.
-Otherwise set to e.g. \"~/.authinfo.gpg\" to have encrypted storage, or
-if you are happy with unencryped storage use e.g. \"~/authinfo\"."
- :group 'mastodon-auth
- :type 'string)
+(defvar mastodon-auth-source-file nil
+ "This variable is obsolete.
+This variable currently serves no purpose and will be removed in
+the future.")
(defvar mastodon-auth--token-alist nil
"Alist of User access tokens keyed by instance url.")
@@ -61,60 +66,95 @@ if you are happy with unencryped storage use e.g. \"~/authinfo\"."
(defvar mastodon-auth--acct-alist nil
"Alist of account accts (name@domain) keyed by instance url.")
+(defvar mastodon-auth--user-unaware
+ " ** MASTODON.EL - NOTICE **
+
+It appears that you are not aware of the recent developments in
+mastodon.el. In short we now require that you also set the
+variable `mastodon-active-user' in your init file in addition to
+`mastodon-instance-url'.
+
+Please see its documentation to understand what value it accepts
+by running M-x describe-variable on it or visiting our web page:
+https://codeberg.org/martianh/mastodon.el
+
+We apologize for the inconvenience.
+")
+
+(defun mastodon-auth--get-browser-login-url ()
+ "Return properly formed browser login url."
+ (mastodon-http--append-query-string
+ (concat mastodon-instance-url "/oauth/authorize/")
+ `(("response_type" "code")
+ ("redirect_uri" ,mastodon-client-redirect-uri)
+ ("scope" ,mastodon-client-scopes)
+ ("client_id" ,(plist-get (mastodon-client) :client_id)))))
+
+(defvar mastodon-auth--explanation
+ (format
+ "
+1. A URL has been copied to your clipboard. Open this URL in a
+javascript capable browser and your browser will take you to your
+Mastodon instance's login page.
+
+2. Login to your account (%s) and authorize \"mastodon.el\".
+
+3. After authorization you will be presented an authorization
+code. Copy this code and paste it in the minibuffer prompt."
+ (mastodon-client--form-user-from-vars)))
+
+(defun mastodon-auth--show-notice (notice buffer-name &optional ask)
+ "Display NOTICE to user.
+NOTICE is displayed in vertical split occupying 50% of total
+width. The buffer name of the buffer being displayed in the
+window is BUFFER-NAME.
+
+When optional argument ASK is given which should be a string, use
+ASK as the minibuffer prompt. Return whatever user types in
+response to the prompt.
+
+When ASK is absent return nil."
+ (let ((buffer (get-buffer-create buffer-name))
+ (inhibit-read-only t)
+ ask-value window)
+ (set-buffer buffer)
+ (erase-buffer)
+ (insert notice)
+ (fill-region (point-min) (point-max))
+ (read-only-mode)
+ (setq window (select-window
+ (split-window (frame-root-window) nil 'left)
+ t))
+ (switch-to-buffer buffer t)
+ (when ask
+ (setq ask-value (read-string ask))
+ (kill-buffer buffer)
+ (delete-window window))
+ ask-value))
+
+(defun mastodon-auth--request-authorization-code ()
+ "Ask authorization code and return it."
+ (let ((url (mastodon-auth--get-browser-login-url))
+ authorization-code)
+ (kill-new url)
+ (setq authorization-code
+ (mastodon-auth--show-notice mastodon-auth--explanation
+ "*mastodon-notice*"
+ "Authorization Code: "))
+ authorization-code))
+
(defun mastodon-auth--generate-token ()
- "Make POST to generate auth token.
-
-If no auth-sources file, runs
-`mastodon-auth--generate-token-no-storing-credentials'. If
-auth-sources file exists, runs
-`mastodon-auth--generate-token-and-store'."
- (if (or (null mastodon-auth-source-file)
- (string= "" mastodon-auth-source-file))
- (mastodon-auth--generate-token-no-storing-credentials)
- (mastodon-auth--generate-token-and-store)))
-
-(defun mastodon-auth--generate-token-no-storing-credentials ()
- "Make POST to generate auth token, without using auth-sources file."
- (mastodon-http--post
- (concat mastodon-instance-url "/oauth/token")
- `(("client_id" . ,(plist-get (mastodon-client) :client_id))
- ("client_secret" . ,(plist-get (mastodon-client) :client_secret))
- ("grant_type" . "password")
- ("username" . ,(read-string "Email: " user-mail-address))
- ("password" . ,(read-passwd "Password: "))
- ("scope" . "read write follow"))
- nil
- :unauthenticated))
-
-(defun mastodon-auth--generate-token-and-store ()
- "Make POST to generate auth token.
-
-Reads and/or stores secrets in `MASTODON-AUTH-SOURCE-FILE'."
- (let* ((auth-sources (list mastodon-auth-source-file))
- (auth-source-creation-prompts
- '((user . "Enter email for %h: ")
- (secret . "Password: ")))
- (credentials-plist (nth 0 (auth-source-search
- :create t
- :host mastodon-instance-url
- :port 443
- :require '(:user :secret)))))
- (prog1
- (mastodon-http--post
- (concat mastodon-instance-url "/oauth/token")
- `(("client_id" . ,(plist-get (mastodon-client) :client_id))
- ("client_secret" . ,(plist-get (mastodon-client) :client_secret))
- ("grant_type" . "password")
- ("username" . ,(plist-get credentials-plist :user))
- ("password" . ,(let ((secret (plist-get credentials-plist :secret)))
- (if (functionp secret)
- (funcall secret)
- secret)))
- ("scope" . "read write follow"))
- nil
- :unauthenticated)
- (when (functionp (plist-get credentials-plist :save-function))
- (funcall (plist-get credentials-plist :save-function))))))
+ "Generate access_token for the user. Return response buffer."
+ (let ((authorization-code (mastodon-auth--request-authorization-code)))
+ (mastodon-http--post
+ (concat mastodon-instance-url "/oauth/token")
+ `(("grant_type" . "authorization_code")
+ ("client_secret" . ,(plist-get (mastodon-client) :client_secret))
+ ("client_id" . ,(plist-get (mastodon-client) :client_id))
+ ("code" . ,authorization-code)
+ ("redirect_uri" . ,mastodon-client-redirect-uri))
+ nil
+ :unauthenticated)))
(defun mastodon-auth--get-token ()
"Make a request to generate an auth token and return JSON response."
@@ -128,16 +168,33 @@ Reads and/or stores secrets in `MASTODON-AUTH-SOURCE-FILE'."
(json-read-from-string json-string))))
(defun mastodon-auth--access-token ()
- "Return exiting or generate new access token.
-
-If an access token for `mastodon-instance-url' is in
-`mastodon-auth--token-alist', return it.
-
-Otherwise, generate a token and pass it to
-`mastodon-auth--handle-token-reponse'."
- (if-let ((token (cdr (assoc mastodon-instance-url mastodon-auth--token-alist))))
- token
- (mastodon-auth--handle-token-response (mastodon-auth--get-token))))
+ "Return the access token to use with `mastodon-instance-url'.
+
+Generate/save token if none known yet."
+ (cond (mastodon-auth--token-alist
+ ;; user variables are known and
+ ;; initialised already.
+ (alist-get mastodon-instance-url mastodon-auth--token-alist
+ nil nil 'equal))
+ ((plist-get (mastodon-client--active-user) :access_token)
+ ;; user variables needs to initialised by reading from
+ ;; plstore.
+ (push (cons mastodon-instance-url
+ (plist-get (mastodon-client--active-user) :access_token))
+ mastodon-auth--token-alist)
+ (alist-get mastodon-instance-url mastodon-auth--token-alist
+ nil nil 'equal))
+ ((null mastodon-active-user)
+ ;; user not aware of 2FA related changes and has not set the
+ ;; `mastodon-active-user' properly. Make user aware and error
+ ;; out.
+ (mastodon-auth--show-notice mastodon-auth--user-unaware
+ "*mastodon-notice*")
+ (error "Variables not set properly"))
+ (t
+ ;; user access-token needs to fetched from the server and
+ ;; stored and variables initialised.
+ (mastodon-auth--handle-token-response (mastodon-auth--get-token)))))
(defun mastodon-auth--handle-token-response (response)
"Add token RESPONSE to `mastodon-auth--token-alist'.
@@ -148,6 +205,8 @@ Handle any errors from the server."
(pcase response
((and (let token (plist-get response :access_token))
(guard token))
+ (mastodon-client--make-user-active
+ (mastodon-client--store-access-token token))
(cdar (push (cons mastodon-instance-url token)
mastodon-auth--token-alist)))