使用Graph API向Teams频道发消息遇401权限问题求助
问题描述
我尝试使用Microsoft Graph API向Teams频道发送消息,已在Microsoft Azure中注册应用,配置了委派权限ChannelMessage.Send和应用权限Teamwork.Migrate.All。通过客户端凭证模式成功获取应用令牌,但调用发送消息接口时返回401未授权错误,提示仅导入场景允许应用上下文POST消息。
相关代码
获取令牌代码
def get_access_token(): url =f"https://login.microsoftonline.com/{tenant_id}/oauth2/v2.0/token" payload = { "grant_type": "client_credentials", "client_id": client_id, "client_secret": client_secret, "scope": "https://graph.microsoft.com/.default" } response = requests.post(url, data=payload) response_data = response.json() print(response_data) access_token = response_data['access_token'] return access_token
发送消息代码
def send_message(): endpoint_url = f"https://graph.microsoft.com/v1.0/teams/{team_id}/channels/{channel_id}/messages" access_token = get_access_token() headers = { 'Authorization': f'Bearer {access_token}', 'Content-Type': 'application/json' } request_body= { "body": { "content": "Hello world" } } response = requests.post(endpoint_url, headers=headers, data=json.dumps(request_body)) print(response.status_code) print(response.json())
错误响应
401 {'error': {'code': 'Unauthorized', 'message': 'Message POST is allowed in application-only context only for import purposes. Refer to https://docs.microsoft.com/microsoftteams/platform/graph-api/import-messages/import-external-messages-to-teams for more details.', 'innerError': {'date': '2024-09-12T13:57:54', 'request-id': '60bd5cfb-xxxx-4bea-ba58-xxxxxxxxx', 'client-request-id': '60bd5cfb-xxxx-4bea-ba58-xxxxxxxx'}}}
问题原因
- 权限与模式不匹配:
ChannelMessage.Send是委派权限,需要用户参与的授权模式(如授权码模式)才能生效,无法在客户端凭证(应用权限)模式下使用。 - 应用权限的限制:客户端凭证模式下,
/teams/{teamId}/channels/{channelId}/messages接口仅支持导入历史消息的场景,需配合Teamwork.Migrate.All权限和特定请求参数,不能直接发送普通实时消息。
解决方案
方案1:切换到授权码模式发送普通消息
如果要发送实时普通消息,需改用用户授权的授权码模式,利用ChannelMessage.Send委派权限:
- 确保Azure应用中
ChannelMessage.Send委派权限已获得管理员同意。 - 修改令牌获取逻辑为授权码模式(需先引导用户完成登录授权,获取授权码):
def get_access_token_auth_code(): # 注意:实际需先引导用户访问授权地址获取code,此处仅展示令牌交换步骤 url = f"https://login.microsoftonline.com/{tenant_id}/oauth2/v2.0/token" payload = { "grant_type": "authorization_code", "client_id": client_id, "client_secret": client_secret, "code": "用户授权后返回的授权码", "redirect_uri": "你的应用注册时配置的重定向URI", "scope": "ChannelMessage.Send" } response = requests.post(url, data=payload) response_data = response.json() access_token = response_data['access_token'] return access_token
- 发送消息的接口和请求体保持原代码不变,使用上述方法获取的令牌即可正常发送。
方案2:使用应用权限导入历史消息
如果你的需求是导入历史消息,需在请求体中添加导入场景必填的字段,配合Teamwork.Migrate.All权限使用:
def send_import_message(): endpoint_url = f"https://graph.microsoft.com/v1.0/teams/{team_id}/channels/{channel_id}/messages" access_token = get_access_token() headers = { 'Authorization': f'Bearer {access_token}', 'Content-Type': 'application/json' } request_body= { "body": { "content": "Hello world" }, # 导入场景必须添加的字段 "createdDateTime": "2024-09-10T10:00:00Z", # 需为过去的时间 "from": { "user": { "id": "消息发送者的用户ID", "displayName": "发送者名称" } } } response = requests.post(endpoint_url, headers=headers, json=request_body) print(response.status_code) print(response.json())
注意:导入的消息会被标记为历史消息,不会触发Teams实时通知,且createdDateTime必须设置为消息实际产生的过去时间。
内容的提问来源于stack exchange,提问作者CloudEnthusiast
相关产品推荐
相关产品推荐

