Skip to contentSkip to navigationSkip to topbar
Rate this page:
On this page

Account Verification with Authy, Python and Flask


(warning)

Warning

As of November 2022, Twilio no longer provides support for Authy SMS/Voice-only customers. Customers who were also using Authy TOTP or Push prior to March 1, 2023 are still supported. The Authy API is now closed to new customers and will be fully deprecated in the future.

For new development, we encourage you to use the Verify v2 API.

Existing customers will not be impacted at this time until Authy API has reached End of Life. For more information about migration, see Migrating from Authy to Verify for SMS(link takes you to an external page).

Ready to add Authy user account verification to your Flask application? Don't worry, this will be the easiest thing you do all day.

Here's how it all works at a high level:

  1. A user begins the registration process by entering their information (including a phone number) into a signup form.
  2. The authentication system sends a one-time password to the user's mobile phone to verify the phone number.
  3. The user enters that one-time password into a form to complete registration.
  4. The user sees a success page and receives an SMS alerting them that their account has been created!

Building Blocks

building-blocks page anchor

To get this done, you'll be working with the following Twilio-powered APIs:

Authy REST API

  • Authy Docs : Find quickstarts, documentation, and info on the helper libraries.

Twilio REST API

  • Messages Resource : We will use Twilio directly to send our users a confirmation message after they creates their accounts.

Initialize Flask application

initialize-flask-application page anchor

account_verification_flask/__init__.py


_27
from flask import Flask
_27
from flask_login import LoginManager
_27
from flask_sqlalchemy import SQLAlchemy
_27
from flask_bcrypt import Bcrypt
_27
from account_verification_flask.config import config_env_files
_27
_27
app = Flask(__name__)
_27
db = SQLAlchemy()
_27
bcrypt = Bcrypt()
_27
login_manager = LoginManager()
_27
_27
_27
def prepare_app(
_27
environment='development', p_db=db, p_bcrypt=bcrypt, p_login_manager=login_manager
_27
):
_27
app.config.from_object(config_env_files[environment])
_27
_27
p_db.init_app(app)
_27
p_bcrypt.init_app(app)
_27
p_login_manager.init_app(app)
_27
p_login_manager.login_view = 'register'
_27
return app
_27
_27
_27
prepare_app()
_27
_27
import account_verification_flask.views # noqa F402

All of this can be done in under a half an hour with the simplicity and power of Authy and Twilio. Let's get started!


Application configuration

application-configuration page anchor

For this application we'll be using the The Twilio Python Helper Library and the Python Client for Authy API(link takes you to an external page). We require some configuration from your side before we can begin.

Edit the DevelopmentConfig class constant values located in the account_verification_flask/config.py file:


_10
AUTHY_KEY = 'your_authy_key'
_10
_10
TWILIO_ACCOUNT_SID = 'your_twilio_account_sid'
_10
TWILIO_AUTH_TOKEN = 'your_twilio_auth_token'
_10
TWILIO_NUMBER = 'your_twilio_phone_number'
_10
_10
SQLALCHEMY_DATABASE_URI = 'sqlite://'

Note that you have to replace the placeholders your_twilio_account_sid, your_twilio_auth_token, your_twilio_phone_number and your_authy_key with your information. You can find all of those parameters (and more) in your Twilio Account Console(link takes you to an external page) and your Authy dashboard(link takes you to an external page).

Configure your application with Twilio and Authy

configure-your-application-with-twilio-and-authy page anchor

account_verification_flask/config.py


_41
import os
_41
_41
from dotenv import load_dotenv
_41
_41
load_dotenv()
_41
_41
basedir = os.path.abspath(os.path.dirname(__file__))
_41
_41
_41
class DefaultConfig(object):
_41
SECRET_KEY = os.environ.get('SECRET_KEY', 'secret-key')
_41
SQLALCHEMY_DATABASE_URI = os.environ.get('DATABASE_URI')
_41
SQLALCHEMY_TRACK_MODIFICATIONS = False
_41
_41
TWILIO_ACCOUNT_SID = os.environ.get('TWILIO_ACCOUNT_SID', None)
_41
TWILIO_API_KEY = os.environ.get('TWILIO_API_KEY', None)
_41
TWILIO_API_SECRET = os.environ.get('TWILIO_API_SECRET', None)
_41
TWILIO_NUMBER = os.environ.get('TWILIO_NUMBER', None)
_41
AUTHY_API_KEY = os.environ.get('AUTHY_API_KEY', None)
_41
_41
DEBUG = False
_41
_41
_41
class DevelopmentConfig(DefaultConfig):
_41
SQLALCHEMY_DATABASE_URI = 'sqlite:///' + os.path.join(basedir, 'dev.sqlite')
_41
DEBUG = True
_41
_41
_41
class TestConfig(DefaultConfig):
_41
SQLALCHEMY_DATABASE_URI = 'sqlite:///:memory:'
_41
SQLALCHEMY_ECHO = True
_41
DEBUG = True
_41
TESTING = True
_41
WTF_CSRF_ENABLED = False
_41
_41
_41
config_env_files = {
_41
'testing': 'account_verification_flask.config.TestConfig',
_41
'development': 'account_verification_flask.config.DevelopmentConfig',
_41
'production': 'account_verification_flask.config.Default',
_41
}

