A Django app to support phone number verification using a security code sent via SMS.
django-phone-verify provides a simple, secure way to verify phone numbers for user authentication, 2FA, account recovery, and more. It works seamlessly with Django and Django REST Framework, supports multiple SMS providers (Twilio, Nexmo/Vonage), and is fully extensible with custom backends.
📖 Full Documentation: https://django-phone-verify.readthedocs.io/
What It Does
django-phone-verify handles the complete phone verification flow:
Send Verification Code - User requests verification, receives SMS with security code
Verify Code - User submits code, system validates and confirms phone number
Session Management - Secure JWT-based session tokens prevent tampering
Multiple Use Cases - Registration, 2FA, password reset, marketing opt-in, and more
Key Features
Security & Flexibility
🔐 Secure verification flow - JWT session tokens, configurable code expiration, one-time use options
🔧 Highly customizable - Token length, expiration time, message templates, custom backends
🔒 Production-ready - Rate limiting support, security best practices, GDPR/CCPA compliance guidance
Easy Integration
🚀 Django REST Framework - Pre-built viewsets and serializers for instant API setup
🔌 Pluggable backends - Use Twilio, Nexmo/Vonage, or write your own (AWS SNS, MessageBird, etc.)
✅ Non-intrusive - Works with any AUTH_USER_MODEL, no database changes required to your user model
🧪 Sandbox mode - Test flows without sending real SMS messages
Multiple Use Cases
👤 User registration phone verification
🔑 Two-factor authentication (2FA)
🔄 Account recovery / password reset
📧 Marketing opt-in verification
📱 Phone number update flows
Installation
Install the package with all supported backends:
pip install django-phone-verify[all]
Or install with just the backend you need:
pip install django-phone-verify[twilio]
pip install django-phone-verify[nexmo]
Configuration
Add phone_verify to INSTALLED_APPS:
INSTALLED_APPS = [
...
"phone_verify",
...
]
Run migrations:
python manage.py migrate
Include the API URLs in your project’s urls.py:
from django.urls import path, include
urlpatterns = [
...
path("api/phone/", include("phone_verify.urls")),
...
]
Configure PHONE_VERIFICATION settings in your settings.py:
For Twilio:
PHONE_VERIFICATION = {
"BACKEND": "phone_verify.backends.twilio.TwilioBackend",
"OPTIONS": {
"SID": "your-twilio-account-sid",
"SECRET": "your-twilio-auth-token",
"FROM": "+1234567890", # Your Twilio phone number
"SANDBOX_TOKEN": "123456", # Optional: for testing without sending real SMS
},
"TOKEN_LENGTH": 6,
"MESSAGE": "Welcome to {app}! Please use security code {security_code} to proceed.",
"APP_NAME": "Phone Verify",
"SECURITY_CODE_EXPIRATION_SECONDS": 3600, # in seconds
"VERIFY_SECURITY_CODE_ONLY_ONCE": False,
}
For Nexmo (Vonage):
PHONE_VERIFICATION = {
"BACKEND": "phone_verify.backends.nexmo.NexmoBackend",
"OPTIONS": {
"KEY": "your-nexmo-api-key",
"SECRET": "your-nexmo-api-secret",
"FROM": "YourApp", # Sender ID
"SANDBOX_TOKEN": "123456", # Optional: for testing
},
"TOKEN_LENGTH": 6,
"MESSAGE": "Welcome to {app}! Please use security code {security_code} to proceed.",
"APP_NAME": "Phone Verify",
"SECURITY_CODE_EXPIRATION_SECONDS": 3600,
"VERIFY_SECURITY_CODE_ONLY_ONCE": False,
}
Quick Start
Step 1: Send verification code
curl -X POST http://localhost:8000/api/phone/register/ \
-H "Content-Type: application/json" \
-d '{"phone_number": "+1234567890"}'
# Response:
# {
# "session_token": "eyJ0eXAiOiJKV1QiLCJhbGc...",
# "phone_number": "+1234567890"
# }
Step 2: Verify the code
curl -X POST http://localhost:8000/api/phone/verify/ \
-H "Content-Type: application/json" \
-d '{
"phone_number": "+1234567890",
"security_code": "123456",
"session_token": "eyJ0eXAiOiJKV1QiLCJhbGc..."
}'
# Response:
# {
# "message": "Security code is valid",
# "phone_number": "+1234567890"
# }
Using in Python/Django code:
from phone_verify.services import send_security_code_and_generate_session_token
from phone_verify.services import verify_security_code
# Send verification code via SMS
session_token = send_security_code_and_generate_session_token(
phone_number="+1234567890"
)
# User receives SMS: "Welcome to Phone Verify! Please use security code 847291 to proceed."
# Verify the code user entered
try:
verify_security_code(
phone_number="+1234567890",
security_code="847291",
session_token=session_token
)
print("✓ Phone number verified successfully!")
except Exception as e:
print(f"✗ Verification failed: {e}")
Documentation
Full documentation is available at https://django-phone-verify.readthedocs.io/
Quick Links:
🚀 Advanced Examples (2FA, password reset, marketing opt-in)
Compatibility
Python 3.8+ (Python 3.7 and below are EOL)
Django 2.1+
Django REST Framework 3.9+
Contributing
Found a bug? Want to suggest an improvement or submit a patch?
We welcome contributions! Here’s how you can help:
🐛 Report bugs via GitHub Issues
💡 Suggest features or improvements
🔧 Submit pull requests - please check the contributing guide first
📖 Improve documentation
Before submitting a PR:
Write tests for new features
Ensure all tests pass: pytest
Follow the existing code style
Update documentation if needed
License
This project is licensed under the GPLv3 license.
Changelog
See the full changelog here: 📄 CHANGELOG.rst
Metadata
Release files for django-phone-verify 3.3.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| django_phone_verify-3.3.0.tar.gz | 9.7 MB | Without provenance | Details for django_phone_verify-3.3.0.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| django_phone_verify-3.3.0-py3-none-any.whl | Python 3 | none | any | Details for django_phone_verify-3.3.0-py3-none-any.whl |
Total release size: 9.7 MB
Release files / django_phone_verify-3.3.0.tar.gz
| Download URL | django_phone_verify-3.3.0.tar.gz |
|---|---|
| Size | 9.7 MB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
fb6e8df5fba8141b6674f1c7a18cd31faa622299218ecd5e2162d407a35bab8b
|
|
BLAKE2b-256 checksum How to use checksums |
d1980dc1067e43ee12cc5067bfa3d8c09bbf2908c83d5b1e2ca01f149f5ce968
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.10.9
|
Release files / django_phone_verify-3.3.0-py3-none-any.whl
| Download URL | django_phone_verify-3.3.0-py3-none-any.whl |
|---|---|
| Size | 34.1 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
ae5f46572e961961fde06271eb4a8aeecac265e292983eec092c00edea64fe64
|
|
BLAKE2b-256 checksum How to use checksums |
b7aa91af5167aac01949068750e8932ec6f05fb57d42362c35ac4849e46ee19d
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.10.9
|
Security for django-phone-verify 3.3.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.