Architecture & Flow#

This document explains how django-phone-verify works internally and how the verification flow operates.

System Architecture#

django-phone-verify uses a layered architecture:

┌─────────────────────────────────────────────────────────┐
│                     Your Application                     │
│  (Views, ViewSets, Forms, Custom Logic)                 │
└────────────────────┬────────────────────────────────────┘
                     │
                     ▼
┌─────────────────────────────────────────────────────────┐
│              Phone Verify API Layer                      │
│  • VerificationViewSet (DRF)                            │
│  • PhoneSerializer, SMSVerificationSerializer           │
└────────────────────┬────────────────────────────────────┘
                     │
                     ▼
┌─────────────────────────────────────────────────────────┐
│              Service Layer                               │
│  • PhoneVerificationService                             │
│  • send_security_code_and_generate_session_token()      │
│  • verify_security_code()                               │
└────────────────────┬────────────────────────────────────┘
                     │
                     ▼
┌─────────────────────────────────────────────────────────┐
│              Backend Layer                               │
│  • BaseBackend (abstract)                               │
│  • TwilioBackend, NexmoBackend                          │
│  • Your Custom Backends                                 │
└────────────────────┬────────────────────────────────────┘
                     │
                     ▼
┌─────────────────────────────────────────────────────────┐
│              Data Layer                                  │
│  • SMSVerification Model (Database)                     │
│  • Stores: phone_number, security_code,                 │
│    session_token, created_at, is_verified               │
└─────────────────────────────────────────────────────────┘
                     │
                     ▼
┌─────────────────────────────────────────────────────────┐
│              External SMS Providers                      │
│  • Twilio API                                           │
│  • Nexmo/Vonage API                                     │
│  • Your Custom Provider                                 │
└─────────────────────────────────────────────────────────┘

Verification Flow#

The phone verification process happens in two main steps:

Step 1: Request Verification Code#

User                 Application           Phone Verify          Backend           SMS Provider
 │                        │                      │                   │                    │
 │  1. Submit Phone       │                      │                   │                    │
 ├──────────────────────►│                      │                   │                    │
 │                        │  2. Call Service     │                   │                    │
 │                        ├────────────────────►│                   │                    │
 │                        │                      │ 3. Generate Code  │                    │
 │                        │                      ├──────────────────►│                    │
 │                        │                      │                   │ 4. Send SMS        │
 │                        │                      │                   ├───────────────────►│
 │                        │                      │                   │                    │
 │                        │                      │ 5. Create Session │                    │
 │                        │                      │    Token (JWT)    │                    │
 │                        │                      │                   │                    │
 │                        │  6. Save to DB       │                   │                    │
 │                        │  (SMSVerification)   │                   │                    │
 │                        │                      │                   │                    │
 │  7. Return             │◄─────────────────────┤                   │                    │
 │     Session Token      │                      │                   │                    │
 │◄───────────────────────┤                      │                   │                    │
 │                        │                      │                   │                    │
 │  8. Receive SMS        │                      │                   │                    │
 │◄─────────────────────────────────────────────────────────────────────────────────────┤
 │                        │                      │                   │                    │

Details:

  1. User submits their phone number via API/form

  2. Application calls send_security_code_and_generate_session_token(phone_number)

  3. Backend generates a random security code (e.g., 6-digit number)

  4. Backend sends SMS via provider (Twilio/Nexmo)

  5. Service generates a JWT session token containing phone number + nonce

  6. Creates SMSVerification record in database with code and token

  7. Returns session_token to user

  8. User receives SMS with security code on their phone

Step 2: Verify Security Code#

User                 Application           Phone Verify          Backend           Database
 │                        │                      │                   │                 │
 │  1. Submit Code +      │                      │                   │                 │
 │     Session Token      │                      │                   │                 │
 ├──────────────────────►│                      │                   │                 │
 │                        │  2. Call Service     │                   │                 │
 │                        ├────────────────────►│                   │                 │
 │                        │                      │ 3. Validate Token │                 │
 │                        │                      │    (JWT verify)   │                 │
 │                        │                      │                   │                 │
 │                        │                      │ 4. Query DB       │                 │
 │                        │                      ├───────────────────────────────────►│
 │                        │                      │                   │                 │
 │                        │                      │ 5. Check:         │                 │
 │                        │                      │    - Code match?  │                 │
 │                        │                      │    - Expired?     │                 │
 │                        │                      │    - Already used?│                 │
 │                        │                      │                   │                 │
 │                        │  6. Mark as Verified │                   │                 │
 │                        │     (if valid)       ├───────────────────────────────────►│
 │                        │                      │                   │                 │
 │  7. Return Status      │◄─────────────────────┤                   │                 │
 │     (Valid/Invalid)    │                      │                   │                 │
 │◄───────────────────────┤                      │                   │                 │
 │                        │                      │                   │                 │

Details:

  1. User submits security code + session token from Step 1

  2. Application calls verify_security_code(phone_number, code, session_token)

  3. Service validates the JWT session token (checks signature, expiration)

  4. Queries database for matching SMSVerification record

  5. Backend validates: - Does the code match? - Has it expired (based on SECURITY_CODE_EXPIRATION_SECONDS)? - Has it already been used (if VERIFY_SECURITY_CODE_ONLY_ONCE is True)?

  6. If valid, marks record as verified in database

  7. Returns validation status (SECURITY_CODE_VALID or error)

Key Components#

1. SMSVerification Model#

Stores verification attempts in the database:

class SMSVerification:
    phone_number          # E.164 format phone number
    session_token         # JWT token for this verification
    security_code         # The code sent via SMS
    is_verified           # Has this been verified?
    created_at            # When was this created?

2. PhoneVerificationService#

Main service class that orchestrates verification:

class PhoneVerificationService:
    def send_verification(self, context=None)
        # Generates code, sends SMS, returns session token

    def verify(self, security_code, session_token)
        # Validates code and token, returns success/failure

3. Backend Classes#

Abstract interface for SMS providers:

class BaseBackend:
    def send_sms(number, message)              # Send single SMS
    def send_bulk_sms(numbers, message)        # Send bulk SMS
    def generate_security_code()               # Generate random code
    def generate_session_token(phone_number)   # Generate JWT token
    def validate_security_code(...)            # Validate code

Concrete implementations:

  • TwilioBackend - Uses Twilio API

  • NexmoBackend - Uses Nexmo/Vonage API

  • Your custom backends

Security Features#

JWT Session Tokens#

Session tokens are JWTs (JSON Web Tokens) containing:

  • phone_number: The phone being verified

  • nonce: Random value to ensure uniqueness

  • iat: Issued at timestamp

  • exp: Expiration timestamp

This prevents:

  • ✓ Token reuse across different phones

  • ✓ Token tampering (signatures are validated)

  • ✓ Replay attacks (nonces ensure uniqueness)

Code Expiration#

Security codes expire after SECURITY_CODE_EXPIRATION_SECONDS seconds (recommended: 300-600).

This limits the window for brute-force attacks.

One-Time Use#

When VERIFY_SECURITY_CODE_ONLY_ONCE is True, codes can only be used once, even if not expired.

This prevents code reuse attacks.

Database Schema#

The phone_verify_smsverification table structure:

CREATE TABLE phone_verify_smsverification (
    id               SERIAL PRIMARY KEY,
    phone_number     VARCHAR(15) NOT NULL,  -- E.164 format
    session_token    TEXT NOT NULL,         -- JWT token
    security_code    VARCHAR(120) NOT NULL, -- Hashed or plain code
    is_verified      BOOLEAN DEFAULT FALSE,
    created_at       TIMESTAMP DEFAULT NOW(),

    CONSTRAINT unique_phone_session
        UNIQUE (phone_number, session_token)
);

-- Index for fast lookups during verification
CREATE INDEX idx_phone_token
    ON phone_verify_smsverification(phone_number, session_token);

Configuration Flow#

Settings are loaded from PHONE_VERIFICATION in settings.py:

PHONE_VERIFICATION = {
    'BACKEND': 'phone_verify.backends.twilio.TwilioBackend',
    'OPTIONS': { ... },
    'TOKEN_LENGTH': 6,
    'MESSAGE': 'Code: {security_code}',
    'APP_NAME': 'MyApp',
    'SECURITY_CODE_EXPIRATION_SECONDS': 600,
    'VERIFY_SECURITY_CODE_ONLY_ONCE': True,
}

When the service is initialized:

  1. Backend class is imported (TwilioBackend)

  2. Backend is instantiated with OPTIONS

  3. Settings are validated for required fields

  4. Backend client (Twilio SDK) is initialized

Extension Points#

You can extend django-phone-verify at multiple levels:

  1. Custom Backends: Implement BaseBackend for new SMS providers

  2. Custom Viewsets: Extend VerificationViewSet for custom API logic

  3. Custom Services: Wrap PhoneVerificationService for custom flows

  4. Custom Messages: Override generate_message() for dynamic messages

  5. Custom Validation: Override validate_security_code() for custom rules

See Customization Guide and Advanced Examples for detailed examples.

Performance Considerations#

Bottlenecks#

  1. SMS API calls - Typically 100-500ms per SMS

  2. Database writes - Usually fast (<10ms) but can be a bottleneck at scale

  3. JWT generation/validation - Minimal overhead (<1ms)

Optimizations#

  1. Async SMS sending - Use Celery to send SMS in background

  2. Database connection pooling - Reuse connections

  3. Caching - Cache backend instances (they’re stateless)

  4. Bulk operations - Use send_bulk_sms() for multiple recipients

  5. Cleanup old records - Periodically delete old SMSVerification records

Monitoring & Observability#

Key Metrics to Track#

  • SMS success rate - % of SMS successfully delivered

  • Verification success rate - % of codes successfully verified

  • Time to verify - How long users take from code request to verification

  • Code expiration rate - % of codes that expire before being used

  • Failed attempts - Rate of failed verification attempts (indicates brute force?)

  • SMS costs - Total spending on SMS (track by provider)

Logging Best Practices#

# Enable debug logging
LOGGING = {
    'version': 1,
    'handlers': {
        'file': {
            'level': 'INFO',
            'class': 'logging.FileHandler',
            'filename': '/var/log/django/phone_verify.log',
        },
    },
    'loggers': {
        'phone_verify': {
            'handlers': ['file'],
            'level': 'INFO',
        },
    },
}

Log important events:

  • SMS sent successfully

  • SMS failed to send (with error)

  • Verification attempts (success/failure)

  • Rate limit violations

  • Security code generation

Do not log: Phone numbers or security codes in plain text (GDPR/privacy).

Further Reading#