Django app for performing 2FA using Twilio Verify
Project description
Django 2FA using Twilio Verify
Prerequisites
- Python 3.6+
- Twilio account
django
2.29+twilio
7.8.2+phonenumbers
8.12.26+
Installation
Install using pip install django-twilio-2fa
.
If install fails, try pip install --upgrade pip
Add to your INSTALLED_APPS
:
INSTALLED_APPS = (
...
"django_twilio_2fa",
"django_widget_tweaks", # only required if using included templates
...
)
Add the project urls.py
:
urlpatterns = [
...
path(
"2fa/", # Can be changed to any path
include("django_twilio_2fa.urls")
),
]
Flow
A user should only enter the 2FA flow if:
- They need to register for 2FA
- They have no previous 2FA authentication
- Their previous 2FA authentication has expired and needs to be renewed
Users should enter the flow through the Start view. If they are not registered for 2FA, they will be redirected to the Register view. Otherwise, it is assumed their 2FA authentication has expired.
How and when to enter into the flow is determined outside the scope of this project. However, there is sample middleware in test_app/middleware.py
that can be used as a reference.
Start
This is the primary entrypoint into the 2FA flow.
If PHONE_NUMBER_CB
returns None
(indicating the user has not registered for 2FA), the user will be redirected to the Register view.
If only one method is allowed, the verification will be created using that method and user would be redirected to the Verify view. (The user would not see this screen.)
Otherwise, the user is presented with a choice of verification methods.
Template for this view: start.html
Register
If the user has not registered for 2FA and ALLOW_REGISTER
is True
, the user will be shown this screen to add a phone number to their account.
If the user has not registered, they will be redirected to the Start view.
If ALLOW_REGISTER
is False
and no phone number is available, the user is redirected to the Failed view.
If REGISTER_OPTIONAL
is True
, the user has the ability to skip 2FA registration. See that setting for more details.
Template for this view: register.html
REGISTER_OPTIONAL is False |
REGISTER_OPTIONAL is True |
Change
If the user has already registered and wants to change their phone number, this view is shown.
If ALLOW_CHANGE
or IS_VERIFIED
is False
, the user will be shown an error.
It is the exact same view as register except is_optional
is always False
.
Template for this view: change.html
Verify
Once the verification has been sent, the user will enter the code on this view. The copy changes based on what method was used.
An incorrect code will show an error message and allow the user to retry up to a total of 5 attempts.
If the user exhausts all attempts, the user will be redirected to the Failed view.
If the user has not received the verification, they can click on the "Haven't received the <>?" link. If it has been more than the specified time, the verification will be recreated. (See RETRY_TIME
.)
Template for this view: verify.html
Success
If no VERIFY_SUCCESS_URL
is defined, the user is redirected to this view upon a successful verification.
Template for this view: success.html
Failed
In case of a verification failure, the user is redirected to this view.
Depending on `RETRY, the user will be able to
Template for this view: failed.html
User can retry verification | User cannot retry verification |
Settings
All settings are prefixed with TWILIO_2FA_
.
Any of the settings can be a callback. However, settings ending with _CB
must be a callback. If a callback setting is defined but is not callable, a ValueError
will be thrown.
ACCOUNT_SID
Your Twilio account SID from the Twilio Console.
Note: You cannot use test credentials with Verify.
AUTH_TOKEN
Your Twilio auth token from the Twilio Console.
Note: You cannot use test credentials with Verify.
SERVICE_ID
Verify service ID from the Twilio Console.
ALLOWED_METHODS
A list of allowed methods. The method must be enabled in the Verify service you setup in the Twilio Console.
Available methods: sms
, call
, email
and whatsapp
. Note: email
requires a Sendgrid integration. Details can be found here https://www.twilio.com/docs/verify/email#create-an-email-template.
If this setting is None
or not set, all available methods will be presented to the end user.
PHONE_NUMBER_DEFAULT_REGION
The default region for phonenumbers
library. Typically, this is the country code, but the entire list can be found here.
Setting this allows users to not need to enter a country code with their phone number.
You can set this to None
to not have a default region.
Defaults to US
for the United States.
PHONE_NUMBER_ALLOWED_COUNTRIES
A list of country codes from which phone numbers are allowed to originate.
Defaults to ["US"]
.
PHONE_NUMBER_DISALLOWED_COUNTRIES
A list of country codes from which phone numbers are not allowed to originate. This can be used in conjunction with PHONE_NUMBER_ALLOWED_COUNTRIES
.
Defaults to []
.
PHONE_NUMBER_CARRIER_LOOKUP
Indicates whether the carrier information lookup should be performed via Twilio. (Note: carrier lookups may affect billing.)
Defaults to True
.
PHONE_NUMBER_ALLOWED_CARRIER_TYPES
A list of allowed carrier types.
Available types: voip
, landline
, and mobile
.
Defaults to ["mobile"]
.
OBFUSCATE
Indicates whether the phone number presented in the views should be obfuscated ((123) 456-7890
vs (XXX) XXX-7890
).
Defaults to True
.
ALLOW_REGISTER
Indicates whether users should be allowed to register their phone number if one does not already exist.
If this is False
and the user has no phone number, they will not be able to use 2FA.
Defaults to True
.
REGISTER_OPTIONAL
Indicates whether user registration can be skipped by a user.
If True
and the user clicks the Skip button, the user will be redirected to REGISTER_OPTIONAL_URL
.
Defaults to False
.
REGISTER_OPTIONAL_URL
URL to redirect user when skipping registration.
Defaults to javascript:history.back()
.
RETRY_TIME
Amount of time (in seconds) after the last delivery attempt to allow the user to reattempt delivery of the verification.
Twilio does not have a limit on the amount of time between retries.
Defaults to 180 seconds or 3 minutes.
VERIFY_SUCCESS_URL
The URL to redirect users to after a successful verification. This should not return a Response
instead (like HttpResponseRedirect
) and should only return the URL as a string.
Defaults to reverse_lazy("twilio_2fa:success")
SERVICE_NAME
Overrides the Verify service's friendly name set in the Twilio Console.
Arguments sent if callable:
user
: User instancerequest
:Request
instancemethod
: Method stringphone_number
: Phone number string
Defaults to None
(no override).
ALLOW_CHANGE
Indicates if a user can change the phone number associated with their 2FA.
Defaults to True
.
MAX_ATTEMPTS
The maximum number of attempts the user has to successfully verify.
Arguments sent if callable:
user
: User instance
Defaults to 5
.
MAX_ATTEMPTS_TIMEOUT
Amount of time (in seconds) before the user will be able to attempt another verification after a maximum attempts failure.
A timed out user will be redirected to the Failure view.
If this setting is None
, 0
or False
, there will be no timeout and the user will be able to immediately try again.
Note: This timeout uses the sessions. It would be advisable to put this logic into a middleware and set this value to None.
Defaults to 600
seconds or 10 minutes.
ALLOW_USER_ERROR_REDIRECT
When ALLOW_USER_CB
returns False
, this setting returns a URL to which to redirect the user.
Arguments sent if callable:
user
: User instance
Defaults to /
.
ALLOW_USER_ERROR_MESSAGE
When ALLOW_USER_CB
returns False
, this setting can return a message to add via Django Messages.
Arguments sent if callable:
user
: User instance
Defaults to You cannot verify using 2FA at this time.
.
REGISTER_CB
This callback is triggered when the user registers their phone number and should be used to update the user.
Arguments sent to this callback:
user
: User instancephone_number
: Phone number string
No return is expected with this callback.
PHONE_NUMBER_CB
This callback is triggered on each verification request and should return the user's phone number.
Arguments sent to this callback:
user
: User instance
Expected return of this callback:
- String of the user's phone number
METHOD_DISPLAY_CB
This callback is triggered on each request and allows customization of the method icon and label.
Arguments sent to this callback:
method
: Method string
Expected return of this callback is a dict
with one or more of the following values (the default will be used if not included):
icon
: Font Awesome 5 icon classes (returnNone
to not use an icon)label
: Label string
TIMEOUT_CB
This callback is triggered on each verification request and should return the timeout timestamp for the current user as a DateTime
instance or None
if no timeout exists.
Arguments sent to this callback:
user
: User instance
ALLOW_USER_CHANGE_CB
This callback is triggered on the Change screen and should return whether the user is allowed to change their phone number.
Arguments sent to this callback:
user
: User instance
IS_VERIFIED_CB
This callback is triggered to determine if the authenticated user has been verified by 2FA.
Arguments sent to this callback:
request
: Request instance
Defaults to False
.
Signals
Signal names are prefixed twilio_2fa_
.
All signals are sent with at least the following arguments:
request
: CurrentRequest
instanceuser
:User
instancetwofa
: Instance ofTwoFA
The TwoFA
class has the following attributes:
method
: 2FA verification method chosen by userphone_number
: Phone number used for verification in E164 formattwilio_sid
: The SID for this verification instanceattempts
: Number of attempts to verify
rate_limited
This signal is triggered when a request to Twilio has been rate limited.
Additional arguments sent with this signal:
exc
:TwilioRestException
instance
verification_start
This signal is triggered when a verification is started (a GET
call to /start
). Should be used to clear any verification sessions.
verification_sent
This signal is triggered anytime a verification is sent.
Additional arguments sent with this signal:
timestamp
:DateTime
instance
verification_success
This signal is triggered when a user completes verification successfully.
The verification_status_changed
signal is also triggered during a successful verification.
verification_status_changed
This signal is triggered when the Twilio verification status is changed.
Options for status
: approved
and canceled
.
Additional arguments sent with this signal:
status
: Status verification was changed to
verification_failed
This signal is triggered when the Twilio verification attempt has failed.
verification_retries_exceeded
This signal is trigger when the number of failed attempts to verify exceeds MAX_ATTEMPTS
.
You should handle storing the timeout timestamp for retrieval by TIMEOUT_CB
.
Additional arguments sent with this signal:
timeout
: The value ofMAX_ATTEMPTS
timeout_until
:DateTime
instance of timeout
Customization
The presentation code uses Bootstrap 5, Font Awesome 5, and django-widget-tweaks. None are an absolute requirement and can be removed using custom templates or, in the case of Font Awesome, defining the METHOD_DISPLAY_CB
setting.
All templates are in the twilio_2fa
directory. To override these templates, you can put your version in your own twilio_2fa
directory anywhere your templates are stored.
_base.html
This is the primary template that all main templates extends.
It defines a single block for content: content
(outlined in yellow above). For django_widget_tweaks
, the content
block is wrapped by WIDGET_ERROR_CLASS
.
The header can also be changed using the header
block (outlined in red). Header icon classes changed using the header_icon_class
block (outlined in blue) and text changed using the header_text
block (outlined in green).
_messages.html
This template shows messages from django.contrib.messages
and is included in each of the main templates.
_form_errors.html
This template displays a form field's errors. field
should be passed in the context.
failed.html
This template is shown when the user's verification failed either from a timeout of the verification, maximum tries are exceeded, an API failure with Twilio, or other general error.
It conditionally allows users to retry verification based on the can_retry
session variable.
registration_form.html
This template shows the registration form to the user and serves as the base template for register.html
and change.html
.
register.html
This template shows the registration form to the user.
If ALLOW_REGISTRATION
is False
, the user is not shown this view and will be redirected to the failure page if no phone number is returned by PHONE_NUMBER_CB
.
It is based on registration_form.html
.
change.html
This template shows the change form to the user.
It is based on registration_form.html
.
start.html
This template allows the user to select the verification method.
If only one method exists, the user will not see this page.
success.html
This template is shown on a successful verification if VERIFY_SUCCESS_URL
is not set.
verify.html
This template shows the token form.
Development
Perform the following steps in the root directory:
- Create a virtual environment and activate.
- Install
django_twilio_2fa
:pip install -e .
Perform the following steps in the test_app
directory:
3. Update path to this package in the requirements.txt
4. Install requirements: pip install -r requirements.txt
.
5. Copy .env-sample
to .env
and update with your twilio settings.
6. Run migrations: python manage.py migrate
.
7. Run the server: python manage.py runserver
.
The test app should now be available at http://localtest.me:8000.
To-Do
InternationalizationE-mail verification- WhatsApp integration
- TOTP integration
- Push for web integration
- Abstraction for 2FA outside of web flow
Changelog
- 0.23 - Twilio rate limiting error handling
- 0.22 - Added internationalization and e-mail verification (thanks to jgoodsell-summitgrp)
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Hashes for django_twilio_2fa-0.23.1-py3-none-any.whl
Algorithm | Hash digest | |
---|---|---|
SHA256 | 3a813ad039d01585b28c3f0f82c6e5a10cd6f758200e294a3dbd376c94d0e3d4 |
|
MD5 | d8cb9bdb0ed862bfb493d6c1c6d8732b |
|
BLAKE2b-256 | 75765005f33892ed24e7d39d9cc319e7331bb6587cb8738866562b30eb7073a5 |