Now that we've got the setup boilerplate out of the way, let's take a look at the User model.


The User Model for this tutorial is pretty straight-forward. Note the new variable authy_user_id, which is implemented for storing the user's Authy identification token.

We'll be using the Flask-Login(link takes you to an external page) library for our user session management. To integrate this library into our code we need to implement a few properties and methods(link takes you to an external page), then we're good to go.

User model with DB configuration and model methods for Flask-Login

user-model-with-db-configuration-and-model-methods-for-flask-login page anchor

account_verification_flask/models/models.py


_41
# from flask.ext.login import UserMixin
_41
from account_verification_flask import db, bcrypt
_41
_41
_41
class User(db.Model):
_41
__tablename__ = "users"
_41
_41
id = db.Column(db.Integer, primary_key=True)
_41
name = db.Column(db.String, nullable=False)
_41
email = db.Column(db.String, nullable=False)
_41
password = db.Column(db.String)
_41
phone_number = db.Column(db.String, nullable=False)
_41
country_code = db.Column(db.String, nullable=False)
_41
phone_number_confirmed = db.Column(db.Boolean, nullable=False, default=False)
_41
authy_user_id = db.Column(db.String, nullable=True)
_41
_41
def __init__(self, name, email, password, phone_number, country_code):
_41
self.name = name
_41
self.email = email
_41
self.password = bcrypt.generate_password_hash(password)
_41
self.phone_number = phone_number
_41
self.country_code = country_code
_41
self.phone_number_confirmed = False
_41
_41
def is_authenticated(self):
_41
return True
_41
_41
def is_active(self):
_41
return True
_41
_41
def is_anonymous(self):
_41
return False
_41
_41
def get_id(self):
_41
return str(self.id)
_41
_41
def __str__(self):
_41
return self.name
_41
_41
def __repr__(self):
_41
return f'<User: {self.name}>'

Pretty simple User model, right? Next we're going to visit the registration form on the client side.


In order to validate the user's account and register the user, we need a mobile number with a country code. We can then use Authy to send a verification code via SMS.

In this example we're validating and rendering the forms with the WTForms(link takes you to an external page) library. This allows us to define the forms as classes inside Python.

account_verification_flask/templates/register.html


_28
{% extends "layout.html" %}
_28
_28
{% block content %}
_28
_28
<h1>We're going to be *BEST* friends</h1>
_28
<p> Thanks for your interest in signing up! Can you tell us a bit about yourself?</p>
_28
_28
_28
<form method="POST" class="form-horizontal" role="form">
_28
{% from "_formhelpers.html" import render_errors, render_field %}
_28
{{ form.csrf_token }}
_28
{{ render_errors(form) }}
_28
<hr/>
_28
_28
{{ render_field(form.name, placeholder='Anakin Skywalker') }}
_28
{{ render_field(form.email, placeholder='darth@vader.com') }}
_28
{{ render_field(form.password) }}
_28
{{ render_field(form.country_code, id="authy-countries" ) }}
_28
{{ render_field(form.phone_number , type='number') }}
_28
_28
<div class="form-group">
_28
<div class="col-md-offset-2 col-md-10">
_28
<input type="submit" class="btn btn-primary" value="Sign Up" />
_28
</div>
_28
</div>
_28
</form>
_28
_28
{% endblock %}

That's it for the client side. Now let's look at what happens when the user submits the form.


Registration Server Side Implementation

registration-server-side-implementation page anchor

Next our controller stores the new user, registers them with Authy's API, and requests a new verification code.

The registration view route

the-registration-view-route page anchor

account_verification_flask/views.py


_155
from flask import request, flash, g
_155
from flask_login import login_user, logout_user, current_user
_155
from account_verification_flask import app, db, login_manager
_155
from account_verification_flask.forms.forms import (
_155
RegisterForm,
_155
ResendCodeForm,
_155
VerifyCodeForm,
_155
)
_155
from account_verification_flask.models.models import User
_155
from account_verification_flask.services.authy_services import AuthyServices
_155
from account_verification_flask.services.twilio_services import TwilioServices
_155
from account_verification_flask.utilities import User_Already_Confirmed
_155
from account_verification_flask.utilities.view_helpers import view, redirect_to
_155
import account_verification_flask.utilities
_155
_155
_155
@app.route('/')
_155
@app.route('/home')
_155
def home():
_155
return view('index')
_155
_155
_155
@app.route('/register', methods=["GET", "POST"])
_155
def register():
_155
form = RegisterForm()
_155
if request.method == 'POST':
_155
if form.validate_on_submit():
_155
_155
if User.query.filter(User.email == form.email.data).count() > 0:
_155
form.email.errors.append(
_155
account_verification_flask.utilities.User_Email_Already_In_Use
_155
)
_155
return view('register', form)
_155
_155
user = User(
_155
name=form.name.data,
_155
email=form.email.data,
_155
password=form.password.data,
_155
country_code=form.country_code.data,
_155
phone_number=form.phone_number.data,
_155
)
_155
db.session.add(user)
_155
db.session.commit()
_155
_155
authy_services = AuthyServices()
_155
if authy_services.request_phone_confirmation_code(user):
_155
db.session.commit()
_155
flash(account_verification_flask.utilities.Verification_Code_Sent)
_155
return redirect_to('verify', email=form.email.data)
_155
_155
form.email.errors.append(
_155
account_verification_flask.utilities.Verification_Code_Not_Sent
_155
)
_155
_155
else:
_155
return view('register', form)
_155
_155
return view('register', form)
_155
_155
_155
@app.route('/verify', methods=["GET", "POST"])
_155
@app.route('/verify/<email>', methods=["GET"])
_155
def verify():
_155
form = VerifyCodeForm()
_155
if request.method == 'POST':
_155
if form.validate_on_submit():
_155
user = User.query.filter(User.email == form.email.data).first()
_155
_155
if user is None:
_155
form.email.errors.append(
_155
account_verification_flask.utilities.User_Not_Found_For_Given_Email
_155
)
_155
return view('verify_registration_code', form)
_155
_155
if user.phone_number_confirmed:
_155
form.email.errors.append(User_Already_Confirmed)
_155
return view('verify_registration_code', form)
_155
_155
authy_services = AuthyServices()
_155
if authy_services.confirm_phone_number(user, form.verification_code.data):
_155
user.phone_number_confirmed = True
_155
db.session.commit()
_155
login_user(user, remember=True)
_155
twilio_services = TwilioServices()
_155
twilio_services.send_registration_success_sms(
_155
"+{0}{1}".format(user.country_code, user.phone_number)
_155
)
_155
return redirect_to('status')
_155
else:
_155
form.email.errors.append(
_155
account_verification_flask.utilities.Verification_Unsuccessful
_155
)
_155
return view('verify_registration_code', form)
_155
else:
_155
form.email.data = request.args.get('email')
_155
return view('verify_registration_code', form)
_155
_155
_155
@app.route('/resend', methods=["GET", "POST"])
_155
@app.route('/resend/<email>', methods=["GET"])
_155
def resend(email=""):
_155
form = ResendCodeForm()
_155
_155
if request.method == 'POST':
_155
if form.validate_on_submit():
_155
user = User.query.filter(User.email == form.email.data).first()
_155
_155
if user is None:
_155
form.email.errors.append(
_155
account_verification_flask.utilities.User_Not_Found_For_Given_Email
_155
)
_155
return view('resend_confirmation_code', form)
_155
_155
if user.phone_number_confirmed:
_155
form.email.errors.append(
_155
account_verification_flask.utilities.User_Already_Confirmed
_155
)
_155
return view('resend_confirmation_code', form)
_155
authy_services = AuthyServices()
_155
if authy_services.request_phone_confirmation_code(user):
_155
flash(account_verification_flask.utilities.Verification_Code_Resent)
_155
return redirect_to('verify', email=form.email.data)
_155
else:
_155
form.email.errors.append(
_155
account_verification_flask.utilities.Verification_Code_Not_Sent
_155
)
_155
else:
_155
form.email.data = email
_155
_155
return view('resend_confirmation_code', form)
_155
_155
_155
@app.route('/status')
_155
def status():
_155
return view('status')
_155
_155
_155
@app.route('/logout', methods=["POST"])
_155
def logout():
_155
logout_user()
_155
return redirect_to('home')
_155
_155
_155
# controller utils
_155
@app.before_request
_155
def before_request():
_155
g.user = current_user
_155
_155
_155
@login_manager.user_loader
_155
def load_user(user_id):
_155
try:
_155
return User.query.get(user_id)
_155
except Exception:
_155
return None

Next we'll set up our application to complete our user verification.


Verifying a User in Python

verifying-a-user-in-python page anchor

On the server we first check that the email belongs to a user that we haven't yet verified.

The process then has two critical steps:

  1. Communicate with Authy's API to check if the given code is correct.
  2. Send a confirmation SMS to the user using Twilio's API.

After that (assuming a success!) we redirect the user to a success page.

Verify a user's code and send a success SMS

verify-a-users-code-and-send-a-success-sms page anchor

account_verification_flask/views.py


_155
from flask import request, flash, g
_155
from flask_login import login_user, logout_user, current_user
_155
from account_verification_flask import app, db, login_manager
_155
from account_verification_flask.forms.forms import (
_155
RegisterForm,
_155
ResendCodeForm,
_155
VerifyCodeForm,
_155
)
_155
from account_verification_flask.models.models import User
_155
from account_verification_flask.services.authy_services import AuthyServices
_155
from account_verification_flask.services.twilio_services import TwilioServices
_155
from account_verification_flask.utilities import User_Already_Confirmed
_155
from account_verification_flask.utilities.view_helpers import view, redirect_to
_155
import account_verification_flask.utilities
_155
_155
_155
@app.route('/')
_155
@app.route('/home')
_155
def home():
_155
return view('index')
_155
_155
_155
@app.route('/register', methods=["GET", "POST"])
_155
def register():
_155
form = RegisterForm()
_155
if request.method == 'POST':
_155
if form.validate_on_submit():
_155
_155
if User.query.filter(User.email == form.email.data).count() > 0:
_155
form.email.errors.append(
_155
account_verification_flask.utilities.User_Email_Already_In_Use
_155
)
_155
return view('register', form)
_155
_155
user = User(
_155
name=form.name.data,
_155
email=form.email.data,
_155
password=form.password.data,
_155
country_code=form.country_code.data,
_155
phone_number=form.phone_number.data,
_155
)
_155
db.session.add(user)
_155
db.session.commit()
_155
_155
authy_services = AuthyServices()
_155
if authy_services.request_phone_confirmation_code(user):
_155
db.session.commit()
_155
flash(account_verification_flask.utilities.Verification_Code_Sent)
_155
return redirect_to('verify', email=form.email.data)
_155
_155
form.email.errors.append(
_155
account_verification_flask.utilities.Verification_Code_Not_Sent
_155
)
_155
_155
else:
_155
return view('register', form)
_155
_155
return view('register', form)
_155
_155
_155
@app.route('/verify', methods=["GET", "POST"])
_155
@app.route('/verify/<email>', methods=["GET"])
_155
def verify():
_155
form = VerifyCodeForm()
_155
if request.method == 'POST':
_155
if form.validate_on_submit():
_155
user = User.query.filter(User.email == form.email.data).first()
_155
_155
if user is None:
_155
form.email.errors.append(
_155
account_verification_flask.utilities.User_Not_Found_For_Given_Email
_155
)
_155
return view('verify_registration_code', form)
_155
_155
if user.phone_number_confirmed:
_155
form.email.errors.append(User_Already_Confirmed)
_155
return view('verify_registration_code', form)
_155
_155
authy_services = AuthyServices()
_155
if authy_services.confirm_phone_number(user, form.verification_code.data):
_155
user.phone_number_confirmed = True
_155
db.session.commit()
_155
login_user(user, remember=True)
_155
twilio_services = TwilioServices()
_155
twilio_services.send_registration_success_sms(
_155
"+{0}{1}".format(user.country_code, user.phone_number)
_155
)
_155
return redirect_to('status')
_155
else:
_155
form.email.errors.append(
_155
account_verification_flask.utilities.Verification_Unsuccessful
_155
)
_155
return view('verify_registration_code', form)
_155
else:
_155
form.email.data = request.args.get('email')
_155
return view('verify_registration_code', form)
_155
_155
_155
@app.route('/resend', methods=["GET", "POST"])
_155
@app.route('/resend/<email>', methods=["GET"])
_155
def resend(email=""):
_155
form = ResendCodeForm()
_155
_155
if request.method == 'POST':
_155
if form.validate_on_submit():
_155
user = User.query.filter(User.email == form.email.data).first()
_155
_155
if user is None:
_155
form.email.errors.append(
_155
account_verification_flask.utilities.User_Not_Found_For_Given_Email
_155
)
_155
return view('resend_confirmation_code', form)
_155
_155
if user.phone_number_confirmed:
_155
form.email.errors.append(
_155
account_verification_flask.utilities.User_Already_Confirmed
_155
)
_155
return view('resend_confirmation_code', form)
_155
authy_services = AuthyServices()
_155
if authy_services.request_phone_confirmation_code(user):
_155
flash(account_verification_flask.utilities.Verification_Code_Resent)
_155
return redirect_to('verify', email=form.email.data)
_155
else:
_155
form.email.errors.append(
_155
account_verification_flask.utilities.Verification_Code_Not_Sent
_155
)
_155
else:
_155
form.email.data = email
_155
_155
return view('resend_confirmation_code', form)
_155
_155
_155
@app.route('/status')
_155
def status():
_155
return view('status')
_155
_155
_155
@app.route('/logout', methods=["POST"])
_155
def logout():
_155
logout_user()
_155
return redirect_to('home')
_155
_155
_155
# controller utils
_155
@app.before_request
_155
def before_request():
_155
g.user = current_user
_155
_155
_155
@login_manager.user_loader
_155
def load_user(user_id):
_155
try:
_155
return User.query.get(user_id)
_155
except Exception:
_155
return None

What happens if the message was never sent, didn't arrive, or can't be found? Let's look at how to handle those scenarios next.


Re-sending a Verification Code

re-sending-a-verification-code page anchor

The form for re-sending the code is a single line, so let's skip that detail for this tutorial. Instead, let's just take a look at the controller function for resending verifications.

This controller loads the User associated with the request and then uses the same Authy API method we used earlier to resend the code. Pretty straightforward, right?

Re-send a verification code

re-send-a-verification-code page anchor

account_verification_flask/views.py


_155
from flask import request, flash, g
_155
from flask_login import login_user, logout_user, current_user
_155
from account_verification_flask import app, db, login_manager
_155
from account_verification_flask.forms.forms import (
_155
RegisterForm,
_155
ResendCodeForm,
_155
VerifyCodeForm,
_155
)
_155
from account_verification_flask.models.models import User
_155
from account_verification_flask.services.authy_services import AuthyServices
_155
from account_verification_flask.services.twilio_services import TwilioServices
_155
from account_verification_flask.utilities import User_Already_Confirmed
_155
from account_verification_flask.utilities.view_helpers import view, redirect_to
_155
import account_verification_flask.utilities
_155
_155
_155
@app.route('/')
_155
@app.route('/home')
_155
def home():
_155
return view('index')
_155
_155
_155
@app.route('/register', methods=["GET", "POST"])
_155
def register():
_155
form = RegisterForm()
_155
if request.method == 'POST':
_155
if form.validate_on_submit():
_155
_155
if User.query.filter(User.email == form.email.data).count() > 0:
_155
form.email.errors.append(
_155
account_verification_flask.utilities.User_Email_Already_In_Use
_155
)
_155
return view('register', form)
_155
_155
user = User(
_155
name=form.name.data,
_155
email=form.email.data,
_155
password=form.password.data,
_155
country_code=form.country_code.data,
_155
phone_number=form.phone_number.data,
_155
)
_155
db.session.add(user)
_155
db.session.commit()
_155
_155
authy_services = AuthyServices()
_155
if authy_services.request_phone_confirmation_code(user):
_155
db.session.commit()
_155
flash(account_verification_flask.utilities.Verification_Code_Sent)
_155
return redirect_to('verify', email=form.email.data)
_155
_155
form.email.errors.append(
_155
account_verification_flask.utilities.Verification_Code_Not_Sent
_155
)
_155
_155
else:
_155
return view('register', form)
_155
_155
return view('register', form)
_155
_155
_155
@app.route('/verify', methods=["GET", "POST"])
_155
@app.route('/verify/<email>', methods=["GET"])
_155
def verify():
_155
form = VerifyCodeForm()
_155
if request.method == 'POST':
_155
if form.validate_on_submit():
_155
user = User.query.filter(User.email == form.email.data).first()
_155
_155
if user is None:
_155
form.email.errors.append(
_155
account_verification_flask.utilities.User_Not_Found_For_Given_Email
_155
)
_155
return view('verify_registration_code', form)
_155
_155
if user.phone_number_confirmed:
_155
form.email.errors.append(User_Already_Confirmed)
_155
return view('verify_registration_code', form)
_155
_155
authy_services = AuthyServices()
_155
if authy_services.confirm_phone_number(user, form.verification_code.data):
_155
user.phone_number_confirmed = True
_155
db.session.commit()
_155
login_user(user, remember=True)
_155
twilio_services = TwilioServices()
_155
twilio_services.send_registration_success_sms(
_155
"+{0}{1}".format(user.country_code, user.phone_number)
_155
)
_155
return redirect_to('status')
_155
else:
_155
form.email.errors.append(
_155
account_verification_flask.utilities.Verification_Unsuccessful
_155
)
_155
return view('verify_registration_code', form)
_155
else:
_155
form.email.data = request.args.get('email')
_155
return view('verify_registration_code', form)
_155
_155
_155
@app.route('/resend', methods=["GET", "POST"])
_155
@app.route('/resend/<email>', methods=["GET"])
_155
def resend(email=""):
_155
form = ResendCodeForm()
_155
_155
if request.method == 'POST':
_155
if form.validate_on_submit():
_155
user = User.query.filter(User.email == form.email.data).first()
_155
_155
if user is None:
_155
form.email.errors.append(
_155
account_verification_flask.utilities.User_Not_Found_For_Given_Email
_155
)
_155
return view('resend_confirmation_code', form)
_155
_155
if user.phone_number_confirmed:
_155
form.email.errors.append(
_155
account_verification_flask.utilities.User_Already_Confirmed
_155
)
_155
return view('resend_confirmation_code', form)
_155
authy_services = AuthyServices()
_155
if authy_services.request_phone_confirmation_code(user):
_155
flash(account_verification_flask.utilities.Verification_Code_Resent)
_155
return redirect_to('verify', email=form.email.data)
_155
else:
_155
form.email.errors.append(
_155
account_verification_flask.utilities.Verification_Code_Not_Sent
_155
)
_155
else:
_155
form.email.data = email
_155
_155
return view('resend_confirmation_code', form)
_155
_155
_155
@app.route('/status')
_155
def status():
_155
return view('status')
_155
_155
_155
@app.route('/logout', methods=["POST"])
_155
def logout():
_155
logout_user()
_155
return redirect_to('home')
_155
_155
_155
# controller utils
_155
@app.before_request
_155
def before_request():
_155
g.user = current_user
_155
_155
_155
@login_manager.user_loader
_155
def load_user(user_id):
_155
try:
_155
return User.query.get(user_id)
_155
except Exception:
_155
return None

Let's take a step back and see how we can use Authy to resend a verification code to an unverified user.


Sending a Token on Account Creation

sending-a-token-on-account-creation page anchor

In order to end up with a cleaner and decoupled design we'll encapsulate all of Authy's related features in an AuthyService. This class will hold a shared class instance of the AuthyApiClient class.

Once the user has an authyId we can send a verification code to that user's mobile phone.

Send Authy token via SMS

send-authy-token-via-sms page anchor

account_verification_flask/services/authy_services.py


_37
import account_verification_flask.utilities
_37
from account_verification_flask.utilities.settings import AuthySettings
_37
from authy.api import AuthyApiClient
_37
_37
_37
class AuthyServices:
_37
authy_client = None
_37
_37
def __init__(self):
_37
if AuthyServices.authy_client is None:
_37
AuthyServices.authy_client = AuthyApiClient(AuthySettings.key())
_37
_37
def request_phone_confirmation_code(self, user):
_37
if user is None:
_37
raise ValueError(account_verification_flask.utilities.User_Id_Not_Found)
_37
_37
if user.authy_user_id is None:
_37
self._register_user_under_authy(user)
_37
_37
sms = self.authy_client.users.request_sms(user.authy_user_id, {'force': True})
_37
return not sms.ignored()
_37
_37
def confirm_phone_number(self, user, verification_code):
_37
if user is None:
_37
raise ValueError(account_verification_flask.utilities.User_Id_Not_Found)
_37
_37
verification = self.authy_client.tokens.verify(
_37
user.authy_user_id, verification_code
_37
)
_37
return verification.ok()
_37
_37
def _register_user_under_authy(self, user):
_37
authy_user = self.authy_client.users.create(
_37
user.email, user.phone_number, user.country_code
_37
)
_37
if authy_user.ok:
_37
user.authy_user_id = authy_user.id

