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
相关产品推荐
相关产品推荐

