使用Microsoft Graph API获取Outlook日历事件时遇权限拒绝错误
解决Microsoft Graph API客户端凭证流访问日历时的ErrorAccessDenied错误
问题场景
使用客户端凭证流调用Microsoft Graph API的日历相关端点(Get Calendars、List Events、Get Schedule等)时,返回权限拒绝错误:
{'error': {'code': 'ErrorAccessDenied', 'message': 'Access is denied. Check credentials and try again.'}}
已完成以下配置:
- Azure AD应用注册,权限类型为Application
- 已授予
Calendars.Read、Calendars.ReadWrite、User.ReadAll权限并完成管理员同意 - 可成功生成Access Token,且能通过该Token读取用户信息,仅日历相关请求失败
排查与解决步骤
1. 确认目标用户日历的文件夹权限
客户端凭证流的应用权限依赖Exchange Online的日历文件夹权限设置。即使应用有Calendars.Read权限,若目标用户的日历未对组织开放读取权限,应用仍无法访问:
- 登录Exchange Admin Center,找到目标用户邮箱,进入权限 -> 日历权限,确认
默认用户的权限为审阅者或更高级别。 - 或用PowerShell命令检查:
Get-MailboxFolderPermission -Identity "<用户邮箱>:\Calendar"
2. 验证Access Token的权限范围
解码Access Token(使用本地JWT解码工具),检查roles字段是否包含Calendars.Read/Calendars.ReadWrite:
- 若缺失这些权限,需确认:
- 管理员同意操作已完成(在Azure AD门户的企业应用权限页面确认状态为"已授予")
- 获取Token时使用的范围为
https://graph.microsoft.com/.default(客户端凭证流必须使用该范围)
3. 检查目标用户的许可证状态
确认目标用户拥有Exchange Online(或包含日历功能的O365订阅)有效许可证,无许可证用户的日历无法通过Graph API访问。
4. 排查Exchange Online应用访问策略
部分租户会配置应用访问策略限制第三方应用访问邮箱数据:
- 登录Exchange Admin Center,进入权限 -> 应用访问策略,检查是否存在阻止该应用的规则。
5. 确认API请求端点格式
客户端凭证流无用户上下文,不能使用/me路径,必须指定具体用户ID(邮箱或对象ID):
正确端点示例:
GET https://graph.microsoft.com/v1.0/users/{user-id}/calendars GET https://graph.microsoft.com/v1.0/users/{user-id}/events
6. 检查Azure AD应用的权限状态
在Azure AD门户企业应用 -> 目标应用 -> 权限页面,确认Calendars.Read/Calendars.ReadWrite的权限状态为"已授予",而非"待同意"。
示例代码检查点
确保MSAL获取Token及API调用代码符合客户端凭证流要求:
from msal import ConfidentialClientApplication import requests client_id = "YOUR_CLIENT_ID" client_secret = "YOUR_CLIENT_SECRET" tenant_id = "YOUR_TENANT_ID" target_user_id = "TARGET_USER_EMAIL_OR_OBJECT_ID" # 初始化MSAL客户端 app = ConfidentialClientApplication( client_id, authority=f"https://login.microsoftonline.com/{tenant_id}", client_credential=client_secret ) # 获取客户端凭证流Token(必须使用.default范围) result = app.acquire_token_for_client(scopes=["https://graph.microsoft.com/.default"]) if "access_token" in result: # 调用日历API时指定目标用户ID response = requests.get( f"https://graph.microsoft.com/v1.0/users/{target_user_id}/calendars", headers={"Authorization": f"Bearer {result['access_token']}"} ) print(response.json())
内容的提问来源于stack exchange,提问作者Rathish Kumar B
相关产品推荐
相关产品推荐

