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

Python调用MS Graph API获取Teams会议信息的认证与权限问题

解决方案:修复MSAL认证流程并配置正确权限

一、修复acquire_token_by_auth_code_flow认证问题

你当前的核心错误是直接将initiate_auth_code_flow的结果传入acquire_token_by_auth_code_flow,但该方法需要的是用户完成授权后,Azure AD重定向回指定redirect_uri时携带的code和state参数字典。以下是完整的修正流程及代码:

修正后的认证代码

import requests
import webbrowser
from msal import ConfidentialClientApplication
from http.server import HTTPServer, BaseHTTPRequestHandler
import urllib.parse

# 全局变量
app_id = '你的应用ID'
tenant_id = '你的租户ID'
client_secret = '你的客户端密钥'
# 修复authority URL格式错误(原代码占位符解析失败)
auth_url = f'https://login.microsoftonline.com/{tenant_id}/'
redirect_uri = 'http://localhost:5000/getAToken'

# 只保留必要权限,缩减权限范围
scopes = [
    'User.Read',
    'Calendars.Read',
    'OnlineMeetings.Read'
]

# 存储回调返回的授权码和状态
auth_code = None
auth_state = None

# 临时HTTP服务器,用于捕获Azure AD的回调参数
class CallbackHandler(BaseHTTPRequestHandler):
    def do_GET(self):
        global auth_code, auth_state
        query_params = urllib.parse.parse_qs(urllib.parse.urlparse(self.path).query)
        if 'code' in query_params and 'state' in query_params:
            auth_code = query_params['code'][0]
            auth_state = query_params['state'][0]
            self.send_response(200)
            self.send_header('Content-type', 'text/html')
            self.end_headers()
            self.wfile.write(b'授权成功!请返回脚本继续执行。')
        else:
            self.send_response(400)
            self.end_headers()
            self.wfile.write(b'授权失败:缺少必要参数。')

# 初始化MSAL客户端
client_instance = ConfidentialClientApplication(
    client_id=app_id,
    client_credential=client_secret,
    authority=auth_url,
)

# 1. 生成授权码流程
auth_code_flow = client_instance.initiate_auth_code_flow(scopes=scopes, redirect_uri=redirect_uri)
saved_state = auth_code_flow['state']

# 2. 打开浏览器引导用户完成登录授权
webbrowser.open(auth_code_flow['auth_uri'])

# 3. 启动临时服务器捕获回调
print("等待用户授权...请在浏览器中完成登录")
server = HTTPServer(('localhost', 5000), CallbackHandler)
server.handle_request()  # 仅处理一次回调请求

# 4. 验证状态并获取令牌
if auth_code and auth_state == saved_state:
    try:
        response_token = client_instance.acquire_token_by_auth_code_flow(
            auth_code_flow,
            {'code': auth_code, 'state': auth_state}
        )
        if 'access_token' in response_token:
            access_token_id = response_token['access_token']
            print("令牌获取成功!")
        else:
            print(f"获取令牌失败:{response_token.get('error_description', '未知错误')}")
    except Exception as e:
        print(f"认证错误:{str(e)}")
else:
    print("授权码无效或状态不匹配")

二、解决权限与查询问题

1. 权限配置修正

无需申请User.Read.All、Directory.Read.All等冗余权限,仅需以下委派权限(需完成管理员同意,若仅访问自己的会议则用户同意即可):

  • Calendars.Read:读取日历事件
  • OnlineMeetings.Read:读取在线会议详情

2. 会议查询逻辑优化

JoinWebUrl字段不支持OData过滤,正确做法是从日历事件中直接提取onlineMeeting.id,再用该ID查询会议详情:

# 获取会议详情并整理数据
base_url = 'https://graph.microsoft.com/v1.0/'
member_id = 'mariam@wy3j6.onmicrosoft.com'
meetings = []

headers = {'Authorization': f'Bearer {access_token_id}'}
# 查询用户日历事件
events_endpoint = base_url + f'users/{member_id}/events?$select=isOnlineMeeting,onlineMeeting,attendees,start,end'
response = requests.get(events_endpoint, headers=headers)

if response.ok:
    for event in response.json()['value']:
        if event.get('isOnlineMeeting') and event.get('onlineMeeting'):
            meeting_id = event['onlineMeeting']['id']
            # 查询会议详情
            meeting_detail_endpoint = base_url + f'me/onlineMeetings/{meeting_id}'
            meeting_response = requests.get(meeting_detail_endpoint, headers=headers)
            if meeting_response.ok:
                meeting_data = meeting_response.json()
                event_record = {
                    '会议标题': meeting_data['subject'],
                    '会议链接': meeting_data['joinUrl'],
                    '开始时间': event['start'],
                    '结束时间': event['end'],
                    '参会者': [att['emailAddress']['address'] for att in event['attendees']]
                }
                meetings.append(event_record)

3. 保存为CSV文件

使用Python内置csv模块将数据写入文件:

import csv

with open('teams_会议详情.csv', 'w', newline='', encoding='utf-8') as f:
    writer = csv.DictWriter(f, fieldnames=['会议标题', '会议链接', '开始时间', '结束时间', '参会者'])
    writer.writeheader()
    for meeting in meetings:
        # 将参会者列表转为分号分隔的字符串
        meeting['参会者'] = ';'.join(meeting['参会者'])
        writer.writerow(meeting)

关键注意事项

  • 确保Azure AD应用中配置的redirect_uri与代码中完全一致(包括端口和路径)
  • 若仅需访问自己的会议,可将users/{member_id}/events替换为me/events,无需管理员同意
  • 授权码流程必须经过用户手动登录授权,无法跳过该步骤

内容的提问来源于stack exchange,提问作者Mariam Abd Eltwab

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.13 11:15:36