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

static-tokens.mddocs/

Static Tokens

Pre-generated backup codes for emergency access when primary OTP devices are unavailable. Static tokens are single-use codes that provide reliable backup authentication.

Capabilities

StaticDevice Model

class StaticDevice(TimestampMixin, ThrottlingMixin, Device):
    """
    Static token device for backup/emergency codes.
    """
    
    def verify_token(self, token) -> bool:
        """
        Verify and consume static token.
        
        Parameters:
        - token: str - Static token to verify
        
        Returns:
        bool - True if token was valid and consumed
        """
    
    def get_throttle_factor(self) -> int:
        """Return throttle factor from settings."""

StaticToken Model

class StaticToken(models.Model):
    """
    Individual static token.
    
    Fields:
    - device: ForeignKey(StaticDevice) - Associated device
    - token: CharField(max_length=16) - Token value
    """
    
    device = models.ForeignKey(StaticDevice, on_delete=models.CASCADE)
    token = models.CharField(max_length=16)
    
    @staticmethod
    def random_token() -> str:
        """Generate random token string."""

Admin Interface

class StaticDeviceAdmin(admin.ModelAdmin):
    """Admin interface for StaticDevice."""

class StaticTokenInline(admin.TabularInline):
    """Inline admin for StaticToken."""

Library Functions

def add_static_token(username, token=None):
    """
    Add static token to user (used by management command).
    
    Parameters:
    - username: str - Username to add token for
    - token: str or None - Specific token value or None for random
    
    Returns:
    StaticToken - The created token instance
    """

Management Commands

# Django management command
python manage.py addstatictoken <username> [token]

Usage Examples

Creating Static Token Devices

from django_otp.plugins.otp_static.models import StaticDevice, StaticToken

# Create device with tokens
device = StaticDevice.objects.create(
    user=user,
    name='Backup Codes',
    confirmed=True
)

# Generate backup tokens
backup_codes = []
for i in range(10):  # Generate 10 backup codes
    token = StaticToken.objects.create(
        device=device,
        token=StaticToken.random_token()
    )
    backup_codes.append(token.token)

print("Your backup codes:")
for code in backup_codes:
    print(f"- {code}")

Token Verification and Consumption

def verify_static_token(user, token):
    """Verify static token and show remaining count."""
    
    try:
        device = StaticDevice.objects.get(user=user, confirmed=True)
        remaining_before = device.statictoken_set.count()
        
        if device.verify_token(token):
            remaining_after = device.statictoken_set.count()
            return {
                'valid': True,
                'remaining_tokens': remaining_after,
                'message': f"Token verified. {remaining_after} backup codes remaining."
            }
        else:
            return {
                'valid': False,
                'remaining_tokens': remaining_before,
                'message': "Invalid backup code."
            }
            
    except StaticDevice.DoesNotExist:
        return {
            'valid': False,
            'remaining_tokens': 0,
            'message': "No backup codes configured."
        }

# Usage
result = verify_static_token(user, 'abc123def456')
print(result['message'])

Bulk Token Management

def regenerate_backup_codes(user, count=10):
    """Regenerate all backup codes for a user."""
    
    try:
        device = StaticDevice.objects.get(user=user)
        
        # Remove existing tokens
        device.statictoken_set.all().delete()
        
        # Generate new tokens
        new_codes = []
        for i in range(count):
            token = StaticToken.objects.create(
                device=device,
                token=StaticToken.random_token()
            )
            new_codes.append(token.token)
        
        return new_codes
        
    except StaticDevice.DoesNotExist:
        # Create new device if none exists
        device = StaticDevice.objects.create(
            user=user,
            name='Backup Codes',
            confirmed=True
        )
        return regenerate_backup_codes(user, count)

# Usage
new_backup_codes = regenerate_backup_codes(user)

Using Management Command

# Add random token for user
python manage.py addstatictoken alice

# Add specific token for user  
python manage.py addstatictoken alice mytoken123

# The command uses the add_static_token library function

Checking Remaining Tokens

def get_backup_code_info(user):
    """Get backup code information for user."""
    
    try:
        device = StaticDevice.objects.get(user=user, confirmed=True)
        tokens = device.statictoken_set.all()
        
        return {
            'has_backup_codes': True,
            'total_codes': tokens.count(),
            'codes': [token.token for token in tokens]  # Only for admin display
        }
        
    except StaticDevice.DoesNotExist:
        return {
            'has_backup_codes': False,
            'total_codes': 0,
            'codes': []
        }

# Usage (for admin/user dashboard)
info = get_backup_code_info(user)
print(f"User has {info['total_codes']} backup codes remaining")

Configuration Settings

# settings.py

# Throttling factor for failed attempts (default: 1)
OTP_STATIC_THROTTLE_FACTOR = 2

Security Considerations

Single Use

Static tokens are consumed immediately upon successful verification and cannot be reused. This prevents replay attacks but means users need multiple backup codes.

Storage

Backup codes should be stored securely by users (password manager, secure physical storage) and never transmitted over insecure channels.

Regeneration

Consider implementing a policy for periodic regeneration of backup codes, especially after any security incidents or when codes are running low.

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