CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/pypi-django-otp

A pluggable framework for adding two-factor authentication to Django using one-time passwords.

Pending
Overview
Eval results
Files

totp-devices.mddocs/

TOTP Devices

Time-based one-time passwords (TOTP) following RFC 6238, typically used with authenticator apps like Google Authenticator, Authy, or 1Password. TOTP generates tokens based on the current time and a shared secret key.

Capabilities

TOTPDevice Model

The main model for TOTP-based OTP devices with built-in security features and drift compensation.

class TOTPDevice(TimestampMixin, ThrottlingMixin, Device):
    """
    Time-based OTP device following RFC 6238.
    
    Fields:
    - key: CharField(max_length=80) - Hex-encoded secret key
    - step: PositiveSmallIntegerField(default=30) - Time step in seconds
    - t0: BigIntegerField(default=0) - Unix time to start counting
    - digits: PositiveSmallIntegerField - Number of digits (6 or 8)
    - tolerance: PositiveSmallIntegerField(default=1) - Time step tolerance
    - drift: SmallIntegerField(default=0) - Clock drift compensation
    - last_t: BigIntegerField(default=-1) - Last verified time step
    """
    
    key = models.CharField(max_length=80, validators=[hex_validator()])
    step = models.PositiveSmallIntegerField(default=30)
    t0 = models.BigIntegerField(default=0)
    digits = models.PositiveSmallIntegerField(choices=[(6, 6), (8, 8)], default=6)
    tolerance = models.PositiveSmallIntegerField(default=1)
    drift = models.SmallIntegerField(default=0)
    last_t = models.BigIntegerField(default=-1)
    
    @property
    def bin_key(self) -> bytes:
        """Secret key as binary string."""
    
    @property
    def config_url(self) -> str:
        """Configuration URL for QR codes and authenticator apps."""
    
    def verify_token(self, token) -> bool:
        """
        Verify TOTP token with drift compensation.
        
        Parameters:
        - token: str - Token to verify
        
        Returns:
        bool - True if token is valid
        """
    
    def get_throttle_factor(self) -> int:
        """Return throttle factor from settings."""

Admin Interface

Admin interface for managing TOTP devices with QR code generation support.

class TOTPDeviceAdmin(admin.ModelAdmin):
    """Admin interface for TOTPDevice with QR code support."""
    
    def qrcode_link(self, device):
        """Generate QR code link for device configuration."""
    
    def config_view(self, request, pk):
        """Configuration view showing device setup information."""
    
    def qrcode_view(self, request, pk):
        """QR code image view for device setup."""

Usage Examples

Creating TOTP Devices

from django_otp.plugins.otp_totp.models import TOTPDevice
from django.contrib.auth.models import User

# Create a basic TOTP device
user = User.objects.get(username='alice')
device = TOTPDevice.objects.create(
    user=user,
    name='My Phone Authenticator',
    confirmed=False  # Set to True after user confirms setup
)

# Create device with custom settings
device = TOTPDevice.objects.create(
    user=user,
    name='High Security Device',
    digits=8,  # 8-digit tokens instead of 6
    step=60,   # 60-second time step instead of 30
    tolerance=2  # Allow 2 time steps of tolerance
)

print(f"Device key: {device.key}")
print(f"Setup URL: {device.config_url}")

QR Code Generation

from django_otp.qr import write_qrcode_image
from io import BytesIO

def generate_qr_code_for_device(device):
    """Generate QR code image for TOTP device setup."""
    
    # Create QR code data
    qr_data = device.config_url
    
    # Generate QR code image
    img_buffer = BytesIO()
    write_qrcode_image(qr_data, img_buffer)
    img_buffer.seek(0)
    
    return img_buffer.getvalue()

# Usage
device = TOTPDevice.objects.get(pk=1)
qr_image_data = generate_qr_code_for_device(device)

# Save to file or return in HTTP response
with open('qr_code.png', 'wb') as f:
    f.write(qr_image_data)

Token Verification

from django_otp.plugins.otp_totp.models import TOTPDevice

