alexa / alexa-skills-kit-sdk-for-python

The Alexa Skills Kit SDK for Python helps you get a skill up and running quickly, letting you focus on skill logic instead of boilerplate code.
https://developer.amazon.com/en-US/docs/alexa/alexa-skills-kit-sdk-for-python/overview.html
Apache License 2.0
812 stars 206 forks source link

WebserviceSkillHandler instantiated with verify_signature=True gives error during verification #154

Closed roebius closed 4 years ago

roebius commented 4 years ago

I'm submitting a...


[ ] Regression (a behavior that used to work and stopped working in a new release)
[x] Bug report  
[ ] Performance issue
[ ] Feature request
[ ] Documentation issue or request
[ ] Other... Please describe:

I am testing a generic web service for a custom skill, instantiating a handler from WebserviceSkillHandler. If I instantiate the handler with verify_signature=True an error occurs during the signature verification. I am getting the error running my web service both locally (with ngrok) and remotely (on EC2), using SSL/TLS certificates from both an Amazon trusted certificate authority and self-signed. I am doing my testing using an Echo Dot device.

Expected Behavior

Basing on the docs for a Generic Web Service Handler, and on the fact that with verify_signature=False everything works fine, I would expect that it should transparently work fine also if I instantiate the handler with verify_signature=True.

Current Behavior

When the handler from WebserviceSkillHandler is instantiated with verify_signature=True the following error occurs when calling the verify_request_and_dispatch method of the handler:

AttributeError: 'bytes' object has no attribute 'encode' 
in File "anaconda3/envs/qa/lib/python3.7/site-packages/ask_sdk_webservice_support/verifier.py", line 363, 
in _valid_request_body request_env_bytes = serialized_request_env.encode(CHARACTER_ENCODING)

Steps to Reproduce (for bugs)

# Complete code working with the interaction model from the Alexa Hello World example
from sanic import Sanic
from sanic import response as sanic_response
from ask_sdk.standard import StandardSkillBuilder
from ask_sdk_core.dispatch_components import (AbstractRequestHandler)
from ask_sdk_core.handler_input import HandlerInput
from ask_sdk_core.utils import is_request_type, is_intent_name
from ask_sdk_model import Response
from ask_sdk_webservice_support.webservice_handler import WebserviceSkillHandler

app = Sanic("hello_example")

@app.route("/", methods=["GET"])
async def health(request):
    """ Health check """
    return sanic_response.json({"status": "ok"})

@app.route("/alexa_webhook", methods=["POST"])
async def receive(request):
    """ Alexa Intent Dispatcher """
    headers = request.headers
    body = request.body
    response_to_ws = webservice_handler.verify_request_and_dispatch(http_request_headers=headers,
                                                                    http_request_body=body)
    return sanic_response.json(response_to_ws)

# Launch request handling
class LaunchRequestHandler(AbstractRequestHandler):
    def can_handle(self, handler_input):
        # type: (HandlerInput) -> bool
        return is_request_type("LaunchRequest")(handler_input)

    def handle(self, handler_input):
        # type: (HandlerInput) -> Response

        speech = "Hello world!"
        reprompt = "Hello world again is the only thing I can say!"
        handler_input.response_builder.speak(speech).ask(reprompt)

        return handler_input.response_builder.response

# Simple intent handling
class HelloWorldIntentHandler(AbstractRequestHandler):
    def can_handle(self, handler_input):
        # type: (HandlerInput) -> bool
        return is_intent_name("HelloWorldIntent")(handler_input)

    def handle(self, handler_input):
        # type: (HandlerInput) -> Response
        speech = "Hello world again!"
        handler_input.response_builder.speak(speech).set_should_end_session(False)

        return handler_input.response_builder.response

if __name__ == "__main__":

    sb = StandardSkillBuilder()
    sb.add_request_handler(LaunchRequestHandler())
    sb.add_request_handler(HelloWorldIntentHandler())

    ver_signature = True   # "ver_signature = True" CAUSES THE ERROR
    ver_timestamp = True
    webservice_handler = WebserviceSkillHandler(skill=sb.create(),
                                                verify_signature=ver_signature,
                                                verify_timestamp=ver_timestamp)

    app.run(host="0.0.0.0", port=3030)

Context

Even if the ASK SDK has specific extensions for Flask and Django, I am trying to develop a custom skill using a web service with the Sanic web framework that should offer higher speed performance.

Your Environment

Python version info

roebius commented 4 years ago

Sorry, my bad! After examining the code in the flask-ask-sdk folder of the Python ASK SDK, I solved the issue with: body = request.body.decode("utf-8") instead of body = request.body