使用Python MSAL库实现Office 365 IMAP OAuth2认证失败排查
Office 365 IMAP OAuth2(客户端凭证流)认证失败排查
我正尝试将旧版邮件机器人从基础认证(Basic authentication)升级为OAuth2认证,因为基础认证将于两天后废弃。
文档说明应用可保留原有逻辑,仅替换认证部分:
开发人员构建的使用这些协议发送、读取或处理邮件的应用,可保留原有协议,但需为用户实现安全的现代认证体验。该功能基于Microsoft Identity platform v2.0构建,支持访问Microsoft 365邮箱账户。
我明确选择了客户端凭证流(client credentials flow),因为文档指出:
此类授权通常用于无需用户即时交互、需在后台运行的服务器到服务器交互场景。
我编写了Python脚本,使用MSAL库获取Access Token,现在尝试用该Token认证IMAP服务器。脚本如下:
import imaplib import msal import logging app = msal.ConfidentialClientApplication( 'client-id', authority='https://login.microsoftonline.com/tenant-id', client_credential='secret-key' ) result = app.acquire_token_for_client(scopes=['https://graph.microsoft.com/.default']) def generate_auth_string(user, token): return 'user=%s\1auth=Bearer %s\1\1' % (user, token) # IMAP time! mailserver = 'outlook.office365.com' imapport = 993 M = imaplib.IMAP4_SSL(mailserver,imapport) M.debug = 4 M.authenticate('XOAUTH2', lambda x: generate_auth_string('user@mydomain.com', result['access_token'])) print(result)
IMAP认证失败,即使设置了M.debug = 4,输出信息也无帮助:
22:56.53 > b'DBDH1 AUTHENTICATE XOAUTH2' 22:56.53 < b'+ ' 22:56.53 write literal size 2048 22:57.84 < b'DBDH1 NO AUTHENTICATE failed.' 22:57.84 NO response: b'AUTHENTICATE failed.' Traceback (most recent call last): File "/home/ubuntu/mini-oauth.py", line 21, in <module> M.authenticate("XOAUTH2", lambda x: generate_auth_string('user@mydomain.com', result['access_token'])) File "/usr/lib/python3.10/imaplib.py", line 444, in authenticate raise self.error(dat[-1].decode('utf-8', 'replace')) imaplib.IMAP4.error: AUTHENTICATE failed.
已排查内容
- 旧方法已失效,其建议的scope无法生成Access Token。
- 客户端凭证流要求使用
https://graph.microsoft.com/.default作为scope,不确定该scope是否包含IMAP资源所需的https://outlook.office.com/IMAP.AccessAsUser.All权限。 - 验证了生成的SASL XOAUTH2字符串符合微软文档示例:
import base64 user = 'test@contoso.onmicrosoft.com' token = 'EwBAAl3BAAUFFpUAo7J3Ve0bjLBWZWCclRC3EoAA' xoauth = "user=%s\1auth=Bearer %s\1\1" % (user, token) xoauth = xoauth.encode('ascii') xoauth = base64.b64encode(xoauth) xoauth = xoauth.decode('ascii') xsanity = 'dXNlcj10ZXN0QGNvbnRvc28ub25taWNyb3NvZnQuY29tAWF1dGg9QmVhcmVyIEV3QkFBbDNCQUFVRkZwVUFvN0ozVmUwYmpMQldaV0NjbFJDM0VvQUEBAQ==' print(xoauth == xsanity) # prints True
- 有资料提到可能需要获取多个Token,一个用于Graph,一个用于IMAP连接,这是否是我缺失的步骤?
疑问
请问我哪里出错了?或者如何从IMAP服务器获取更详细的认证失败原因?
内容的提问来源于stack exchange,提问作者quickshiftin
相关产品推荐
相关产品推荐

