Browse Source

Add support for bitwarden API key based login

<p dir="auto"><span class="issue-keyword tooltipped tooltipped-se" aria-label="This pull request closes issue #10.">Fixes</span> <a class="issue-link js-issue-link" data-error-text="Failed to load title" data-id="1203413750" data-permission-text="Title is private" data-url="https://github.com/seanfarley/emacs-bitwarden/issues/10" data-hovercard-type="issue" data-hovercard-url="/seanfarley/emacs-bitwarden/issues/10/hovercard" href="https://github.com/seanfarley/emacs-bitwarden/issues/10">#10</a></p>
<p dir="auto">This adds two custom variables <code class="notranslate">bitwarden-api-secret-key</code> and <code class="notranslate">bitwarden-api-client-id</code> that have the same interface as the existing <code class="notranslate">bitwarden-automatic-unlock</code>. These are optional but used as the prefered login method if non-nil. I have updated the readme providing guidance on how to obtain an API key and client_id and emphasizing that this approach only need to be used if users are storing their valult on bitwarden servers.</p>
Sean Farley 3 years ago
parent
commit
276b363f13
2 changed files with 59 additions and 9 deletions
  1. 31 0
      README.org
  2. 28 9
      bitwarden.el

+ 31 - 0
README.org

@@ -29,6 +29,37 @@ the [[file:bitwarden.el::(defcustom%20bitwarden-automatic-unlock%20nil][bitwarde
         (plist-get entry :secret)))
 #+end_src
 
+If your bitwarden store is kept on bitwarden servers (as opposed to hosting your
+own server), you may find that the above approach does not work and gives a
+=Symbol’s value as variable is void: print-message= error. This may be due to a
+recent change in bitwarden login procedures which require the use of an API
+application key. To check if this is indeed the problem try logging in from the
+command line with =bw login [username] 'password'= if you are prompted for an API
+key then the following instructions may resolve your problem.
+
+Login to the [[https://vault.bitwarden.com][bitwarden web interface]], login to your vault, go to the =settings=
+tab and scroll down to the API Key section. Follow the instructions to setup
+your API key. You will need both your =client_id= and your =client_secret=. Then add
+to your init.el configuration two functions =bitwarden-api-client-id= and
+=bitwarden-api-client-secret= which have the same specification format as
+=bitwarden-automatic-unlock=. Here is an example emacs configuration,
+
+#+begin_src emacs-lisp
+(setq bitwarden-api-secret-key
+      (plist-get (car (auth-source-search :host "bitwarden.key"))
+                 :secret))
+(setq bitwarden-api-client-id
+      (plist-get (car (auth-source-search :host "bitwarden.id"))
+                 :secret))
+#+end_src
+
+For those using =authinfo.gpg=, these two lines have the form,
+
+#+begin_quote
+machine bitwarden.key login YYY password XXX
+machine bitwarden.id login YYY password XXX
+#+end_quote
+
 ** auth-source
 
 There is read-only support for auth-source as well. You can run

+ 28 - 9
bitwarden.el

@@ -80,6 +80,21 @@ example, this can be the :secret plist from
   :group 'bitwarden
   :type 'function)
 
+(defcustom bitwarden-api-secret-key nil
+  "Optional function to be called to return API secret key.
+
+Set this to a lambda that will evaluate to a string (the API secret key)."
+  :group 'bitwarden
+  :type 'function)
+
+(defcustom bitwarden-api-client-id nil
+  "Optional function to be called to return the API client id..
+
+Set this to a lambda that will evaluate to a string (the API client id)."
+  :group 'bitwarden
+  :type 'function)
+
+
 (defconst bitwarden--err-logged-in "you are not logged in")
 (defconst bitwarden--err-multiple  "more than one result found")
 (defconst bitwarden--err-locked    "vault is locked")
@@ -90,14 +105,14 @@ example, this can be the :secret plist from
   "Check if `bitwarden-user' is logged in.
 Returns nil if not logged in."
   (let* ((ret (apply #'bitwarden--raw-runcmd "login" '("--check")))
-	 (exit-code (nth 0 ret)))
+         (exit-code (nth 0 ret)))
     (eq exit-code 0)))
 
 (defun bitwarden-unlocked-p ()
   "Check if `bitwarden-user' is loged in.
 Returns nil if not unlocked."
   (let* ((ret (apply #'bitwarden--raw-runcmd "unlock" '("--check")))
-	 (exit-code (nth 0 ret)))
+         (exit-code (nth 0 ret)))
     (eq exit-code 0)))
 
 (defun bitwarden--raw-runcmd (cmd &rest args)
@@ -223,12 +238,16 @@ printed to minibuffer."
 If run interactively PRINT-MESSAGE gets set and messages are
 printed to minibuffer."
   (interactive "p")
-  (unless bitwarden-user
-    (setq bitwarden-user (read-string "Bitwarden email: ")))
-
-  (let ((pass (when bitwarden-automatic-unlock
-                (funcall bitwarden-automatic-unlock))))
-    (bitwarden--raw-unlock (list "login" bitwarden-user pass) print-message)))
+  (if (and bitwarden-api-client-id bitwarden-api-secret-key)
+      (progn
+        (setenv "BW_CLIENTID" (funcall bitwarden-api-client-id))
+        (setenv "BW_CLIENTSECRET" (funcall bitwarden-api-secret-key))
+        (bitwarden--raw-unlock (list "login") print-message))
+    (unless bitwarden-user
+      (setq bitwarden-user (read-string "Bitwarden email: ")))
+    (let ((pass (when bitwarden-automatic-unlock
+                  (funcall bitwarden-automatic-unlock))))
+      (bitwarden--raw-unlock (list "login" bitwarden-user pass) print-message))))
 
 (defun bitwarden-lock ()
   "Lock the bw vault.  Does not ask for confirmation."
@@ -614,7 +633,7 @@ If optional argument GROUP is given, only entries in GROUP will be listed."
          :notify 'bitwarden-list-cancel-dialog
          "Cancel")
         (goto-char (point-min)))
-    (bitwarden--message "not logged in!" nil t)))
+    (bitwarden--message "vault not unlocked!" nil t)))
 
 (provide 'bitwarden)