Teams Bot throws Unauthorized error when attempting to send message back

ncv :

I am attempting to build a simple MS Teams bot using the Python bot framework SDK. When testing my bot locally using the emulator, everything works fine. I registered the bot using the legacy portal here https://dev.botframework.com/bots as I do not want to create an Azure subscription.

I added the app id and app secret to the bot and deployed it on an EC2 machine using API Gateway (with HTTP proxy integration) to get an HTTPS url for the messaging endpoint.

When deployed, the code is able to receive and parse messages from both the testing functionality on the dev framework page and from an actual deployed app on Teams. However, when attempting to respond to the message, I get an Unauthorized error message.

Here is the stack trace:

[on_turn_error] unhandled error: Operation returned an invalid status code 'Unauthorized'
Traceback (most recent call last):
  File "/usr/local/lib/python3.7/site-packages/botbuilder/core/bot_adapter.py", line 103, in run_pipeline
    context, callback
  File "/usr/local/lib/python3.7/site-packages/botbuilder/core/middleware_set.py", line 69, in receive_activity_with_status
    return await self.receive_activity_internal(context, callback)
  File "/usr/local/lib/python3.7/site-packages/botbuilder/core/middleware_set.py", line 79, in receive_activity_internal
    return await callback(context)
  File "/usr/local/lib/python3.7/site-packages/botbuilder/core/activity_handler.py", line 28, in on_turn
    await self.on_message_activity(turn_context)
  File "/home/ec2-user/bot/bot.py", line 24, in on_message_activity
    return await turn_context.send_activity(response)
  File "/usr/local/lib/python3.7/site-packages/botbuilder/core/turn_context.py", line 165, in send_activity
    result = await self.send_activities([activity_or_text])
  File "/usr/local/lib/python3.7/site-packages/botbuilder/core/turn_context.py", line 198, in send_activities
    return await self._emit(self._on_send_activities, output, logic())
  File "/usr/local/lib/python3.7/site-packages/botbuilder/core/turn_context.py", line 276, in _emit
    return await logic
  File "/usr/local/lib/python3.7/site-packages/botbuilder/core/turn_context.py", line 193, in logic
    responses = await self.adapter.send_activities(self, output)
  File "/usr/local/lib/python3.7/site-packages/botbuilder/core/bot_framework_adapter.py", line 444, in send_activities
    raise error
  File "/usr/local/lib/python3.7/site-packages/botbuilder/core/bot_framework_adapter.py", line 431, in send_activities
    activity.conversation.id, activity.reply_to_id, activity
  File "/usr/local/lib/python3.7/site-packages/botframework/connector/aio/operations_async/_conversations_operations_async.py", line 533, in reply_to_activity
    raise models.ErrorResponseException(self._deserialize, response)
botbuilder.schema._models_py3.ErrorResponseException: Operation returned an invalid status code 'Unauthorized'

My app code is:

CONFIG = DefaultConfig()
SETTINGS = BotFrameworkAdapterSettings(CONFIG.APP_ID, CONFIG.APP_PASSWORD,
                                       CONFIG.APP_AUTH_TENANT, CONFIG.APP_OAUTH_ENDPOINT)
ADAPTER = BotFrameworkAdapter(SETTINGS)


# Catch-all for errors.
async def on_error(context: TurnContext, error: Exception):
    print(f"\n [on_turn_error] unhandled error: {error}", file=sys.stderr)
    traceback.print_exc()

    # Send a message to the user
    await context.send_activity("The bot encountered an error or bug.")
    await context.send_activity("To continue to run this bot, please fix the bot source code.")
    # Send a trace activity if we're talking to the Bot Framework Emulator
    if context.activity.channel_id == "emulator":
        # Create a trace activity that contains the error object
        trace_activity = Activity(
            label="TurnError",
            name="on_turn_error Trace",
            timestamp=datetime.utcnow(),
            type=ActivityTypes.trace,
            value=f"{error}",
            value_type="https://www.botframework.com/schemas/error",
        )
        # Send a trace activity, which will be displayed in Bot Framework Emulator
        await context.send_activity(trace_activity)


ADAPTER.on_turn_error = on_error
APP_ID = SETTINGS.app_id
dynamodb = boto3.resource("dynamodb")
CONVERSATION_REFERENCES = dynamodb.Table("ConversationReferences")

# Create the Bot
BOT = MyBot(CONVERSATION_REFERENCES)


# Listen for incoming requests on /api/messages
async def messages(req):
    print(f"Message Received - {str(datetime.now())}")
    json_request = await req.json()
    print(f"Request Body: {json_request}")
    activity = Activity().deserialize(json_request)
    print("Request successfully deserialized")
    auth_header = req.headers["Authorization"] if "Authorization" in req.headers else ""
    try:
        print("Sending activity to adapter")
        response = await ADAPTER.process_activity(activity, auth_header, BOT.on_turn)
        if response:
            return Response(status=response.status, text=response.body)
        return Response(status=201)
    except Exception as exception:
        raise exception

async def health(req):
    return Response(status=200, text="Working")

APP = web.Application(middlewares=[aiohttp_error_middleware])
APP.router.add_post("/api/messages", messages)
APP.router.add_get("/health", health)

if __name__ == "__main__":
    web.run_app(APP)

And my bot code is:

class MyBot(ActivityHandler):

    def __init__(self, conversation_references):
        self.conversation_references = conversation_references

    async def on_message_activity(self, turn_context: TurnContext):
        print("Message received by bot adapter")
        # The next two lines also cause an unauthorized error. I commented them out to try and simplify
        # team_details = await teams.TeamsInfo.get_members(turn_context)
        # user = team_details[1].email
        user = "[email protected]"
        conversation = self.conversation_references.get_item(Key={"user": user})
        if "Item" in conversation:
            response = "You are already registered"
        else:
            conversation = TurnContext.get_conversation_reference(turn_context.activity)
            item = {"user": user, "conversation": conversation.as_dict()}
            self.conversation_references.put_item(Item=item)
            response = "You have been successfully registered!"
        return await turn_context.send_activity(response)

Update: When testing locally, I did not add the app id and password to the emulator. When I do, in debug mode I get the following error message: "An error occurred while POSTing "/INSPECT open" command to conversation xxxxxx|livechat: 400: The bot's Microsoft App ID or Microsoft App Password is incorrect."

I am 100% sure the id and password are correct, though, as I've manually used the token endpoint from the registration page to get an access token with those credentials. It may be related to the fact that in the code and using the endpoint manually I can specify the Directory (tenant) ID, while I cannot do so using the emulator.

Another strange point is that when the emulator returns that response it doesn't actually seem to be making a request to my local endpoint, so I'm not sure where the 400 response is even coming from.

Kyle Delaney :

You will need to make sure you use multi-tenant AAD app credentials, as described here.

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=34419&siteId=1