def verify_totp_token(user, token):
    """Verify TOTP token for user's device."""
    
    # Get user's TOTP devices
    devices = TOTPDevice.objects.filter(user=user, confirmed=True)
    
    for device in devices:
        if device.verify_token(token):
            print(f"Token verified with device: {device.name}")
            print(f"Device drift: {device.drift}")
            return device
    
    return None

# Usage
user = User.objects.get(username='alice')
device = verify_totp_token(user, '123456')

Device Configuration URLs

from django_otp.plugins.otp_totp.models import TOTPDevice

def get_device_setup_info(device):
    """Get device setup information for user."""
    
    info = {
        'device_name': device.name,
        'config_url': device.config_url,
        'manual_setup': {
            'secret_key': device.key,
            'digits': device.digits,
            'step': device.step,
            'algorithm': 'SHA1'
        },
        'qr_code_data': device.config_url
    }
    
    return info

# Usage
device = TOTPDevice.objects.get(pk=1)
setup_info = get_device_setup_info(device)

# The config_url can be used in QR codes or manual setup
print(f"QR Code URL: {setup_info['config_url']}")
print(f"Manual key: {setup_info['manual_setup']['secret_key']}")

Drift Compensation

from django_otp.plugins.otp_totp.models import TOTPDevice
from django_otp.oath import TOTP

def check_device_drift(device):
    """Check and display device drift information."""
    
    totp = TOTP(device.bin_key, device.step, device.t0, device.digits)
    current_t = totp.t()
    
    info = {
        'device_name': device.name,
        'current_drift': device.drift,
        'last_verified_t': device.last_t,
        'current_t': current_t,
        'time_difference_seconds': (current_t - device.last_t) * device.step if device.last_t >= 0 else None
    }
    
    return info

# Usage
device = TOTPDevice.objects.get(pk=1)
drift_info = check_device_drift(device)
print(f"Current drift: {drift_info['current_drift']} time steps")

Bulk Device Management

from django_otp.plugins.otp_totp.models import TOTPDevice
from django.contrib.auth.models import User

def setup_totp_for_users(usernames, device_name="Default TOTP"):
    """Set up TOTP devices for multiple users."""
    
    devices = []
    for username in usernames:
        try:
            user = User.objects.get(username=username)
            device = TOTPDevice.objects.create(
                user=user,
                name=device_name,
                confirmed=False
            )
            devices.append({
                'username': username,
                'device_id': device.pk,
                'setup_url': device.config_url,
                'key': device.key
            })
        except User.DoesNotExist:
            print(f"User {username} not found")
    
    return devices

# Usage
setup_info = setup_totp_for_users(['alice', 'bob', 'charlie'])
for info in setup_info:
    print(f"User: {info['username']}, Key: {info['key']}")

Configuration Settings

TOTP devices can be configured through Django settings:

# settings.py

# Enable drift synchronization (default: True)
OTP_TOTP_SYNC = True

# Throttling factor for failed verifications (default: 1)
OTP_TOTP_THROTTLE_FACTOR = 2

# Issuer name for QR codes (default: None)
OTP_TOTP_ISSUER = "My Company"

# Image URL for QR codes (default: None)
OTP_TOTP_IMAGE = "https://example.com/logo.png"

Security Considerations

Time Synchronization

TOTP relies on synchronized clocks between client and server. The tolerance field allows for some clock skew, and django-otp includes automatic drift compensation to handle gradual clock drift.

Throttling

TOTP devices inherit from ThrottlingMixin, which provides exponential backoff for failed verification attempts. Configure the throttle factor through OTP_TOTP_THROTTLE_FACTOR.

Key Management

Secret keys should be generated securely and stored safely. The keys are stored as hex-encoded strings in the database and should be backed up appropriately for disaster recovery.

Install with Tessl CLI

npx tessl i tessl/pypi-django-otp

docs

admin-interface.md

core-authentication.md

device-models.md

django-integration.md

email-devices.md

hotp-devices.md

index.md

oath-algorithms.md

static-tokens.md

totp-devices.md

tile.json