exchangelib 4.7.3连接Office 365共享邮箱报未授权错误排查
问题背景
需要编写Python脚本检测托管在Office 365上的共享邮箱入站邮件,开发使用exchangelib 4.7.3版本,初始测试代码如下:
#!/usr/bin/env python3 import logging from exchangelib import Credentials, Account, Configuration, DELEGATE def list_mails(): credentials = Credentials('user@company.com', 'SecretPassword') config = Configuration(server='outlook.office365.com', credentials=credentials) account = Account(primary_smtp_address='sharedmailbox@company.com', config=config, autodiscover=False, access_type=DELEGATE) for item in account.inbox.all().order_by('-datetime_received')[:100]: print(item.subject, item.sender, item.datetime_received) def main(): list_mails() if __name__ == "__main__": logging.basicConfig(level=logging.DEBUG) main()
故障现象
多次调整配置后运行代码始终抛出权限错误,核心报错信息如下:
DEBUG:exchangelib.protocol:No retry: no fail-fast policy DEBUG:exchangelib.protocol:Server outlook.office365.com: Retiring session 87355 DEBUG:exchangelib.protocol:Server outlook.office365.com: Created session 82489 DEBUG:exchangelib.protocol:Server outlook.office365.com: Releasing session 82489 Traceback (most recent call last): File "/Users/test/Code/./orderalert.py", line 24, in <module> main() File "/Users/test/Code/./orderalert.py", line 20, in main list_mails() File "/Userstest/Code/./orderalert.py", line 11, in list_mails account = Account(primary_smtp_address='sharedmailbox@company.com', config=config, autodiscover=False, access_type=DELEGATE) File "/opt/homebrew/lib/python3.9/site-packages/exchangelib/account.py", line 204, in __init__ self.version = self.protocol.version.copy() File "/opt/homebrew/lib/python3.9/site-packages/exchangelib/protocol.py", line 483, in version self.config.version = Version.guess(self, api_version_hint=self._api_version_hint) File "/opt/homebrew/lib/python3.9/site-packages/exchangelib/version.py", line 233, in guess list(ResolveNames(protocol=protocol).call(unresolved_entries=[name])) File "/opt/homebrew/lib/python3.9/site-packages/exchangelib/services/common.py", line 187, in _elems_to_objs for elem in elems: File "/opt/homebrew/lib/python3.9/site-packages/exchangelib/services/common.py", line 245, in _chunked_get_elements yield from self._get_elements(payload=payload_func(chunk, **kwargs)) File "/opt/homebrew/lib/python3.9/site-packages/exchangelib/services/common.py", line 265, in _get_elements yield from self._response_generator(payload=payload) File "/opt/homebrew/lib/python3.9/site-packages/exchangelib/services/common.py", line 227, in _response_generator response = self._get_response_xml(payload=payload) File "/opt/homebrew/lib/python3.9/site-packages/exchangelib/services/common.py", line 343, in _get_response_xml r = self._get_response(payload=payload, api_version=api_version) File "/opt/homebrew/lib/python3.9/site-packages/exchangelib/services/common.py", line 297, in _get_response r, session = post_ratelimited( File "/opt/homebrew/lib/python3.9/site-packages/exchangelib/util.py", line 917, in post_ratelimited protocol.retry_policy.raise_response_errors(r) File "/opt/homebrew/lib/python3.9/site-packages/exchangelib/protocol.py", line 688, in raise_response_errors raise UnauthorizedError(f"Invalid credentials for {response.url}") exchangelib.errors.UnauthorizedError: Invalid credentials for https://outlook.office365.com/EWS/Exchange.asmx DEBUG:exchangelib.protocol:Server outlook.office365.com: Closing sessions
已尝试的无效排查方案
- 确认
user@company.com凭据有效,可正常登录网页端,账号未强制启用MFA等验证策略 - 尝试开启autodiscover自动发现模式创建Account实例,代码为
account = Account('sharedmailbox@company.com', credentials=credentials, autodiscover=True, access_type=DELEGATE) - 连接时禁用TLS验证
- 按建议为邮箱地址配置on.company.com的on子域名
- 在微软官方连接测试站点验证自动发现整体配置正常
- Office 365 Azure AD管理后台可看到脚本发起的登录记录为成功状态
故障根因
该问题和代码逻辑、本地网络配置无关,由三点共同导致:
- 微软自2022年10月起对所有Exchange Online租户默认禁用EWS协议的基础身份验证(Basic Auth),原代码中用明文账号密码初始化
Credentials的方式走的就是已被禁用的Basic Auth流程。Azure AD后台显示登录成功仅代表账号密码合法性校验通过,到EWS协议鉴权环节会被微软直接拦截返回401,触发凭据无效报错。 - 使用的
exchangelib 4.7.3版本未适配Office 365的OAuth2现代认证默认配置,无论怎么调整Basic Auth相关参数都无法绕过微软的安全限制。 - 即使认证方式配置正确,如果委托账号未被显式授予目标共享邮箱的「完全访问(Full Access)」权限,或是Exchange管理中心禁用了该委托账号的EWS协议访问权限,也会触发完全相同的401错误。
修复方案
按以下步骤操作即可解决:
- 先升级exchangelib到最新稳定版,旧版本对Office 365的OAuth2支持存在已知bug:
pip install --upgrade exchangelib - 到Exchange Online管理中心确认两项权限配置:
- 找到登录用的委托账号
user@company.com,在邮箱功能设置中确认EWS协议为允许状态 - 找到目标共享邮箱
sharedmailbox@company.com,在委派权限设置中给user@company.com添加「完全访问」权限,添加后等待15分钟等待权限跨节点同步生效
- 找到登录用的委托账号
- 替换原有认证逻辑,使用OAuth2交互式认证适配Office 365,可直接运行的参考代码如下:
#!/usr/bin/env python3 import logging from exchangelib import Account, Configuration, DELEGATE, O365InteractiveCredentials def list_mails(): # 首次运行会弹出浏览器完成账号登录,后续会本地缓存token无需重复认证 # 客户端ID使用微软官方公开的Office桌面端EWS应用ID,无需自行注册Azure应用 credentials = O365InteractiveCredentials( client_id='d3590ed6-52b3-4102-aeff-aad2292ab01c', username='user@company.com' ) config = Configuration(server='outlook.office365.com', credentials=credentials) account = Account( primary_smtp_address='sharedmailbox@company.com', config=config, autodiscover=False, access_type=DELEGATE ) for item in account.inbox.all().order_by('-datetime_received')[:100]: print(item.subject, item.sender, item.datetime_received) def main(): list_mails() if __name__ == "__main__": logging.basicConfig(level=logging.WARNING) main()提示:如果需要完全无人值守运行,可在Azure AD注册公共客户端应用,授予应用
EWS.AccessAsUser.All委托权限,开启公共客户端流后替换为OAuth2Credentials传入凭证即可。
内容的提问来源于stack exchange,提问作者Kami
相关产品推荐
相关产品推荐

