Felix Hanley

Using Mutt with Office365

2025-11-24 mutt office365

Apparently authenticating IMAP over TLS is not good enough so we have to jump through the multiple OAuth hoops.

Voidlinux

$ sudo xbps-install neomutt cyrus-sasl-xoauth2 notmuch goimapnotify isync

Use the helper script that comes with neomutt at /usr/share/neomutt/oauth2/mutt_oauth2.py. Change it as required below.

Age encryption

The mutt_oauth2.py script uses GPG, update (and copy) it to use age:

@@ -44,8 +44,8 @@
 # whose usage does not require additional verification. Specify whichever
 # encryption and decryption pipes you prefer. They should read from standard
 # input and write to standard output. The example values here invoke GPG.
-ENCRYPTION_PIPE = ['gpg', '--encrypt', '--default-recipient-self']
-DECRYPTION_PIPE = ['gpg', '--decrypt']
+ENCRYPTION_PIPE = ['age', '--encrypt', '--recipient', 'age1yzawwghdhv7m3845j8d8j4nze3qpye4qtlhr3u633xdwvxzyg9sqmfpcvy']
+DECRYPTION_PIPE = ['age', '--decrypt', '-i', '/home/felix/.age/mutt.txt']
 
 registrations = {
     'google': {

Tokens

Change the mutt_oauth2.py script to redirect to localhost. This fails but we can grab the code from the request.

--- /old 2025-11-24 10:12:28.187218739 +1100
+++ /new    2025-11-24 10:12:19.191166524 +1100
@@ -63,7 +63,7 @@
         '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': 'http://localhost',
         'tenant': 'common',
         'imap_endpoint': 'outlook.office365.com',
         'pop_endpoint': 'outlook.office365.com',

Run the script to set new tokens:

$ mutt_oauth2.py --authorize ~/.mutt/toennjes.tokens

and when asked use the client_id from Thunderbird 9e5f94bc-e8a4-4e73-b8be-63364c29d753 (taken from https://github.com/mozilla/releases-comm-central/blob/master/mailnews/base/src/OAuth2Providers.sys.mjs#L192)

Then things like mbsync just work like this (set in config):

PassCmd "$HOME/bin/mutt_oauth2.py ~/.mutt/toennjes.tokens"

SMTP

To send you need the following in mutt’s config:

set smtp_url = smtp://${imap_user}@smtp.office365.com:587
set ssl_force_tls = yes
set ssl_use_sslv3 = yes
set smtp_authenticators = 'xoauth2'
set smtp_oauth_refresh_command = "$HOME/bin/mutt_oauth2.py ~/.mutt/toennjes.tokens"