使用Python exchangelib读取Exchange共享邮箱遇invalid_token错误
问题:使用exchangelib读取Exchange共享邮箱时遇到invalid_token错误
我尝试用Python的exchangelib库读取Exchange共享邮箱,代码如下:
def getExchangeMailBox(): username = 'user@domain.com' ## make sure to give valid address tenant_id = '<mytenantid>' client_id = '<myclientid>' secret_value = '<mysecret>' version = Version(build=Build(15, 0, 12, 34)) credentials = OAuth2Credentials(client_id=client_id, client_secret=secret_value, tenant_id=tenant_id,identity=username) config = Configuration(service_endpoint = 'https://outlook.office365.com/EWS/Exchange.asmx', credentials=credentials, auth_type=OAUTH2 , version=version) account = Account(primary_smtp_address=username, credentials=credentials, autodiscover=False, config=config, access_type=IMPERSONATION) for item in account.inbox.all().order_by("-datetime_received")[:100]: logger.info(item.subject, item.sender, item.datetime_received)
已在Entra门户注册应用,并配置了以下Microsoft Graph权限:
- EWS.AccessAsUser.All
- Mail.Read
- User.Read
但运行时出现如下错误:
WARNING:tzlocal:/etc/timezone is deprecated on Debian, and no longer reliable. Ignoring. ERROR:visualizer:Exception on / [GET] Traceback (most recent call last): File "/home/adm-mfrau/visualizer/.venv/lib/python3.10/site-packages/cached_property.py", line 63, in __get__ return obj_dict[name] KeyError: 'inbox' During handling of the above exception, another exception occurred: Traceback (most recent call last): File "/home/adm-mfrau/visualizer/.venv/lib/python3.10/site-packages/cached_property.py", line 63, in __get__ return obj_dict[name] KeyError: 'root' During handling of the above exception, another exception occurred: Traceback (most recent call last): File "/home/adm-mfrau/visualizer/.venv/lib/python3.10/site-packages/flask/app.py", line 1511, in wsgi_app response = self.full_dispatch_request() File "/home/adm-mfrau/visualizer/.venv/lib/python3.10/site-packages/flask/app.py", line 919, in full_dispatch_request rv = self.handle_user_exception(e) File "/home/adm-mfrau/visualizer/.venv/lib/python3.10/site-packages/flask/app.py", line 917, in full_dispatch_request rv = self.dispatch_request() File "/home/adm-mfrau/visualizer/.venv/lib/python3.10/site-packages/flask/app.py", line 902, in dispatch_request return self.ensure_sync(self.view_functions[rule.endpoint])(**view_args) # type: ignore[no-any-return] File "/home/adm-mfrau/visualizer/visualizer.py", line 69, in returnExcelDF getExchangeMailBox() File "/home/adm-mfrau/visualizer/visualizer.py", line 55, in getExchangeMailBox for item in account.inbox.all().order_by("-datetime_received")[:100]: File "/home/adm-mfrau/visualizer/.venv/lib/python3.10/site-packages/cached_property.py", line 67, in __get__ return obj_dict.setdefault(name, self.func(obj)) File "/home/adm-mfrau/visualizer/.venv/lib/python3.10/site-packages/exchangelib/account.py", line 286, in inbox return self.root.get_default_folder(Inbox) File "/home/adm-mfrau/visualizer/.venv/lib/python3.10/site-packages/cached_property.py", line 67, in __get__ return obj_dict.setdefault(name, self.func(obj)) File "/home/adm-mfrau/visualizer/.venv/lib/python3.10/site-packages/exchangelib/account.py", line 350, in root return Root.get_distinguished(account=self) File "/home/adm-mfrau/visualizer/.venv/lib/python3.10/site-packages/exchangelib/folders/roots.py", line 145, in get_distinguished return cls._get_distinguished( File "/home/adm-mfrau/visualizer/.venv/lib/python3.10/site-packages/exchangelib/folders/base.py", line 226, in _get_distinguished return cls.resolve(account=folder.account, folder=folder) File "/home/adm-mfrau/visualizer/.venv/lib/python3.10/site-packages/exchangelib/folders/base.py", line 530, in resolve folders = list(FolderCollection(account=account, folders=[folder]).resolve()) File "/home/adm-mfrau/visualizer/.venv/lib/python3.10/site-packages/exchangelib/folders/collections.py", line 335, in resolve yield from self.__class__(account=self.account, folders=resolveable_folders).get_folders( File "/home/adm-mfrau/visualizer/.venv/lib/python3.10/site-packages/exchangelib/folders/collections.py", line 403, in get_folders yield from GetFolder(account=self.account).call( File "/home/adm-mfrau/visualizer/.venv/lib/python3.10/site-packages/exchangelib/services/get_folder.py", line 51, in _elems_to_objs for folder, elem in zip(self.folders, elems): File "/home/adm-mfrau/visualizer/.venv/lib/python3.10/site-packages/exchangelib/services/common.py", line 279, in _chunked_get_elements yield from self._get_elements(payload=payload_func(chunk, **kwargs)) File "/home/adm-mfrau/visualizer/.venv/lib/python3.10/site-packages/exchangelib/services/common.py", line 300, in _get_elements yield from self._response_generator(payload=payload) File "/home/adm-mfrau/visualizer/.venv/lib/python3.10/site-packages/exchangelib/services/common.py", line 263, in _response_generator response = self._get_response_xml(payload=payload) File "/home/adm-mfrau/visualizer/.venv/lib/python3.10/site-packages/exchangelib/services/common.py", line 396, in _get_response_xml r = self._get_response(payload=payload, api_version=api_version) File "/home/adm-mfrau/visualizer/.venv/lib/python3.10/site-packages/exchangelib/services/common.py", line 347, in _get_response r, session = post_ratelimited( File "/home/adm-mfrau/visualizer/.venv/lib/python3.10/site-packages/exchangelib/util.py", line 866, in post_ratelimited protocol.retry_policy.raise_response_errors(r) File "/home/adm-mfrau/visualizer/.venv/lib/python3.10/site-packages/exchangelib/protocol.py", line 738, in raise_response_errors raise MalformedResponseError( exchangelib.errors.MalformedResponseError: Unknown failure in response. Code: 403 headers: {'Cache-Control': 'private', 'Content-Length': '0', 'Content-Type': 'text/xml; charset=utf-8', 'Server': 'Microsoft-HTTPAPI/2.0', 'X-BEServer': 'MI2P293MB0038', 'X-NanoProxy': '1', 'X-AspNet-Version': '4.0.30319', 'Request-Id': '91119436-71b9-dd61-0833-cb1ba8e3e1be', 'X-CalculatedBETarget': 'MI2P293MB0038.ITAP293.PROD.OUTLOOK.COM', 'Restrict-Access-Confirm': '1', 'X-BeSku': 'WCS7', 'X-BackEndHttpStatus': '403', 'X-DiagInfo': 'MI2P293MB0038', 'x-ms-appId': '79909861-20d8-4e21-8d3a-8e79e9baee06', 'X-Proxy-BackendServerStatus': '403', 'X-Proxy-RoutingCorrectness': '1', 'X-RUM-NotUpdateQueriedPath': '1', 'X-RUM-NotUpdateQueriedDbCopy': '1', 'X-RUM-Validated': '1', 'X-UserType': 'Business', 'X-FEServer': 'ZR2P278CA0072', 'X-FirstHopCafeEFZ': 'ZRH', 'Strict-Transport-Security': 'max-age=31536000; includeSubDomains', 'MS-CV': 'NpQRkblxYd0IM8sbqOPhvg.1', 'Set-Cookie': 'exchangecookie=afb09f614196456b8d7b6fa8c4862b87; expires=Thu, 05-Mar-2026 17:15:57 GMT; path=/; secure; HttpOnly', 'WWW-Authenticate': 'Bearer client_id="00000002-0000-0ff1-ce00-000000000000", trusted_issuers="00000001-0000-0000-c000-000000000000@*", token_types="app_asserted_user_v1 service_asserted_app_v1", error="invalid_token"', 'Date': 'Wed, 05 Mar 2025 17:15:57 GMT'} content: INFO:__main__:Finished
无法定位token_types="app_asserted_user_v1 service_asserted_app_v1", error="invalid_token"的问题原因,已在Entra ID应用中完成配置。
问题排查与解决方法
1. 权限类型不匹配
你配置的EWS.AccessAsUser.All是委托权限,但当前用的是客户端凭证(client secret)的应用权限模式,需要配置应用权限的EWS.AccessAsApp:
- 登录Entra门户,找到目标应用,进入「API权限」
- 添加Exchange Web Services的应用权限
EWS.AccessAsApp,并完成管理员同意
2. 身份认证参数错误
客户端凭证流不需要identity参数,且access_type设置有误:
- 移除
OAuth2Credentials中的identity=username参数 - 将
Account初始化的access_type改为DELEGATE(应用权限模式下无需模拟用户)
3. Exchange版本适配问题
指定的Build(15, 0, 12, 34)版本过旧,不适用于Office365:
- 改用
Version.latest()自动适配最新版本,或明确指定兼容Office365的版本(如Version(build=Build(15, 20, 5447, 10)))
4. 修改后的示例代码
def getExchangeMailBox(): tenant_id = '<mytenantid>' client_id = '<myclientid>' secret_value = '<mysecret>' # 使用Office365兼容的最新版本 version = Version.latest() # 客户端凭证流无需identity参数 credentials = OAuth2Credentials( client_id=client_id, client_secret=secret_value, tenant_id=tenant_id ) config = Configuration( service_endpoint='https://outlook.office365.com/EWS/Exchange.asmx', credentials=credentials, auth_type=OAUTH2, version=version ) # 替换为共享邮箱的SMTP地址 account = Account( primary_smtp_address='shared_mailbox@domain.com', credentials=credentials, autodiscover=False, config=config, access_type=DELEGATE ) for item in account.inbox.all().order_by("-datetime_received")[:100]: logger.info(f"主题: {item.subject}, 发件人: {item.sender}, 接收时间: {item.datetime_received}")
内容的提问来源于stack exchange,提问作者Marco Frau
相关产品推荐
相关产品推荐

