diff options
author | martianh <martianh@noreply.codeberg.org> | 2022-03-23 08:04:07 +0100 |
---|---|---|
committer | martianh <martianh@noreply.codeberg.org> | 2022-03-23 08:04:07 +0100 |
commit | cecd5de060a56f13f7f7eb4528b341027812faab (patch) | |
tree | b612438cd9e5b46c0300c2ae1dc1b007698226bb /lisp/mastodon-auth.el | |
parent | c7b475160d2e7712e339e15adf168529f71b52c6 (diff) | |
parent | 56fa25df379623e79261b535cd724db3ed979d44 (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.el | 201 |
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))) |