Password-Protected FlexML Application

In this guide, you will learn how to build a password-protected FlexML application. This might be useful if you want to protect your destination from unauthorized calls, so that only the users who know the password could reach it.

I. Plan Application Structure

Before we start writing our application, we need to plan how the application is going to behave and respond to different user actions. To do that, we suggest the following algorithm that describes the application behavior:

  1. The caller calls the phone number that is associated with our FlexML application.
  2. The application greets the caller and asks the caller to enter the password.
  3. When the caller enters the password, the application will redirect the caller to the password validation route and compare the entered password to the correct one:
    • If the entered password is correct, the application will redirect the user to the destination number.
    • If the entered password is incorrect, the application informs the caller about it and redirects them to the route where they can enter the password once again.
    • If the caller enters the incorrect password three times, the application will hang up.

This application algorithm can be described with the following block diagram:

Application Algorithm

FlexML has verbs for all the above actions, so we can proceed to building the application.

II. Build Application

In this section, we will build a FlexML application using Python to serve our route.

Create First Route

Let’s start our application with the default route that will accept the call and make some response. The flexml_password_protect.py Python code file will hold our default route, and all the next routes we create for the sake of simplicity.

Import Flask at the beginning of the file, and initiate the Flask application with the app = Flask(__name__) command.

We will call the default route hello. It will be available at the http://example.com/hello address of our application, and will only work if the POST is applied when addressing it.

To respond to the incoming call, the Say FlexML verb is used. It will pronounce the words stored inside the <Say></Say> tags, for example:

<Say>Welcome to CarrierX password-protected FlexML application</Say>

For the Say to work correctly, we need to place it inside the Response tag, like this:

<Response>
    <Say>Welcome to CarrierX password-protected FlexML application</Say>
</Response>

The Pause FlexML verb allows us to add some time before the application responds to the call, so that the voice could be correctly loaded even on slower connections. The length of the pause is set in seconds, and three seconds is alright for most cases.

The resulting code for our hello route will look the following way:

from flask import Flask

app = Flask(__name__)

@app.route('/hello', methods=['POST'])
def hello():
    return '''<Response>
                  <Pause length="3"/>
                  <Say>Welcome to CarrierX password-protected FlexML application</Say>
              </Response>'''

Start Flask Server

Now let’s test the first lines of our application! You can run your local Flask server through terminal using the following command that will serve the created application to the incoming requests.

FLASK_APP=flexml_password_protect.py flask run

Now your application is running, but it is not visible to the outside world. We need to expose the localhost route publicly over the Internet so that your endpoint could access and load the FlexML instructions. To do this, we can use the free ngrok tool. If you choose to use ngrok, follow the instructions on their website to download it. Alternatively, you may expose the URL any other way you choose.

Once your Flask server is running, your terminal will list the port it is running on. Expose this port by running the following command through terminal. Make sure to replace 5000 (which is the Flask default port) with the port that your Flask server is running on.

./ngrok http 5000

When ngrok is run successfully, it will open a new terminal tab that will show you the http and https URLs that make the application publicly available over the Internet.

ngrok Terminal Window

