Feature Request: Set preferred authentication method
ement-connect hardcodes a completing-read for the authentication method in the flows-callback label. I'd like to be able to set a preferred authentication method to avoid the additional prompt when connecting.
I can imagine implementing a user option for this several ways depending on how flexible you want it to be. The simplest would just be a symbol indicating the preferred method. e.g.
(defcustom ement-preferred-authentication-method 'password)
Slightly more complex would be allowing a preference per homeserver or arbitrary predicate matching.
Hi Nic,
Please see the discussion here: https://github.com/alphapapa/ement.el/issues/24 Given that 1) this is an interactive command, and 2) that it's not intended for Matrix users to enter their password or "log in" every time they connect, and 3) that session saving will probably be enabled by default at some point, I think it wouldn't be very useful to add a way to specify the login method for users to avoid having one prompt on the rare occasion that they need to log in again from scratch--they'd spend more time reading the documentation and writing code than by just answering the completing-read prompt.
But if you feel more strongly about this, feel free to elaborate.
it's not intended for Matrix users to enter their password or "log in" every time they connect
Interesting. I'd much prefer that to storing a plain-text auth token to disk. I assumed ement-disconnect invalidated a session, but I now see that's not the case. How do you feel about Ement.el being able to manage sessions in general? Element has basic session managemnet options, IIRC, like "rename a session" and "sign out of this session". I'd be happy with just the sign out. That would allow me to invalidate the session as part of ement-disconnect-hook without changing anything else on ement's side.
Well, of course, it would be good if Ement offered a UI for managing sessions, similar to what Element offers. That should go on the to-do list.
To reject the concept of session token-based authentication altogether is an interesting idea, but that would seem antithetical to the Matrix specification, so it's probably not something I'd want to offer support for. For one reason, it would seem impractical to guarantee that a session is invalidated when "disconnecting," because Emacs could crash, the network connection could fail, etc.
But as you said, if we were to offer a UI for session management, you could write a few lines in your config to call the appropriate functions and do whatever you want, such as invalidating a session (although I don't know if Matrix allows a session to invalidate itself--I haven't looked at that part of the spec).
But if the real problem you want to solve is that of writing a plain-text token to disk, that's probably what we should aim to solve. (Keep in mind that, AFAIK a browser using Element doesn't encrypt the session token in a secure way, unless you a) use full-disk encryption where the browser's profile is stored, or b) encrypt the browser's profile with a password within the browser (which used to be possible with Firefox, AFAIK, but I don't know if it still is, or if other browsers offer anything like that).)
FWIW there's a recent thread on emacs-devel where we've been talking about ways to securely store data in Emacs: https://lists.gnu.org/archive/html/emacs-devel/2023-06/msg00006.html
With regard to doing so in Ement, the real problem I see is that of UI/UX: if we pick one of these backends to use, that backend itself probably would require some configuration outside of Ement, and that's not something I want to require users to do in order to start using it.
So probably what's needed is some kind of flexible API that could be used to store/retrieve secret data like the session token; and by default, it would need to store it in plain-text, as it currently does. Then we'd need to carefully document how users can change to a different storage backend, make a new session with a new token, and manually invalidate the old one that was stored in plain text. (I don't look forward to fielding questions about that process.)
What do you think? Thanks.
Well, of course, it would be good if Ement offered a UI for managing sessions, similar to what Element offers. That should go on the to-do list.
Agreed. Glad to hear it.
it would seem impractical to guarantee that a session is invalidated when "disconnecting," because Emacs could crash, the network connection could fail, etc.
That's a fair point. I wouldn't expect official support for that workflow in any case.
But as you said, if we were to offer a UI for session management, you could write a few lines in your config to call the appropriate functions and do whatever you want, such as invalidating a session (although I don't know if Matrix allows a session to invalidate itself--I haven't looked at that part of the spec).
Element is able to sign itself out for the current device. I assume it's possible. I took a peek at https://spec.matrix.org/historical/client_server/r0.6.1#post-matrix-client-r0-keys-query and and came up with this crude example for listing verified devices:
;; -*- lexical-binding: t; -*-
(require 'ement)
(require 'cl-lib)
(when-let ((session (cdar ement-sessions)) ;;@INCOMPLETE: handle mutliple sessions
(mxid (ement-user-id (ement-session-user session))))
(ement-api session "keys/query"
:method 'post
:then (lambda (res)
(when-let ((display-names
(cl-loop for key in (alist-get (intern mxid) (alist-get 'device_keys res))
collect (alist-get 'device_display_name (alist-get 'unsigned (cdr key))))))
(with-current-buffer (get-buffer-create (format "*Ement Devices (%s)*" mxid))
(read-only-mode -1)
(erase-buffer)
(goto-char (point-min))
(insert (string-join display-names "\n"))
(pop-to-buffer (current-buffer))
(special-mode))))
:data (json-encode (ement-alist "device_keys" (ement-alist mxid [])))))
Yields:
app.element.io (Firefox, Linux)
Element Android
app.element.io: Firefox on Linux
I can imagine something like ement-device-list which groups devices (verified, unverified) and a couple of commands to operate on them (ement-device-sign-out, ement-device-rename).
Forgive me if I have the terminology or any concepts wrong (perhaps "session" is better than "device" above).
I'm not up to speed with the Matrix spec.
AFAIK a browser using Element doesn't encrypt the session token in a secure way,
I'm definitely in the minority, but I invalidate those sessions as well when I'm done with them.
FWIW there's a recent thread on emacs-devel where we've been talking about ways to securely store data in Emacs: https://lists.gnu.org/archive/html/emacs-devel/2023-06/msg00006.html
Thanks. I agree it would be a useful API.
What do you think? Thanks.
Addition of session management would suit me. And thank you for the excellent package. Even just messing around with the example above, I can tell it's well engineered. When I have time I'll have to dig into it more.
Edit: Never mind, I've misunderstood... the desired/discussed API is for avoiding unencrypted data persisting in memory in Emacs after reading it from the encrypted file, and I don't believe auth-sources helps with that.
Initial comment follows...
So probably what's needed is some kind of flexible API that could be used to store/retrieve secret data like the session token; and by default, it would need to store it in plain-text, as it currently does. Then we'd need to carefully document how users can change to a different storage backend, make a new session with a new token, and manually invalidate the old one that was stored in plain text. (I don't look forward to fielding questions about that process.)
Unless something disqualifies it in this case, I presume the general API is:
-
C-h i g (emacs)Authentication -
C-h i g (auth)
if the real problem you want to solve is that of writing a plain-text token to disk, that's probably what we should aim to solve.
In general Emacs is very good at seamlessly handling encryption if the file happens to be encrypted.
For instance (json-read "~/.foo.json.gpg") Just Works (grabbing an example I've used before).
I just tested encrypting ~/.cache/ement.el and then customizing the ement-sessions-file user option to be "~/.cache/ement.el.gpg" and I can report that this also Just Works, with M-x ement-connect resulting in messages:
Decrypting /path/to/.cache/ement.el.gpg...done
Ement: Read sessions.
Ement: Sync request sent, waiting for response...
Ement: Response arrived after 18.83 seconds. Reading 1.7M JSON response...
Ement: Reading JSON took 0.42 seconds
Ement: Reading events...
Ement: Sync request sent, waiting for response...
Ement: Sync done. Use commands ‘ement-list-rooms’ or ‘ement-view-room’ to view a room.
Edit: After more testing, saving the session file (e.g. when exiting Emacs or running ement-disconnect) prompts me for the key to re-encrypt the file with, so that's a minor speed bump; but it's still functional.
Edit 2: It looks like that could be circumvented by writing to the file from a buffer which had visited/decrypted the file. So I think without ement needing any encryption-specific code, if it simply visits the file and writes its changes from that same buffer, the gpg key prompt at write-time will go away.
Well, of course, it would be good if Ement offered a UI for managing sessions, similar to what Element offers. That should go on the to-do list.
list-processes and its process-menu-mode might serve as a useful template for a simple tabulated-list-mode-based UI.
Edit: Never mind, I've misunderstood... the desired/discussed API is for avoiding unencrypted data persisting in memory in Emacs after reading it from the encrypted file, and I don't believe auth-sources helps with that.
I documented problems with auth-source here: https://github.com/alphapapa/ement.el/issues/109 It seems especially unsuitable for programmatically setting, changing, and deleting data. I don't want users of Ement.el to have to edit configuration files or manually set Lisp variables, etc.
In general Emacs is very good at seamlessly handling encryption if the file happens to be encrypted.
Please see https://github.com/alphapapa/ement.el/pull/151.
list-processes and its process-menu-mode might serve as a useful template for a simple tabulated-list-mode-based UI.
I've implemented a number of tabulated-list-mode-based views before, including in this project: https://github.com/alphapapa/ement.el/blob/master/ement-tabulated-room-list.el But then I wrote taxy-magit-section as an alternative, e.g. https://github.com/alphapapa/ement.el/blob/master/ement-room-list.el