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:
- The caller calls the phone number that is associated with our FlexML application.
- The application greets the caller and asks the caller to enter the password.
- 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:
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.
- Register with CarrierX to access the CarrierX portal.
- Rent a phone number from CarrierX to associate it with the created FlexML endpoint later.
- Have Python installed on your local machine. There are Python versions available for MS Windows, Mac OS X, various Linux distributions, and some other platforms.
- Add Flask micro web framework to your Python installation. It will serve as a simple web server and will run our FlexML application.
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.
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:
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:
- The caller calls the phone number provided in the FlexML endpoint and gets to the
hello
route. - The FlexML instructions greet the caller, then the call is redirected to the
enter
route. There the caller is instructed and expected to enter the password. - The
enter
route collects the digits that the caller enters.
This logic can be described with the following block diagram:
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:
- if
entered_digits
equals topassword_value
, the application will respond with theYour password is correct. Thank you for calling.
phrase and redirect to the destination (or call the destination number using the Dial FlexML verb, in our case). - if not, the application will respond with the
The password you entered is incorrect. Please try again.
phrase and redirect back to theenter
route, allowing the caller to enter the password once again.
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:
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: