Verify Your Users on WhatsApp with Python and Twilio

June 07, 2022
Written by
Reviewed by

Verify Your Users on WhatsApp with Python and Twilio

Twilio Verify is an easy-to-use service for user verification through numeric codes. This service now includes support for WhatsApp (in public beta, as of June 2022). In this tutorial, you’ll learn how to implement a WhatsApp user verification flow in a Python and Flask application.

Verification code sent to WhatsApp

Requirements

To work on this tutorial you will need the following items:

Create a Twilio Verify service

To use Twilio Verify in an application, a Verify service must be created first. Open the Twilio Console in your web browser, type verify in the navigation bar’s search box, and select “Verify services” from the search results.

Verify Services in Twilio Console

In the Verify services page, click the “Create Service Now” button.

If you already have one or more Verify services in your account, then this button will be displayed as a plus sign at the top of the service list.

Type the name of your application in the “Friendly Name” field. For this tutorial, I’m going to use the name “Awesome App”. The name that you type in this field will appear in the WhatsApp verification messages that are sent to your users. Once you have entered the name, press “Create”.

Create new verify service

Once the service is created, you will be redirected to its settings page. Leave this page open while you continue with the tutorial, as you will later need the “Service SID” that was assigned to your service.

For this tutorial, the default settings for the service are appropriate, but note how you can configure, among other things, how many digits to use in the verification codes that are sent to users.

Project setup

In this section, you are going to set up a brand-new Flask project. To keep things nicely organized, open a terminal or command prompt and find a suitable parent directory where the project you are about to create will live. Type the following commands to create a directory for this project:

mkdir flask-whatsapp-verify
cd flask-whatsapp-verify

This application is going to need a few HTML templates. Create a directory for them inside the project:

mkdir templates

Create a virtual environment

Following Python best practices, you are now going to create a virtual environment, where you are going to install the Python dependencies needed for this project.

If you are using a UNIX or macOS system, open a terminal and enter the following commands:

python3 -m venv venv
source venv/bin/activate

If you are following the tutorial on Windows, enter the following commands in a command prompt window:

python -m venv venv
venv\Scripts\activate

With the virtual environment activated, you are ready to install the Python dependencies required for this project:

pip install flask python-dotenv twilio

The Python packages that this project uses are:

Define application settings

To send verification messages with Twilio Verify, the Flask application will need to authenticate with your Twilio account credentials. The application will also need to know the “Service SID” assigned to the Verify service you created above.

The most secure way to define these configuration values is to set environment variables for them, and the most convenient way to manage your environment variables in a Flask application is to use a .env file.

Create a new file named .env (note the leading dot) in the root directory of your project and enter the following contents in it:

TWILIO_ACCOUNT_SID=xxxxxxxxx
TWILIO_AUTH_TOKEN=xxxxxxxxx
TWILIO_VERIFY_SERVICE=xxxxxxxxx

You will need to replace all the xxxxxxxxx placeholders with the correct values that apply to you. The first two variables are your Twilio “Account SID” and your “Auth Token”. You can find them in the “Account Info” section of the Twilio Console main dashboard:

Twilio Account Info

The value of the third configuration variable comes from the “Service SID” field in the Verify service page. This is a long string of characters that starts with the letters VA.

Send a verification code

You are now ready to start coding the Python application. In your text editor or IDE, create a file named app.py in the root directory of the project. Copy the following contents to it:

import os
from flask import Flask, render_template, request, session, redirect, url_for
from twilio.rest import Client

app = Flask(__name__)
app.config['SECRET_KEY'] = 'top-secret!'
client = Client()


@app.route('/', methods=['GET', 'POST'])
def index():
    if request.method == 'GET':
        return render_template('index.html')
    phone = request.form.get('phone')
    if not phone:
        return redirect(url_for('index'))

    verification = client.verify.services(
        os.environ['TWILIO_VERIFY_SERVICE']).verifications.create(
            to=phone, channel='whatsapp')
    if verification.status != 'pending':
        return redirect(url_for('index'))

    session['phone'] = phone
    session['verified'] = False
    return redirect(url_for('verify'))

The application creates two global objects: the Flask application and the Twilio client instance. The Flask application instance is configured with a secret key, as this is required to have access to session storage.

The Twilio client is implicitly initialized with the TWILIO_ACCOUNT_SID and TWILIO_AUTH_TOKEN variables defined in the environment. If these variables are not defined or are incorrect, an authentication error message will appear when using the application.

The index() function is the handler for the root URL of the application. This route supports GET and POST methods. The GET request is going to render a web form where the user can enter their WhatsApp phone number. The browser will then send a POST request to the same URL when the user submits the form.

When the request method is GET, the application renders a template named index.html. Create the index.html file in the templates subdirectory, and copy the following HTML into it:

<!doctype html>
<html>
  <head>
    <title>Twilio WhatsApp Verification Example</title>
  </head>
  <body>
    <h1>Twilio WhatsApp Verification Example</h1>
    <form method="post">
      <label for="phone">Your WhatsApp number:</label>
      <input name="phone" id="phone" placeholder="+12345678900" autofocus>
      <input type="submit" value="Submit">
    </form>
  </body>
</html>

This HTML page renders a form that prompts the user to enter their WhatsApp phone number.

When the user submits the form, the browser will send a POST request to the same URL. At this point the application will retrieve the phone number from the request.form object, provided by the Flask framework. If the phone number is not present, then a redirect to the index page is issued, to give the user another chance to enter correct information.

The Twilio client is then used to create a verification instance, initialized with the user’s phone number and a channel set to whatsapp. This will trigger a verification code to be generated by Twilio, and sent to the user via WhatsApp on the provided number.

The application then checks the reported status on the verification instance. If the status is anything other than pending, it is assumed that an error has occurred and the user is then redirected to the main page.

With a pending verification, the phone number is stored in the user’s session, and then the user is redirected to the /verify URL, which is where the verification code will be requested.

Verify a code

Once the user provides a phone number and a verification code is issued, the /verify page of the application allows the user to enter the code they received via WhatsApp.

Below you can see the verification route of the application. Add this code at the bottom of app.py:

@app.route('/verify', methods=['GET', 'POST'])
def verify():
    if request.method == 'GET':
        return render_template('verify.html')
    phone = session.get('phone')
    code = request.form.get('code')
    if not phone:
        return redirect(url_for('index'))
    if not code:
        return redirect(url_for('verify'))
        
    verification_check = client.verify.services(
        os.environ['TWILIO_VERIFY_SERVICE']).verification_checks.create(
            to=phone, code=code)
    if verification_check.status == 'approved':
        del session['phone']
        session['verified'] = True
        return redirect(url_for('success'))
    return redirect(url_for('verify'))

This route also supports GET and POST requests. In the GET case, it renders a template named verify.html. In your editor, open a new file named verify.html in the templates subdirectory, and copy the following HTML page to it:

<!doctype html>
<html>
  <head>
    <title>Twilio WhatsApp Verification Example</title>
  </head>
  <body>
    <h1>Twilio WhatsApp Verification Example</h1>
    <form method="post">
      <label for="code">Verification code:</label>
      <input name="code" id="code" placeholder="123456" autofocus>
      <input type="submit" value="Submit">
    </form>
  </body>
</html>

The form defined in verify.html requests a code from the user. When the form is submitted, a POST request is sent to the same URL.

The application retrieves the phone argument from the session storage and the code argument submitted with the form, and sends them to Twilio Verify to create a verification check instance. If the code is correct for the given number, then the status of the verification check is approved. In this case, the phone number is removed from the session, the verified session variable is set to True, and then the user is redirected to the third and last route of the application on the /success URL. If the status is any other value, then the verification has failed and the user is redirected back to the /verify route to try another code.

Below, you can see the definition of the /success route. Copy this code at the bottom of app.py.

@app.route('/success')
def success():
    if not session.get('verified'):
        return redirect(url_for('index'))
    return render_template('success.html')

This route renders the success.html template when the user has been verified, or else redirects to the initial page. This HTML template is shown below. Remember that template files go in the templates subdirectory.

<!doctype html>
<html>
  <head>
    <title>Twilio WhatsApp Verification Example</title>
  </head>
  <body>
    <h1>Twilio WhatsApp Verification Example</h1>
    <p>You have been verified!</p>
    <p><a href="/">Verify another number?</a></p>
  </body>
</html>

Run the application

The application is now complete. Make sure that your app.py file is in the root directory of the project, and that your templates subdirectory has the index.html, verify.html and success.html files.

Start the application by entering the following command in your terminal:

flask run

Once the application starts, open your web browser and navigate to http://localhost:5000. Type your WhatsApp phone number in E.164 format to request a verification code.

A few seconds later, you should have an incoming message on WhatsApp with your code. Type this code in the verification page of the application to receive a success message. Also, feel free to try a wrong code to see how it is rejected and you’re offered another chance.

Flask application demo

Conclusion

Congratulations on learning how to verify users on WhatsApp with Twilio Verify! Did you know that Twilio Verify can also send verification codes to users by SMS, voice call and email? It can also verify standard TOTP codes and push notifications to your iOS or Android app!

I’d love to see what you build with Twilio Verify!

Miguel Grinberg is a Principal Software Engineer for Technical Content at Twilio. Reach out to him at mgrinberg [at] twilio [dot] com if you have a cool project you’d like to share on this blog!