botframeworkazure-bot-service

Microsoft Bot Framework app throws error "Unauthorized Access. Request is not authorized" when attempting to send message using "Test In Web Chat"


I am attempting to build a simple echo bot using the Python bot framework SDK. My bot works locally using the emulator without a problem. I used ngrok to create an https tunnel to my app then I registered the bot using azure bot service (bot type: multitenant). I added the app_id and secret from bot service to my app and when I test sending messages from bot service using "Test in Web Chat" feature, I get error "Unauthorized Access. Request is not authorized" on my app log.

I tested on emulator without credentials and it works, also I can get token successfully with the same app_id and password when I test using: "curl -k -X POST https://login.microsoftonline.com/botframework.com/oauth2/v2.0/token -d "grant_type=client_credentials&client_id=my_app_id_here&client_secret=my_app_secret_here&scope=https%3A%2F%2Fapi.botframework.com%2F.default"

But when I try to send a message from bot service's Test in web chat, I get "Unauthorized Access. Request is not authorized" on app.

Stack trace:

    [2024-05-23 22:27:15,827] ERROR in app: Exception on /api/messages [POST]
    Traceback (most recent call last):
    File "/home/metalmlover/dev/microsoft bot framework/BotTutorialSample/Python_tutorial/01-EchoBot/venv/lib/python3.8/site-packages/flask/app.py", line 1473, in wsgi_app
        response = self.full_dispatch_request()
    File "/home/metalmlover/dev/microsoft bot framework/BotTutorialSample/Python_tutorial/01-EchoBot/venv/lib/python3.8/site-packages/flask/app.py", line 882, in full_dispatch_request
        rv = self.handle_user_exception(e)
    File "/home/metalmlover/dev/microsoft bot framework/BotTutorialSample/Python_tutorial/01-EchoBot/venv/lib/python3.8/site-packages/flask/app.py", line 880, in full_dispatch_request
        rv = self.dispatch_request()
    File "/home/metalmlover/dev/microsoft bot framework/BotTutorialSample/Python_tutorial/01-EchoBot/venv/lib/python3.8/site-packages/flask/app.py", line 865, in dispatch_request
        return self.ensure_sync(self.view_functions[rule.endpoint])(**view_args)  # type: ignore[no-any-return]
    File "/home/metalmlover/dev/microsoft bot framework/BotTutorialSample/Python_tutorial/01-EchoBot/app.py", line 30, in messages
        loop.run_until_complete(task)
    File "/home/metalmlover/.pyenv/versions/3.8.16/lib/python3.8/asyncio/base_events.py", line 616, in run_until_complete
        return future.result()
    File "/home/metalmlover/dev/microsoft bot framework/BotTutorialSample/Python_tutorial/01-EchoBot/venv/lib/python3.8/site-packages/botbuilder/core/bot_framework_adapter.py", line 442, in process_activity
        identity = await self._authenticate_request(activity, auth_header)
    File "/home/metalmlover/dev/microsoft bot framework/BotTutorialSample/Python_tutorial/01-EchoBot/venv/lib/python3.8/site-packages/botbuilder/core/bot_framework_adapter.py", line 551, in _authenticate_request
        claims = await JwtTokenValidation.authenticate_request(
    File "/home/metalmlover/dev/microsoft bot framework/BotTutorialSample/Python_tutorial/01-EchoBot/venv/lib/python3.8/site-packages/botframework/connector/auth/jwt_token_validation.py", line 49, in authenticate_request
        raise PermissionError("Unauthorized Access. Request is not authorized")
    PermissionError: Unauthorized Access. Request is not authorized
    127.0.0.1 - - [23/May/2024 22:27:15] "POST /api/messages HTTP/1.1" 500 -
    127.0.0.1 - - [23/May/2024 22:27:25] "OPTIONS /api/messages HTTP/1.1" 200 -
    127.0.0.1 - - [23/May/2024 22:34:59] "OPTIONS /api/messages HTTP/1.1" 200 -
    127.0.0.1 - - [23/May/2024 22:35:04] "OPTIONS /api/messages HTTP/1.1" 200 -
    [2024-05-23 22:35:08,763] ERROR in app: Exception on /api/messages [POST]
    Traceback (most recent call last):
    File "/home/metalmlover/dev/microsoft bot framework/BotTutorialSample/Python_tutorial/01-EchoBot/venv/lib/python3.8/site-packages/flask/app.py", line 1473, in wsgi_app
        response = self.full_dispatch_request()
    File "/home/metalmlover/dev/microsoft bot framework/BotTutorialSample/Python_tutorial/01-EchoBot/venv/lib/python3.8/site-packages/flask/app.py", line 882, in full_dispatch_request
        rv = self.handle_user_exception(e)
    File "/home/metalmlover/dev/microsoft bot framework/BotTutorialSample/Python_tutorial/01-EchoBot/venv/lib/python3.8/site-packages/flask/app.py", line 880, in full_dispatch_request
        rv = self.dispatch_request()
    File "/home/metalmlover/dev/microsoft bot framework/BotTutorialSample/Python_tutorial/01-EchoBot/venv/lib/python3.8/site-packages/flask/app.py", line 865, in dispatch_request
        return self.ensure_sync(self.view_functions[rule.endpoint])(**view_args)  # type: ignore[no-any-return]
    File "/home/metalmlover/dev/microsoft bot framework/BotTutorialSample/Python_tutorial/01-EchoBot/app.py", line 30, in messages
        loop.run_until_complete(task)
    File "/home/metalmlover/.pyenv/versions/3.8.16/lib/python3.8/asyncio/base_events.py", line 616, in run_until_complete
        return future.result()
    File "/home/metalmlover/dev/microsoft bot framework/BotTutorialSample/Python_tutorial/01-EchoBot/venv/lib/python3.8/site-packages/botbuilder/core/bot_framework_adapter.py", line 442, in process_activity
        identity = await self._authenticate_request(activity, auth_header)
    File "/home/metalmlover/dev/microsoft bot framework/BotTutorialSample/Python_tutorial/01-EchoBot/venv/lib/python3.8/site-packages/botbuilder/core/bot_framework_adapter.py", line 551, in _authenticate_request
        claims = await JwtTokenValidation.authenticate_request(
    File "/home/metalmlover/dev/microsoft bot framework/BotTutorialSample/Python_tutorial/01-EchoBot/venv/lib/python3.8/site-packages/botframework/connector/auth/jwt_token_validation.py", line 49, in authenticate_request
        raise PermissionError("Unauthorized Access. Request is not authorized")
    PermissionError: Unauthorized Access. Request is not authorized

My App Code:

    from flask import Flask,request,Response
    from botbuilder.schema import Activity
    from botbuilder.core import BotFrameworkAdapter,BotFrameworkAdapterSettings
    import asyncio    
    from echobot import EchoBot    
    app = Flask(__name__)
    loop = asyncio.get_event_loop()    
    botadaptersettings = BotFrameworkAdapterSettings("my_app_id_here","my_app_secret_here")
    botadapter = BotFrameworkAdapter(botadaptersettings)    
    ebot = EchoBot()    
    @app.route("/api/messages",methods=["POST"])
    def messages():    
        if "application/json" in request.headers["content-type"]:
        jsonmessage = request.json
        else:
        return Response(status=415)
    
        activity = Activity().deserialize(jsonmessage)
    
        async def turn_call(turn_context):
            await ebot.on_turn(turn_context)
    
        task = loop.create_task(botadapter.process_activity(activity,"",turn_call))
        loop.run_until_complete(task)
        return "200"
    
    if __name__ == '__main__':
        app.run('localhost',3978)

My bot:

    from botbuilder.core import TurnContext

    class EchoBot:
        async def on_turn(self,turn_context:TurnContext):
            await turn_context.send_activity(turn_context.activity.text)

Azure Bot Configuration: enter image description here


Solution

  • Refer MSDOC. to create an echo Bot using Python framework sdk.

    app.py:

    import sys
    import traceback
    from datetime import datetime
    
    from aiohttp import web
    from aiohttp.web import Request, Response, json_response
    from botbuilder.core import (BotFrameworkAdapter, BotFrameworkAdapterSettings,
                                 TurnContext)
    from botbuilder.core.integration import aiohttp_error_middleware
    from botbuilder.schema import Activity, ActivityTypes
    
    from bot import MyBot
    from config import DefaultConfig
    
    CONFIG = DefaultConfig()
    
    SETTINGS = BotFrameworkAdapterSettings(CONFIG.APP_ID, CONFIG.APP_PASSWORD)
    ADAPTER = BotFrameworkAdapter(SETTINGS)
    
    async def on_error(context: TurnContext, error: Exception):
    
        print(f"\n [on_turn_error] unhandled error: {error}", file=sys.stderr)
        traceback.print_exc()
    
        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."
        )
    
        if context.activity.channel_id == "emulator":
    
            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",
            )
    
            await context.send_activity(trace_activity)
    
    
    ADAPTER.on_turn_error = on_error
    
    # Create the Bot
    BOT = MyBot()
    
    # Listen for incoming requests on /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=415)
    
        activity = Activity().deserialize(body)
        auth_header = req.headers["Authorization"] if "Authorization" in req.headers else ""
    
        response = await ADAPTER.process_activity(activity, auth_header, BOT.on_turn)
        if response:
            return json_response(data=response.body, status=response.status)
        return Response(status=201)
    
    
    def init_func(argv):
        APP = web.Application(middlewares=[aiohttp_error_middleware])
        APP.router.add_post("/api/messages", messages)
        return APP
    if __name__ == "__main__":
        APP = init_func(None)
    
        try:
            web.run_app(APP, host="0.0.0.0", port=CONFIG.PORT)
        except Exception as error:
            raise error
    

    config.py:

    class DefaultConfig:
        """ Bot Configuration """
    
        PORT = 3978
        APP_ID = os.environ.get("MicrosoftAppId", "<AppID>")
        APP_PASSWORD = os.environ.get("MicrosoftAppPassword", "<Password>")
    
    ngrok http 3978 --host-header rewrite
    

    Navigate to Configuration in the Azure Bot=>Settings, paste the generated ngrok forwarding URL in the Messaging endpoint field by adding "/api/messages" at the end of the URL.

    enter image description here

    Test the Bot in Web Chat:

    enter image description here

    Console Output:

    enter image description here