Add the https ngrok link (in our sample it is https://d4d66ed41d49.ngrok.io/hello, as we chose hello to be our default route) to either a FlexML endpoint or a DID associated with a FlexML endpoint. Refer to the FlexML Endpoint quick start guide to learn how.

Now we have an introductory route that greets the caller.

This logic can be described with the following block diagram:

Application Algorithm Greeting

The next step is to create a route that allows the caller to enter the password and gathers the entered digits.

Enter Password

Let’s add the second route to our file: enter.

In it, we will use the Gather FlexML verb to collect the digits that the caller enters as a password. We will also use Say once again to instruct the caller of what is expected to be done.

<Response>
    <Gather timeout="10">
        <Say>Please enter the password followed by the hash sign</Say>
    </Gather>
</Response>

The application will redirect the caller to this route from the hello route. For this, we will use the Redirect FlexML verb.

We add <Redirect>/enter</Redirect> to the first route. The resulting code looks like this:

from flask import Flask

app = Flask(__name__)

@app.route('/hello', methods=['POST'])
def hello():
    return '''<Response>
                  <Pause length="3"/>
                  <Say>Welcome to CarrierX password-protected FlexML application</Say>
                  <Redirect>/enter</Redirect>
              </Response>'''

@app.route('/enter', methods=['POST'])
def enter():
    return '''<Response>
                  <Gather timeout="10">
                      <Say>Please enter the password followed by the hash sign</Say>
                  </Gather>
              </Response>'''

This is what happens in the code above:

This logic can be described with the following block diagram:

Application Algorithm Enter

Parse Password

Once the caller enters the password, the application must parse the entered information to find out whether the entered password is correct or not.

But wait! We forgot to specify the password that we expect the caller to enter. Let’s add it to the beginning of the code file before the first route:

password_value = '123456'

Now let’s add the parse route where we compare the entered password with the one we specified for our application. For now, we will simply announce the route and do nothing else afterwards:

@app.route('/parse', methods=['POST'])
def parse():

For the call to be redirected to the new parse route, we add the action="/parse" attribute to the Gather verb. The response for the enter route will look like this:

<Response>
    <Gather timeout="10" action="/parse">
        <Say>Please enter the password followed by the hash sign</Say>
    </Gather>
</Response>

At this stage, we have the following resulting code for our application:

from flask import Flask

app = Flask(__name__)

password_value = '123456'

@app.route('/hello', methods=['POST'])
def hello():
    return '''<Response>
                  <Pause length="3"/>
                  <Say>Welcome to CarrierX password-protected FlexML application</Say>
                  <Redirect>/enter</Redirect>
              </Response>'''

@app.route('/enter', methods=['POST'])
def enter():
    return '''<Response>
                  <Gather timeout="10" action="/parse">
                      <Say>Please enter the password followed by the hash sign</Say>
                  </Gather>
              </Response>'''

@app.route('/parse', methods=['POST'])
def parse():

Now we have the set password and the parse route, but what exactly will we compare to the specified password?

The enter route, once it gathers all the entered digits (i.e., once the caller enters the digits and presses the # sign on the phone keypad), sends the following data to the parse route:

{
    "AccountSid": "",
    "ApiVersion": "2.0",
    "CallSid": "4f0d84c72ff90967f58351cbe8364a77",
    "CallerName": "+19093189030",
    "Digits": "654321",
    "Direction": "inbound",
    "From": "19093189030",
    "OriginalFrom": "+19093189030",
    "OriginalTo": "19093189029",
    "RequestUrl": "https://d4d66ed41d49.ngrok.io/enter",
    "To": "19093189029"
}

This part is of interest to us:

{
    "Digits": "654321"
}

These are the digits that the caller entered when instructed, and in our example they differ from the password we need.

As this data is sent in a form of JSON, our application must get it and correctly retrieve the necessary Digits value.

This is done with the request.get_json() method that is available in the Flask request module, which parses data as JSON.

Add the following lines to the parse route of the application:

data = request.get_json()
entered_digits = data.get('Digits','')

Here the data variable will be equal to all the JSON data received from the enter route, and entered_digits will accept the Digits value and be equal to 654321.

And now we can compare our set password with the received digits. The result should follow this logic:

The resulting code is here:

from flask import Flask, request

app = Flask(__name__)

password_value = '123456'

@app.route('/hello', methods=['POST'])
def hello():
    return '''<Response>
                  <Pause length="3"/>
                  <Say>Welcome to CarrierX password-protected FlexML application</Say>
                  <Redirect>/enter</Redirect>
              </Response>'''

@app.route('/enter', methods=['POST'])
def enter():
    return '''<Response>
                  <Gather timeout="10" action="/parse">
                      <Say>Please enter the password followed by the hash sign</Say>
                  </Gather>
              </Response>'''

@app.route('/parse', methods=['POST'])
def parse():
    data = request.get_json()
    entered_digits = data.get('Digits','')
    if entered_digits == password_value:
        return '''<Response>
                      <Say>Your password is correct. Thank you for calling.</Say>
                      <Dial>
                          <Number>19093189031</Number>
                      </Dial>
                  </Response>'''
    else:
        return '''<Response>
                      <Say>The password you entered is incorrect. Please try again.</Say>
                      <Redirect>/enter</Redirect>
                  </Response>'''

This logic can be described with the following block diagram:

Application Algorithm Parse

If we run our application now and call the number, we can see that it works as expected.

Limit Number of Attempts

The problem is that the caller can try and enter the password again and again, the number of attempts is not restricted in any way. We want to limit this number to three, so that after the third incorrect attempt the application would end the call hanging up.

For this, we will use a new variable that will store the number of incorrect attempts. Let’s call it wrong_password and set it equal to 1 at the beginning. With each unsuccessful attempt it will increase (wrong_password += 1) until it reaches 3.

We use the Say and Hangup FlexML verbs to respond in case the caller does not know the password and enters it incorrectly for three times. The code in this case will look the following way:

from flask import Flask, request

app = Flask(__name__)

password_value = '123456'

@app.route('/hello', methods=['POST'])
def hello():
    wrong_password = 1
    return '''<Response>
                  <Pause length="3"/>
                  <Say>Welcome to CarrierX password-protected FlexML application</Say>
                  <Redirect>/enter</Redirect>
              </Response>'''

@app.route('/enter', methods=['POST'])
def enter():
    return '''<Response>
                  <Gather timeout="10" action="/parse">
                      <Say>Please enter the password followed by the hash sign</Say>
                  </Gather>
              </Response>'''

@app.route('/parse', methods=['POST'])
def parse():
    data = request.get_json()
    entered_digits = data.get('Digits','')
    if entered_digits == password_value:
        return '''<Response>
                      <Say>Your password is correct. Thank you for calling.</Say>
                      <Dial>
                          <Number>15162065515</Number>
                      </Dial>
                  </Response>'''
    elif entered_digits != password_value and wrong_password < 3:
        wrong_password += 1
        return '''<Response>
                      <Say>The password you entered is incorrect. Please try again.</Say>
                      <Redirect>/enter</Redirect>
                  </Response>'''
    else:
        return '''<Response>
                      <Say>You entered an incorrect password too many times. Goodbye.</Say>
                      <Hangup/>
                  </Response>'''

Unfortunately, this code will not work the way we expect it to work, because the variable values are not transferred among the routes.

Luckily, we have the Store FlexML verb that will do the work for us.

Lets add <Store name="wrong_password">1</Store> to the hello route and remove the wrong_password = 1 line from there. In this case, the response sent to the parse route will look like this:

{
    "AccountSid": "",
    "ApiVersion": "2.0",
    "CallSid": "4f0d84c72ff90967f58351cbe8364a77",
    "CallerName": "+19093189030",
    "Digits": "123456",
    "Direction": "inbound",
    "From": "19093189030",
    "OriginalFrom": "+19093189030",
    "OriginalTo": "19093189029",
    "RequestUrl": "https://d4d66ed41d49.ngrok.io/enter",
    "Storage_wrong_password": "1",
    "To": "19093189029"
}

Here, Storage_wrong_password is the data introduced by the Store verb that is transferred among all the routes (hello, enter, and parse). Now we can easily manipulate this data value.

Let’s extract this value from the returned JSON data using the wrong_password = data.get('Storage_wrong_password','') method, just like we did with the Digits.

This will work, but will throw an error at the step where we compare the wrong_password to an integer (elif wrong_password < 3), or try to add an integer to it (wrong_password += 1). It happens because the wrong_password variable type is string, and we need it to be integer to perform math operations on it.

To change the variable type, we will use the int() Python function, and the resulting method to get the wrong_password value will look like this:

wrong_password = int(data.get('Storage_wrong_password',''))

To manipulate the wrong_password value for all other routes, we add the <Store name="wrong_password">{wrong_password}</Store> variable to the response from the parse route.

The final code of our application looks the following way:

from flask import Flask, request

app = Flask(__name__)

password_value = '123456'

@app.route('/hello', methods=['POST'])
def hello():
    return '''<Response>
                  <Pause length="3"/>
                  <Say>Welcome to CarrierX password-protected FlexML application</Say>
                  <Store name="wrong_password">1</Store>
                  <Redirect>/enter</Redirect>
              </Response>'''

@app.route('/enter', methods=['POST'])
def enter():
    return '''<Response>
                  <Gather timeout="10" action="/parse">
                      <Say>Please enter the password followed by the hash sign</Say>
                  </Gather>
              </Response>'''

@app.route('/parse', methods=['POST'])
def parse():
    data = request.get_json()
    wrong_password = int(data.get('Storage_wrong_password',''))
    entered_digits = data.get('Digits','')
    if entered_digits == password_value:
        return '''<Response>
                      <Say>Your password is correct. Thank you for calling.</Say>
                      <Dial>
                          <Number>15162065515</Number>
                      </Dial>
                  </Response>'''
    elif entered_digits != password_value and wrong_password < 3:
        wrong_password += 1
        return f'''<Response>
                      <Say>The password you entered is incorrect. Please try again.</Say>
                      <Store name="wrong_password">{wrong_password}</Store>
                      <Redirect>/enter</Redirect>
                  </Response>'''
    else:
        return '''<Response>
                      <Say>You entered an incorrect password too many times. Goodbye.</Say>
                      <Hangup/>
                  </Response>'''

Run it, call the application, and try and enter the correct password. Or try and enter the incorrect password three times, and see the result.

III. Further Reading

You have created your password-protected FlexML application!

Refer to the following pages to learn more about FlexML verbs and how to use them, and about ways to set up a FlexML endpoint: