My email workflow mainly consists of the following parts:
- Email synchronizer (indexer):
mbsync
(orisync
, they are the same thing) - Email Sender:
msmtp
- Mail User Agent (MUA):
notmuch
andemacs
mutt_oauth2.py
from neomutt for oauth2 authentication with outlook email.
For how mbsync + notmuch + msmtp
above work together, Protesilaos
Stavrou provides a great explanation.
I will not delve into the configuration of mbsync
and msmtp
here, as
abundant resources are available across the internet. Instead, I’ll
focus on aspects less commonly discussed online: specifically, how to
set up notmuch effectively and configure OAuth2 authentication with
mbsync.
This guide will address two key areas:
- My hacks on
notmuch
for efficient email management - Configuring OAuth2 authentication for
mbsync
Notmuch is a tag-centric email client, but its tags don’t usually sync up with IMAP folders. It also can’t move emails between maildir folders (which are basically mailboxes in your email account) out of the box. This setup works fine if you only use Notmuch to read your emails. However, things can get messy if you’re also using other email clients, like on your phone, and want everything to stay in sync across your devices.
This section covers some handy workarounds, mainly focusing on moving emails between maildir folders. These tricks help sync changes with your email mailboxes, making it easier to use Notmuch alongside other email clients on different devices without things getting out of sync.
Outlook enforced oauth2 authentication for outlook email. See this news.
I used the script from neomutt for oauth2 authentication, with some modification in the patch below.
diff --git a/mutt_oauth2.py b/bin/mutt_oauth2.py
old mode 100644
new mode 100755
index c973b98..c296dff
--- a/mutt_oauth2.py
+++ b/bin/mutt_oauth2.py
@@ -45,7 +45,7 @@ import readline
# encryption and decryption pipes you prefer. They should read from standard
# input and write to standard output. The example values here invoke GPG,
# although won't work until an appropriate identity appears in the first line.
-ENCRYPTION_PIPE = ['gpg', '--encrypt', '--recipient', 'YOUR_GPG_IDENTITY']
+ENCRYPTION_PIPE = ['gpg', '--encrypt', '--recipient', '<[email protected]>']
DECRYPTION_PIPE = ['gpg', '--decrypt']
registrations = {
@@ -66,17 +66,17 @@ registrations = {
'authorize_endpoint': 'https://login.microsoftonline.com/common/oauth2/v2.0/authorize',
'devicecode_endpoint': 'https://login.microsoftonline.com/common/oauth2/v2.0/devicecode',
'token_endpoint': 'https://login.microsoftonline.com/common/oauth2/v2.0/token',
- 'redirect_uri': 'https://login.microsoftonline.com/common/oauth2/nativeclient',
+ 'redirect_uri': 'https://login.live.com/oauth20_desktop.srf', # use the redirect url of outlook instead of office365
'tenant': 'common',
'pop_endpoint': 'outlook.office365.com',
- 'smtp_endpoint': 'smtp.office365.com',
+ 'smtp_endpoint': 'smtp-mail.outlook.com', # use the SMTP endpoint of outlook instead of office365
'sasl_method': 'XOAUTH2',
'scope': ('offline_access https://outlook.office.com/IMAP.AccessAsUser.All '
'https://outlook.office.com/POP.AccessAsUser.All '
'https://outlook.office.com/SMTP.Send'),
- 'client_id': '',
- 'client_secret': '',
+ 'client_id': 'my app id, see the next section on how to register your own app',
+ 'client_secret': ' ',
},
}
To set up an app to sync emails, follow these steps:
- Sign up for an Azure account using your personal Outlook email or preferred email address.
- After logging in to your Azure account, switch to the “default directory” as new app registrations are currently restricted to directories only.
- Follow the instruction provided by neomutt for your app
configuration. During the app configuration process, you’ll find
the specified settings under the
Manage | Authentication
andManage | API permissions
sidebars in your app portal. It’s crucial to enable access for IMAP, SMTP, POP, and other relevant scopes as detailed in the mutt script under theManage | API permissions
sidebar.
mutt_oauth2.py --verbose --authorize -t your/path/to/the/oauth/file
Upon executing the aforementioned command, a wizard will guide you
through the setup process. Follow the on-screen instructions to
proceed. I used the ‘devicecode’ authentication flow, although other
options should also work. Once the setup is complete, the mutt script
will encrypt the OAuth2 token and save it to
your/path/to/the/oauth/file
.
Please be aware that OAuth2 tokens expire regularly. I recommend familiarize yourself with the concept of refresh tokens and access tokens in the OAuth2 protocol. If you encounter issues with mbsync, consider the possibility of token expiration. In such cases, you may need to execute the command again to obtain a fresh token.
Warning: if you are using macOS, the mbsync
installed from homebrew
does not work with xoauth2
, following the instruction at this
thread
to build the mbsync
from source. I took my own hackish approach: I
created a Docker container that runs mbsync with xoauth2 support. I
then mounted my host machine’s maildir folder to this container,
allowing mbsync to access and sync my emails. The Docker container is
based on Ubuntu and includes the following installed packages:
isync libsasl2-modules-kdexoauth2 ca-certificates openssl libsasl2-modules python3
In your mbsync config file, configure your outlook email like this:
IMAPAccount myPersonalOutlook
PassCmd "mutt_oauth2.py -t your/path/to/the/outlook/oauth/file"
AuthMechs XOAUTH2
# And your rest configs just works
You are lucky that the msmtp
installed from homebrew
just works, no
need to worry about building from source. Changing the following lines
should be sufficient.
account outlook
auth xoauth2
passwordeval mutt_oauth2.py -t your/path/to/the/outlook/oauth/file
# And your rest configs just works
As someone switching from mu4e
to notmuch
, I’ve found that the
resources I used to set up mu4e are still super helpful for
configuring notmuch
. It’s great that I can keep using mbsync
for
syncing my emails and msmtp for sending messages. These tools work
just as well with notmuch
, making the transition much smoother.
There are two primary reasons for my decision to transition. Firstly, mu4e’s backward compatibility is horrible, with many plugins dependent on mu4e often breaking after major version updates. Secondly, notmuch offers a threaded conversation view, whereas mu4e only provides a threaded tree view. While I had implemented thread-folding in mu4e, it worked but the result was less than ideal. Even the official thread-folding feature introduced in mu4e 1.12 has numerous issues. In contrast, the conversation view in notmuch works great for threads.