When our user is created successfully via the form we have implemented, we send a token to the user's mobile phone asking them to verify their account in our controller. When the code is sent, we redirect our users to another page where they can enter the received token, completing the verification process.


Authy provides us with a tokens.verify method that allows us to pass a user id and token. In this case we just need to check that the API request was successful and, if so, set a verified flag on the user.

Verify a user's token with AuthyServices

verify-a-users-token-with-authyservices page anchor

account_verification_flask/services/authy_services.py


_37
import account_verification_flask.utilities
_37
from account_verification_flask.utilities.settings import AuthySettings
_37
from authy.api import AuthyApiClient
_37
_37
_37
class AuthyServices:
_37
authy_client = None
_37
_37
def __init__(self):
_37
if AuthyServices.authy_client is None:
_37
AuthyServices.authy_client = AuthyApiClient(AuthySettings.key())
_37
_37
def request_phone_confirmation_code(self, user):
_37
if user is None:
_37
raise ValueError(account_verification_flask.utilities.User_Id_Not_Found)
_37
_37
if user.authy_user_id is None:
_37
self._register_user_under_authy(user)
_37
_37
sms = self.authy_client.users.request_sms(user.authy_user_id, {'force': True})
_37
return not sms.ignored()
_37
_37
def confirm_phone_number(self, user, verification_code):
_37
if user is None:
_37
raise ValueError(account_verification_flask.utilities.User_Id_Not_Found)
_37
_37
verification = self.authy_client.tokens.verify(
_37
user.authy_user_id, verification_code
_37
)
_37
return verification.ok()
_37
_37
def _register_user_under_authy(self, user):
_37
authy_user = self.authy_client.users.create(
_37
user.email, user.phone_number, user.country_code
_37
)
_37
if authy_user.ok:
_37
user.authy_user_id = authy_user.id

That's it for token verification! Let's provide a nice user onboarding experience, and send a confirmation message to our new user.


Sending the Confirmation Message

sending-the-confirmation-message page anchor

Just as we did for our Authy client, we create a single instance of the Twilio REST API helper. It will be called twilio_client in this example.

After that, it's straightforward - send an SMS using the Twilio Python helper library to the same number we used in messages.create().

Send a registration success SMS from the TwilioServices class

send-a-registration-success-sms-from-the-twilioservices-class page anchor

account_verification_flask/services/twilio_services.py


_22
import account_verification_flask.utilities
_22
from account_verification_flask.utilities.settings import TwilioSettings
_22
from twilio.rest import Client
_22
_22
_22
class TwilioServices:
_22
twilio_client = None
_22
_22
def __init__(self):
_22
if TwilioServices.twilio_client is None:
_22
TwilioServices.twilio_client = Client(
_22
TwilioSettings.api_key(),
_22
TwilioSettings.api_secret(),
_22
TwilioSettings.account_sid(),
_22
)
_22
_22
def send_registration_success_sms(self, to_number):
_22
self.twilio_client.messages.create(
_22
body=account_verification_flask.utilities.Signup_Complete,
_22
to=to_number,
_22
from_=TwilioSettings.phone_number(),
_22
)

Congratulations! You've successfully verified new user accounts with Authy. Where can we take it from here?


In one simple tutorial, we've implemented account verification with Authy and Twilio, allowing your users to confirm accounts with their phone number! Now it's on you - let us know what you build on Twitter(link takes you to an external page), and check out these other tutorials:

Appointment Reminders

Use Twilio to automate the process of reaching out to your customers in advance of an upcoming appointment.

Two-Factor Authentication with Authy

Use Twilio and Twilio-powered Authy OneTouch to implement two-factor authentication (2FA) in your web app.


Rate this page: