twilio / twilio-python

A Python module for communicating with the Twilio API and generating TwiML.

Home Page:https://www.twilio.com/docs/libraries/python

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Twilio authentication always fails

hklchung opened this issue · comments

Issue Summary

I am developing a Python (Flask) voice app on a local machine and using Ngrok tunnel to allow Twilio access the app via a webhook (all set up according to the Twilio guide). I need to add a step to check the requests are coming from Twilio and am using the RequestValidator from the Twilio Python SDK. However, this step always returns false, but should be true as I am the one initiating the request during testing.

Steps to Reproduce

  1. Set up a test voice application with Python Flask, and using Ngrok tunnel
  2. Get the host, request url and scope
  3. Run RequestValidator()

Code Snippet

In your Python Flask test voice application, set up a /call app route, then inside the call() test the following

validator = RequestValidator(TWILIO_AUTH_TOKEN)

path = request.path
headers = dict(request.headers)
host = headers.get('Host')
url = f"http://{host}{path}"
body = request.data

validator.validate(request.url, headers, body)

Technical details:

  • twilio-python version: 8.1.0
  • python version: 3.8.8
  • Flask version: 2.2.2
  • pyngrok version: 6.0.0

Hello @hklchung Can you please refer this end-end test link? Let me know if this helps.

@hklchung did you use this guide ?

@hklchung did you use this guide ?

Hi @charan678 this is the guide I have been following. I have also double checked to ensure the Twilio webhook URL starts with http:// since I am using Ngrok.

I tried to run test_url() but unfortunately all returned with a response code 404.

Any advice on how to proceed would be highly appreciated
111
.
222

hello @hklchung It's working for me. Following are the steps followed by me

  1. setup ngrok
  • run ngrok config command ( ngrok config add-authtoken XXXXXXXXX). here XXXXXX is your ngrok authToken
  • run ngrok http command (ngrok --scheme http http 5000)
  1. open active phone number page and add ngrok http url to webhook for both messaging and voice

  2. I used this code below to test and export TWILIO_AUTH_TOKEN before running in local env

from flask import Flask, request, abort
from twilio.twiml.voice_response import VoiceResponse
from twilio.twiml.messaging_response import MessagingResponse
from twilio.request_validator import RequestValidator
from functools import wraps
import os


app = Flask(__name__)

def validate_twilio_request(f):
    """Validates that incoming requests genuinely originated from Twilio"""
    @wraps(f)
    def decorated_function(*args, **kwargs):
        # Create an instance of the RequestValidator class
        validator = RequestValidator(os.environ.get('TWILIO_AUTH_TOKEN'))

        # Validate the request using its URL, POST data,
        # and X-TWILIO-SIGNATURE header
        request_valid = validator.validate(
            request.url,
            request.form,
            request.headers.get('X-TWILIO-SIGNATURE', ''))

        print("Request valid = ", request_valid)
        # Continue processing the request if it's valid, return a 403 error if
        # it's not
        if request_valid:
            return f(*args, **kwargs)
        else:
            return abort(403)
    return decorated_function


@app.route('/voice', methods=['POST'])
@validate_twilio_request
def incoming_call():
    """Twilio Voice URL - receives incoming calls from Twilio"""
    # Create a new TwiML response
    resp = VoiceResponse()

    # <Say> a message to the caller
    from_number = request.values['From']
    body = """
    Thanks for calling!

    Your phone number is {0}. I got your call because of Twilio's webhook.

    Goodbye!""".format(' '.join(from_number))
    resp.say(body)

    # Return the TwiML
    return str(resp)

#
@app.route('/message', methods=['POST'])
@validate_twilio_request
def incoming_message():
    """Twilio Messaging URL - receives incoming messages from Twilio"""
    # Create a new TwiML response
    resp = MessagingResponse()

    # <Message> a text back to the person who texted us
    body = "Your text to me was {0} characters long. Webhooks are neat :)" \
        .format(len(request.values['Body']))
    resp.message(body)

    # Return the TwiML
    return str(resp)

@app.route('/health', methods=['GET'])
def healthcheck():
    return str("received")

if __name__ == '__main__':
    app.run(debug=True)
  • check your flask server is running on 5000 port
  1. Call on your active phone number and see the flask logs
  • Its displaying 200 status code and request valid True for me

Technical Details

twilio-python = 8.1.0
python version = 3.8.8
os = mac os
Test phone number location = us ( +1 XXX )
Flask version = 2.2.2

refer:

How to Use Ngrok to Send Automatic Textback SMS Using POST & GET API Requests
how-to-secure-your-flask-app-by-validating-incoming-twilio-requests

Thanks @charan678, your answer provided me new direction to test in a more granular level. I found a working solution.

The webhook on the Twilio console must first be set to http://, Ngrok tunnels can have both http:// and https:// (no need to change anything here). But the tricky part is when the request comes through to the app, the request URL is of course a http://, this needs to be manually changed back to https:// before sending into the RequestValidator().

Would you know why this change is required, considering the webhook URL set in the console is http:// and not a https://?