配置 mbsync 支持 oauth2 验证登陆 outlook 邮箱的千辛万苦的折腾过程

最近 outlook 正式禁用了 app password,强制使用 oauth2 来登陆邮箱 (Modern Authentication Methods now needed to continue syncing Outlook Email in non-Microsoft email apps - Microsoft Support)

我习惯使用 mbsync+msmtp+notmuch 的非常 “unix” 风格的邮件工作流,这对于我自然是一个沉重的打击。经过了无数的折腾,终于搞定了。以下给大家写一个简要的分享:

以下的分享假设你已经熟悉了如何使用 gpg,并且已经有了一个过去曾可用的 mbsync 的配置。

配置Outlook的oauth2支持

设置mutt脚本以进行oauth2认证

我使用了来自neomutt的脚本进行oauth2认证,并进行了一些修改,如下面的补丁所示。

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': ' ',
     },
 }

在Azure中设置应用注册

要设置一个应用来同步邮件,请按照以下步骤操作:

  1. 使用你的个人Outlook邮箱或首选邮箱地址注册一个Azure账户。

  2. 登录Azure账户后,切换到"默认目录",因为新的应用注册强制要求必须位于某个目录底下。

  3. 按照neomutt提供的说明进行应用配置。需要注意的就是在 Manage|API permissions 的设置里,要把 IMAP/SMTP/POP 等等在 mutt 脚本里写明要求的权限都打开。

使用mutt脚本获取认证令牌

mutt_oauth2.py --verbose --authorize -t your/path/to/the/oauth/file

运行上述命令时会出现一个向导。只需按照提供的说明完成设置即可。我选择了devicecode认证流程,但其他方法也应该可以使用。完成以后 mutt 会将加密后的 token 写入 your/path/to/the/oauth/file

请注意,OAuth2令牌的有效期有限,会定期过期。如果你遇到mbsync的问题,请考虑令牌可能已过期。在这种情况下,你需要再次执行上述脚本以获取新的令牌。

配置mbsync

注意:如果你使用macOS,从homebrew安装的mbsync不支持xoauth2,请按照这个讨论中的说明从源码构建mbsync。我自己采取了一个非常 hackish 的方法:我开了一个运行支持 xoauth2 的 mbsync 的 Docker 。然后,我将主机的 maildir 文件夹挂载到这个容器中,使 mbsync 能够访问和同步我的邮件。

在你的mbsync配置文件中,像这样配置你的Outlook邮箱:

IMAPAccount myPersonalOutlook
PassCmd "mutt_oauth2.py -t your/path/to/the/outlook/oauth/file"
AuthMechs XOAUTH2
# 其余配置保持不变

配置msmtp

幸运的是,从homebrew安装的msmtp可以直接使用,不需要担心从源码构建。更改以下几行应该就足够了。

account outlook
auth xoauth2
passwordeval mutt_oauth2.py -t your/path/to/the/outlook/oauth/file
# 其余配置保持不变
8 个赞

之前试图折腾过,大概跟楼主成功的流程差不多,但我最终放弃了。。在折腾过程里顿感微软邮箱太邪恶了,直接把主力邮箱换到 migadu 用上自己的域名

1 个赞

推荐 GitHub - harishkrupo/oauth2ms 会自动 renew

macports 装的 mbsync (isync) 可以用 XOAUTH2

1 个赞

谢谢。打算把 mutt script 换成这个试一下。但是 refresh token 肯定还是会定时过期的。

至于用 port 那就算了,brew 用习惯了,不可能把一整套开发工具链都迁移过去的。我宁愿用 docker也不想手动改 brew 的 formula。。。

同意。outlook 邮箱很邪恶,根本就完全不考虑 unix 风格的工作流。之前我还发现 outlook 没办法使用 git-send-email,因为它不会 respect git-send-email 生成的邮件 id,导致你的一系列 patch 没办法合并到同一个 thread 底下。

但是没办法啊…刚出国的时候不懂事用了 outlook 邮箱,国内以短信验证码为主,在国外啥事情都是用邮箱、迁移太麻烦了。

1 个赞

最近因为这个问题,暂时放弃使用 Emacs管理邮件,后面准备换掉微软的邮箱。

我之前的outlook邮箱也是绑了国外不少的服务,但基本都是不用回复的信,用手机app看一下就好,慢慢迁移也不是很难。还是用自己的域名自由,邮件服务商发疯了可以随时离开。

mutt 的脚本也会自动用 refesh token 去获得最新的 access token。之前还是我吃了没文化的亏,不知道 oauth2 原来有 refresh token 和 access token 两种概念。原先我是直接把 mutt 脚本给的 accesstoken 给保存下来到别处,那自然就很快过期了。正确的做法就是每次都用 mutt 脚本来获得最新的 access token ,这样就只会90天才过期一次,而不是一小时一次。

mutt 脚本还没有任何依赖包,一个脚本文件直接就可以直接带走到处用,打算继续用它了,挺好的。

1 个赞

感谢楼主啊 :laughing:

最近几天网易云邮箱总是提示我hotmail登录错误,但是ios自带的邮件客户端一直工作正常,就没太在意,就当ms又抽风了。今天想起来,准备看看云邮箱客户端哪里有问题,才突然发现,好家伙neomutt里面hotmail有一个月没同步了。在google上搜索一番后,没想到直接指向了论坛里面的帖子。还好有楼主的教程,否则真是少不了折腾。

发现用oauth2登录之后有个问题,就是因为gpg加密之后,每过一段时间要同步邮件的话都需要重新如入gpg的密码。特别是我用systemctl定时同步邮箱的话,会频繁跳出输入密码的对话框。我对gpg完全没什么了解,有什么办法不用每次都输入gpg密码吗?

有推荐使用的邮箱吗?migadu在国内好使不

gpg 可以不需要输入密码直接工作的。

#!/bin/bash

# warmup gpg so that it will be ready for the next command without prompting to input passphrase
gpg --pinentry-mode=loopback --passphrase "`cat /root/passphrase`" -d /root/OAUTH_TOKENS.gpg > /dev/null 2>&1
bash "$@"

/root/OAUTH_TOKENS.gpg 可以是随便的一个加密的文件,只是需要解密一次之后 gpg 就不会弹出密码了。

我后来选择了 zoho 的邮件。

zoho 有国内版和国际版;我用的是国际版。

域名 $10/y,两个邮箱账号 $24/y,没有什么特别的使用问题。

1 个赞

谢谢。我本来觉得设置一下mutt_oauth2.py里面的ENCRYPTION_PIPEDECRYPTION_PIPE也许可以让token文件不加密,结果我试来试去,怎么填都不行 :rofl:

这个 azure 是怎么收费的?我注册提示要提供付费的银行卡等内容。

我也遇到注册完让填银行卡的步骤,我没填就直接把页面关了。然后发现从neomutt说明里面提到的这个网页能直接进入 :sweat_smile: