单租户团队Bot身份验证错误:缺少access_token

0 投票
1 回答
38 浏览
提问于 2025-04-12 19:31

我正在用 FastAPIbotbuilder-core 库开发一个 Teams 机器人。我想把这个机器人限制在我公司内部使用,保持它是一个单租户的应用,以保护机密信息。不过,我遇到了一个问题:当机器人尝试发送消息时,出现了与 access_token 相关的错误。

以下是相关的代码片段:

CONFIG = DefaultConfig()

# BotFramework Adapter setup
ADAPTER = CloudAdapter(ConfigurationBotFrameworkAuthentication(CONFIG))

# Teams Bot
BOT = TeamsBot()

@app.post("/api/messages")
@app.options("/api/messages")
async def messages(req: Request) -> Response:
    # Main bot message handler.
    if "application/json" in req.headers["Content-Type"]:
        body = await req.json()
    else:
        return Response(status_code=HTTPStatus.UNSUPPORTED_MEDIA_TYPE)

    activity = Activity().deserialize(body)
    if not isinstance(activity, Activity):
        print(f"Error: Expected Activity, got {type(activity)} instead.")
        return Response(status_code=400)
    auth_header = req.headers["Authorization"] if "Authorization" in req.headers else ""

    response = await ADAPTER.process_activity(auth_header, activity, BOT.on_turn)
    if response:
        return json_response(data=response.body, status=response.status)
    return Response(status_code=201)

错误追踪信息:

Traceback (most recent call last):
  File "C:\Users\X(Aspe\PycharmProjects\TeamsBotProject_Main\venv\Lib\site-packages\uvicorn\protocols\http\h11_impl.py", line 408, in run_asgi
    result = await app(  # type: ignore[func-returns-value]
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\X(Aspe\PycharmProjects\TeamsBotProject_Main\venv\Lib\site-packages\uvicorn\middleware\proxy_headers.py", line 84, in __call__
    return await self.app(scope, receive, send)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\X(Aspe\PycharmProjects\TeamsBotProject_Main\venv\Lib\site-packages\fastapi\applications.py", line 292, in __call__
    await super().__call__(scope, receive, send)
  File "C:\Users\X(Aspe\PycharmProjects\TeamsBotProject_Main\venv\Lib\site-packages\starlette\applications.py", line 122, in __call__
    await self.middleware_stack(scope, receive, send)
  File "C:\Users\X(Aspe\PycharmProjects\TeamsBotProject_Main\venv\Lib\site-packages\starlette\middleware\errors.py", line 184, in __call__
    raise exc
  File "C:\Users\X(Aspe\PycharmProjects\TeamsBotProject_Main\venv\Lib\site-packages\starlette\middleware\errors.py", line 162, in __call__
    await self.app(scope, receive, _send)
  File "C:\Users\X(Aspe\PycharmProjects\TeamsBotProject_Main\venv\Lib\site-packages\starlette\middleware\sessions.py", line 86, in __call__
    await self.app(scope, receive, send_wrapper)
  File "C:\Users\X(Aspe\PycharmProjects\TeamsBotProject_Main\venv\Lib\site-packages\starlette\middleware\exceptions.py", line 79, in __call__
    raise exc
  File "C:\Users\X(Aspe\PycharmProjects\TeamsBotProject_Main\venv\Lib\site-packages\starlette\middleware\exceptions.py", line 68, in __call__
    await self.app(scope, receive, sender)
  File "C:\Users\X(Aspe\PycharmProjects\TeamsBotProject_Main\venv\Lib\site-packages\fastapi\middleware\asyncexitstack.py", line 20, in __call__
    raise e
  File "C:\Users\X(Aspe\PycharmProjects\TeamsBotProject_Main\venv\Lib\site-packages\fastapi\middleware\asyncexitstack.py", line 17, in __call__
    await self.app(scope, receive, send)
  File "C:\Users\X(Aspe\PycharmProjects\TeamsBotProject_Main\venv\Lib\site-packages\starlette\routing.py", line 718, in __call__
    await route.handle(scope, receive, send)
  File "C:\Users\X(Aspe\PycharmProjects\TeamsBotProject_Main\venv\Lib\site-packages\starlette\routing.py", line 276, in handle
    await self.app(scope, receive, send)
  File "C:\Users\X(Aspe\PycharmProjects\TeamsBotProject_Main\venv\Lib\site-packages\starlette\routing.py", line 66, in app
    response = await func(request)
               ^^^^^^^^^^^^^^^^^^^
  File "C:\Users\X(Aspe\PycharmProjects\TeamsBotProject_Main\venv\Lib\site-packages\fastapi\routing.py", line 273, in app
    raw_response = await run_endpoint_function(
                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\X(Aspe\PycharmProjects\TeamsBotProject_Main\venv\Lib\site-packages\fastapi\routing.py", line 190, in run_endpoint_function
    return await dependant.call(**values)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\X(Aspe\PycharmProjects\TeamsBotProject\_api\fast_api.py", line 113, in messages
    return Response(status_code=201)
               ^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\X(Aspe\PycharmProjects\TeamsBotProject_Main\venv\Lib\site-packages\botbuilder\core\cloud_adapter_base.py", line 364, in process_activity
    await self.run_pipeline(context, logic)
  File "C:\Users\X(Aspe\PycharmProjects\TeamsBotProject_Main\venv\Lib\site-packages\botbuilder\core\bot_adapter.py", line 181, in run_pipeline
    raise error
  File "C:\Users\X(Aspe\PycharmProjects\TeamsBotProject_Main\venv\Lib\site-packages\botbuilder\core\bot_adapter.py", line 174, in run_pipeline
    return await self._middleware.receive_activity_with_status(
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\X(Aspe\PycharmProjects\TeamsBotProject_Main\venv\Lib\site-packages\botbuilder\core\middleware_set.py", line 69, in receive_activity_with_status
    return await self.receive_activity_internal(context, callback)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\X(Aspe\PycharmProjects\TeamsBotProject_Main\venv\Lib\site-packages\botbuilder\core\middleware_set.py", line 79, in receive_activity_internal
    return await callback(context)
           ^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\X(Aspe\PycharmProjects\TeamsBotProject\services\ProjectName\teams_bot\bot.py", line 69, in on_turn
    await super().on_turn(turn_context)
  File "C:\Users\X(Aspe\PycharmProjects\TeamsBotProject_Main\venv\Lib\site-packages\botbuilder\core\activity_handler.py", line 70, in on_turn
    await self.on_message_activity(turn_context)
  File "C:\Users\X(Aspe\PycharmProjects\TeamsBotProject\services\ProjectName\teams_bot\bot.py", line 27, in on_message_activity
    await self.get_template_quote(turn_context)
  File "C:\Users\X(Aspe\PycharmProjects\TeamsBotProject\services\ProjectName\teams_bot\bot.py", line 223, in get_template_quote
    await self._send_file_card(turn_context, filename, file_size)
  File "C:\Users\X(Aspe\PycharmProjects\TeamsBotProject\services\ProjectName\teams_bot\bot.py", line 250, in _send_file_card
    await turn_context.send_activity(reply_activity)
  File "C:\Users\X(Aspe\PycharmProjects\TeamsBotProject_Main\venv\Lib\site-packages\botbuilder\core\turn_context.py", line 173, in send_activity
    result = await self.send_activities([activity_or_text])
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\X(Aspe\PycharmProjects\TeamsBotProject_Main\venv\Lib\site-packages\botbuilder\core\turn_context.py", line 225, in send_activities
    return await self._emit(self._on_send_activities, output, logic())
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\X(Aspe\PycharmProjects\TeamsBotProject_Main\venv\Lib\site-packages\botbuilder\core\turn_context.py", line 303, in _emit
    return await logic
           ^^^^^^^^^^^
  File "C:\Users\X(Aspe\PycharmProjects\TeamsBotProject_Main\venv\Lib\site-packages\botbuilder\core\turn_context.py", line 220, in logic
    responses = await self.adapter.send_activities(self, output)
                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\X(Aspe\PycharmProjects\TeamsBotProject_Main\venv\Lib\site-packages\botbuilder\core\cloud_adapter_base.py", line 93, in send_activities
    response = await connector_client.conversations.reply_to_activity(
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\X(Aspe\PycharmProjects\TeamsBotProject_Main\venv\Lib\site-packages\botframework\connector\aio\operations_async\_conversations_operations_async.py", line 523, in reply_to_activity
    response = await self._client.async_send(
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\X(Aspe\PycharmProjects\TeamsBotProject_Main\venv\Lib\site-packages\msrest\async_client.py", line 115, in async_send
    pipeline_response = await self.config.pipeline.run(request, **kwargs)
                        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\X(Aspe\PycharmProjects\TeamsBotProject_Main\venv\Lib\site-packages\msrest\pipeline\async_abc.py", line 159, in run
    return await first_node.send(pipeline_request, **kwargs)  # type: ignore
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\X(Aspe\PycharmProjects\TeamsBotProject_Main\venv\Lib\site-packages\msrest\pipeline\async_abc.py", line 79, in send
    response = await self.next.send(request, **kwargs)  # type: ignore
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\X(Aspe\PycharmProjects\TeamsBotProject_Main\venv\Lib\site-packages\msrest\pipeline\async_requests.py", line 99, in send
    self._creds.signed_session(session)
  File "C:\Users\X(Aspe\PycharmProjects\TeamsBotProject_Main\venv\Lib\site-packages\botframework\connector\auth\app_credentials.py", line 98, in signed_session
    auth_token = self.get_access_token()
                 ^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\X(Aspe\PycharmProjects\TeamsBotProject_Main\venv\Lib\site-packages\botframework\connector\auth\microsoft_app_credentials.py", line 65, in get_access_token
    return auth_token["access_token"]
           ~~~~~~~~~~^^^^^^^^^^^^^^^^
KeyError: 'access_token'

错误信息:

{'error': 'unauthorized_client', 'error_description': "AADSTS700016: Application with identifier 'AppID' was not found in the directory 'Bot Framework'. This can happen if the application has not been installed by the administrator of the tenant or consented to by any user in the tenant. You may have sent your authentication request to the wrong tenant. Trace ID: 00 Correlation ID: 00 Timestamp: 2024-03-25 20:35:48Z", 'error_codes': [700016], 'timestamp': '2024-03-25 20:35:48Z', 'trace_id': '00', 'correlation_id': '00', 'error_uri': 'https://login.microsoftonline.com/error?code=700016'}

在上面的代码中,我只把应用 ID 和应用密钥传递给了 ConfigurationBotFrameworkAuthentication 实例。我注意到,当我切换到多租户模式时,错误就消失了,但我想保持这个机器人是单租户的应用。

我觉得我可能需要传递一些额外的信息来进行正确的验证,或者在 Azure 中更改一些设置。在我的项目中,在 "Authentication" 标签下,我选择了 "Single Tenant",

这里输入图片描述

但在机器人的 "Configuration" 标签下,它显示的是 "MultiTenant"。我不确定这个不一致是否导致了问题。

这里输入图片描述

我正在使用 FastAPI 运行这个机器人,/api/messages 端点负责处理 Teams 消息。我的目标是让我公司内部的用户能够自由地将这个机器人添加到他们的集合中并使用它。

我观察到,当我发送消息时,并没有关于 access_token 的错误,但显示机器人尝试发送消息却失败了。

我希望能得到一些指导,关于如何正确配置一个单租户的 Teams 机器人,使用 FastAPIbotbuilder-core 库。我是否需要传递额外的信息,或者在 Azure 中修改任何设置来解决这个问题?

1 个回答

1

正如上面的评论所说,虽然这听起来有点不直观,但你需要为应用程序启用多租户功能,这样机器人才能正常工作。不过,用户发送的实际消息中会包含一个租户ID,这样你就可以根据这个ID进行过滤,阻止其他消息。有一种方法是使用中间件,虽然这里的示例是用C#写的,而不是Python,但希望它能给你一个好的起点:https://github.com/OfficeDev/BotBuilder-MicrosoftTeams-dotnet/blob/master/CSharp/Microsoft.Bot.Builder.Teams/Middlewares/TeamsTenantFilteringMiddleware.cs。你也可以在你的处理程序中,或者甚至在机器人本身的OnTurn或OnMessage事件处理程序中实现这个功能。

这样做确实能提供一定的保护,因为只有Bot Framework服务知道你机器人的端点地址,这个地址不会被泄露给最终用户。因此,如果有任何东西到达你的机器人,你可以在中间件、处理程序或类似的地方进行过滤。不过,如果你想要更高的安全性,确保即使恶意用户找到了并调用了你机器人的网络端点也能被阻止,那么你可以考虑实现单点登录(SSO)。这样你就可以确保(a)有一个有效的令牌,以及(b)这个令牌100%来自正确的租户。你可以在这里了解更多信息:https://learn.microsoft.com/en-us/microsoftteams/platform/bots/how-to/authentication/bot-sso-overview

撰写回答