You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用Python和Graph API从Outlook发件遇权限拒绝问题求助

问题分析与解决方案

核心错误原因

你当前使用的acquire_token_for_client是客户端凭证授权流,该流程仅支持应用级权限,但你配置的是委托权限(且组织不允许应用级权限),两者不匹配导致权限拒绝错误。委托权限要求必须以具体用户身份完成认证,而非应用本身直接获取权限。

解决方案

根据你的场景(无人值守脚本、组织限制委托权限),提供两种可行的授权流方案:

方案1:用户名密码授权流(无MFA场景)

适用于用户账号未开启多因素认证的情况,直接通过用户名密码获取委托权限的token:

import msal
import requests
import json

# 替换为你的配置信息
TENANT_ID = 'Tenant ID'
AUTHORITY_URL = f'https://login.microsoftonline.com/{TENANT_ID}'
CLIENT_ID = 'Client ID'
USERNAME = "你的Outlook邮箱地址"
PASSWORD = "你的邮箱密码"
SCOPES = ["Mail.Send"]  # 指定具体的委托权限

# 初始化公共客户端应用(委托权限使用公共客户端)
app = msal.PublicClientApplication(
    client_id=CLIENT_ID,
    authority=AUTHORITY_URL
)

# 获取token
result = app.acquire_token_by_username_password(
    username=USERNAME,
    password=PASSWORD,
    scopes=SCOPES
)

if "access_token" in result:
    # 使用/me端点代表当前授权用户发送邮件
    endpoint = 'https://graph.microsoft.com/v1.0/me/sendMail'
    toUserEmail = "收件人邮箱地址"
    email_msg = {
        'Message': {
            'Subject': "脚本运行报错通知",
            'Body': {'ContentType': 'Text', 'Content': "报表数据收集脚本出现异常,请检查SharePoint文件状态。"},
            'ToRecipients': [{'EmailAddress': {'Address': toUserEmail}}]
        },
        'SaveToSentItems': 'true'
    }
    r = requests.post(
        endpoint,
        headers={'Authorization': 'Bearer ' + result['access_token']},
        json=email_msg
    )
    if r.ok:
        print('报错邮件发送成功')
    else:
        print(r.json())
else:
    print(f"获取token失败: {result.get('error')} - {result.get('error_description')}")

注意:

  • 请勿硬编码密码,建议通过环境变量或密钥管理工具存储。
  • 若账号开启MFA,此方案无法使用,需改用设备授权流。

方案2:设备授权流(支持MFA场景)

无需存储密码,首次运行需手动在浏览器完成身份验证(支持MFA),后续脚本可自动刷新token,适合长期无人值守运行:

import msal
import requests
import json

# 替换为你的配置信息
TENANT_ID = 'Tenant ID'
AUTHORITY_URL = f'https://login.microsoftonline.com/{TENANT_ID}'
CLIENT_ID = 'Client ID'
SCOPES = ["Mail.Send"]

# 初始化公共客户端应用
app = msal.PublicClientApplication(
    client_id=CLIENT_ID,
    authority=AUTHORITY_URL
)

# 启动设备授权流程
flow = app.initiate_device_flow(scopes=SCOPES)
if "user_code" not in flow:
    raise ValueError(f"设备流程初始化失败: {json.dumps(flow, indent=4)}")

# 输出验证提示
print(flow["message"])

# 轮询获取token
result = app.acquire_token_by_device_flow(flow)

if "access_token" in result:
    endpoint = 'https://graph.microsoft.com/v1.0/me/sendMail'
    toUserEmail = "收件人邮箱地址"
    email_msg = {
        'Message': {
            'Subject': "脚本运行报错通知",
            'Body': {'ContentType': 'Text', 'Content': "报表数据收集脚本出现异常,请检查SharePoint文件状态。"},
            'ToRecipients': [{'EmailAddress': {'Address': toUserEmail}}]
        },
        'SaveToSentItems': 'true'
    }
    r = requests.post(
        endpoint,
        headers={'Authorization': 'Bearer ' + result['access_token']},
        json=email_msg
    )
    if r.ok:
        print('报错邮件发送成功')
    else:
        print(r.json())
else:
    print(f"获取token失败: {result.get('error')} - {result.get('error_description')}")

注意:

  • 首次运行会输出一个验证链接和代码,需在浏览器打开链接并输入代码完成登录授权,MSAL会自动缓存token,后续运行无需再次验证。
  • 需在Entra ID应用注册中,将应用类型设置为移动和桌面应用,并添加重定向URI http://localhost(路径:应用注册 -> 身份验证 -> 添加平台 -> 移动和桌面应用)。

关于权限审批的问题

Mail.Send属于无需管理员同意的委托权限(除非组织修改了默认权限策略),使用上述两种委托权限流时,普通用户自身即可完成授权,不需要管理员审批。之前遇到的审批要求,可能是因为误用了需要管理员同意的权限或应用配置错误。

内容的提问来源于stack exchange,提问作者Merlin Nestler

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.09 15:30:57