A pluggable framework for adding two-factor authentication to Django using one-time passwords.
—
HMAC-based one-time passwords (HOTP) following RFC 4226, using a counter that increments with each successful verification. HOTP devices are useful when time synchronization is not reliable.
class HOTPDevice(TimestampMixin, ThrottlingMixin, Device):
"""
HMAC-based OTP device following RFC 4226.
Fields:
- key: CharField(max_length=80) - Hex-encoded secret key
- digits: PositiveSmallIntegerField - Number of digits (6 or 8)
- tolerance: PositiveSmallIntegerField(default=5) - Counter tolerance
- counter: BigIntegerField(default=0) - Next counter value
"""
key = models.CharField(max_length=80, validators=[hex_validator()])
digits = models.PositiveSmallIntegerField(choices=[(6, 6), (8, 8)], default=6)
tolerance = models.PositiveSmallIntegerField(default=5)
counter = models.BigIntegerField(default=0)
@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 HOTP token and advance counter.
Parameters:
- token: str - Token to verify
Returns:
bool - True if token is valid
"""
def get_throttle_factor(self) -> int:
"""Return throttle factor from settings."""class HOTPDeviceAdmin(admin.ModelAdmin):
"""Admin interface for HOTPDevice 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."""from django_otp.plugins.otp_hotp.models import HOTPDevice
# Create HOTP device
device = HOTPDevice.objects.create(
user=user,
name='Hardware Token',
tolerance=10, # Allow up to 10 counter values ahead
digits=8 # 8-digit tokens
)
print(f"Initial counter: {device.counter}")
print(f"Setup URL: {device.config_url}")def verify_hotp_with_info(device, token):
"""Verify HOTP token and return verification info."""
old_counter = device.counter
is_valid = device.verify_token(token)
return {
'valid': is_valid,
'old_counter': old_counter,
'new_counter': device.counter,
'counter_advanced': device.counter - old_counter
}
# Usage
result = verify_hotp_with_info(device, '123456')
if result['valid']:
print(f"Counter advanced by {result['counter_advanced']}")# settings.py
OTP_HOTP_THROTTLE_FACTOR = 1 # Throttling factor
OTP_HOTP_ISSUER = "My Company" # Issuer for QR codesInstall with Tessl CLI
npx tessl i tessl/pypi-django-otp