0
# TOTP Devices
1
2
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.
3
4
## Capabilities
5
6
### TOTPDevice Model
7
8
The main model for TOTP-based OTP devices with built-in security features and drift compensation.
9
10
```python { .api }
11
class TOTPDevice(TimestampMixin, ThrottlingMixin, Device):
12
"""
13
Time-based OTP device following RFC 6238.
14
15
Fields:
16
- key: CharField(max_length=80) - Hex-encoded secret key
17
- step: PositiveSmallIntegerField(default=30) - Time step in seconds
18
- t0: BigIntegerField(default=0) - Unix time to start counting
19
- digits: PositiveSmallIntegerField - Number of digits (6 or 8)
20
- tolerance: PositiveSmallIntegerField(default=1) - Time step tolerance
21
- drift: SmallIntegerField(default=0) - Clock drift compensation
22
- last_t: BigIntegerField(default=-1) - Last verified time step
23
"""
24
25
key = models.CharField(max_length=80, validators=[hex_validator()])
26
step = models.PositiveSmallIntegerField(default=30)
27
t0 = models.BigIntegerField(default=0)
28
digits = models.PositiveSmallIntegerField(choices=[(6, 6), (8, 8)], default=6)
29
tolerance = models.PositiveSmallIntegerField(default=1)
30
drift = models.SmallIntegerField(default=0)
31
last_t = models.BigIntegerField(default=-1)
32
33
@property
34
def bin_key(self) -> bytes:
35
"""Secret key as binary string."""
36
37
@property
38
def config_url(self) -> str:
39
"""Configuration URL for QR codes and authenticator apps."""
40
41
def verify_token(self, token) -> bool:
42
"""
43
Verify TOTP token with drift compensation.
44
45
Parameters:
46
- token: str - Token to verify
47
48
Returns:
49
bool - True if token is valid
50
"""
51
52
def get_throttle_factor(self) -> int:
53
"""Return throttle factor from settings."""
54
```
55
56
### Admin Interface
57
58
Admin interface for managing TOTP devices with QR code generation support.
59
60
```python { .api }
61
class TOTPDeviceAdmin(admin.ModelAdmin):
62
"""Admin interface for TOTPDevice with QR code support."""
63
64
def qrcode_link(self, device):
65
"""Generate QR code link for device configuration."""
66
67
def config_view(self, request, pk):
68
"""Configuration view showing device setup information."""
69
70
def qrcode_view(self, request, pk):
71
"""QR code image view for device setup."""
72
```
73
74
## Usage Examples
75
76
### Creating TOTP Devices
77
78
```python
79
from django_otp.plugins.otp_totp.models import TOTPDevice
80
from django.contrib.auth.models import User
81
82
# Create a basic TOTP device
83
user = User.objects.get(username='alice')
84
device = TOTPDevice.objects.create(
85
user=user,
86
name='My Phone Authenticator',
87
confirmed=False # Set to True after user confirms setup
88
)
89
90
# Create device with custom settings
91
device = TOTPDevice.objects.create(
92
user=user,
93
name='High Security Device',
94
digits=8, # 8-digit tokens instead of 6
95
step=60, # 60-second time step instead of 30
96
tolerance=2 # Allow 2 time steps of tolerance
97
)
98
99
print(f"Device key: {device.key}")
100
print(f"Setup URL: {device.config_url}")
101
```
102
103
### QR Code Generation
104
105
```python
106
from django_otp.qr import write_qrcode_image
107
from io import BytesIO
108
109
def generate_qr_code_for_device(device):
110
"""Generate QR code image for TOTP device setup."""
111
112
# Create QR code data
113
qr_data = device.config_url
114
115
# Generate QR code image
116
img_buffer = BytesIO()
117
write_qrcode_image(qr_data, img_buffer)
118
img_buffer.seek(0)
119
120
return img_buffer.getvalue()
121
122
# Usage
123
device = TOTPDevice.objects.get(pk=1)
124
qr_image_data = generate_qr_code_for_device(device)
125
126
# Save to file or return in HTTP response
127
with open('qr_code.png', 'wb') as f:
128
f.write(qr_image_data)
129
```
130
131
### Token Verification
132
133
```python
134
from django_otp.plugins.otp_totp.models import TOTPDevice
135
136
def verify_totp_token(user, token):
137
"""Verify TOTP token for user's device."""
138
139
# Get user's TOTP devices
140
devices = TOTPDevice.objects.filter(user=user, confirmed=True)
141
142
for device in devices:
143
if device.verify_token(token):
144
print(f"Token verified with device: {device.name}")
145
print(f"Device drift: {device.drift}")
146
return device
147
148
return None
149
150
# Usage
151
user = User.objects.get(username='alice')
152
device = verify_totp_token(user, '123456')
153
```
154
155
### Device Configuration URLs
156
157
```python
158
from django_otp.plugins.otp_totp.models import TOTPDevice
159
160
def get_device_setup_info(device):
161
"""Get device setup information for user."""
162
163
info = {
164
'device_name': device.name,
165
'config_url': device.config_url,
166
'manual_setup': {
167
'secret_key': device.key,
168
'digits': device.digits,
169
'step': device.step,
170
'algorithm': 'SHA1'
171
},
172
'qr_code_data': device.config_url
173
}
174
175
return info
176
177
# Usage
178
device = TOTPDevice.objects.get(pk=1)
179
setup_info = get_device_setup_info(device)
180
181
# The config_url can be used in QR codes or manual setup
182
print(f"QR Code URL: {setup_info['config_url']}")
183
print(f"Manual key: {setup_info['manual_setup']['secret_key']}")
184
```
185
186
### Drift Compensation
187
188
```python
189
from django_otp.plugins.otp_totp.models import TOTPDevice
190
from django_otp.oath import TOTP
191
192
def check_device_drift(device):
193
"""Check and display device drift information."""
194
195
totp = TOTP(device.bin_key, device.step, device.t0, device.digits)
196
current_t = totp.t()
197
198
info = {
199
'device_name': device.name,
200
'current_drift': device.drift,
201
'last_verified_t': device.last_t,
202
'current_t': current_t,
203
'time_difference_seconds': (current_t - device.last_t) * device.step if device.last_t >= 0 else None
204
}
205
206
return info
207
208
# Usage
209
device = TOTPDevice.objects.get(pk=1)
210
drift_info = check_device_drift(device)
211
print(f"Current drift: {drift_info['current_drift']} time steps")
212
```
213
214
### Bulk Device Management
215
216
```python
217
from django_otp.plugins.otp_totp.models import TOTPDevice
218
from django.contrib.auth.models import User
219
220
def setup_totp_for_users(usernames, device_name="Default TOTP"):
221
"""Set up TOTP devices for multiple users."""
222
223
devices = []
224
for username in usernames:
225
try:
226
user = User.objects.get(username=username)
227
device = TOTPDevice.objects.create(
228
user=user,
229
name=device_name,
230
confirmed=False
231
)
232
devices.append({
233
'username': username,
234
'device_id': device.pk,
235
'setup_url': device.config_url,
236
'key': device.key
237
})
238
except User.DoesNotExist:
239
print(f"User {username} not found")
240
241
return devices
242
243
# Usage
244
setup_info = setup_totp_for_users(['alice', 'bob', 'charlie'])
245
for info in setup_info:
246
print(f"User: {info['username']}, Key: {info['key']}")
247
```
248
249
## Configuration Settings
250
251
TOTP devices can be configured through Django settings:
252
253
```python
254
# settings.py
255
256
# Enable drift synchronization (default: True)
257
OTP_TOTP_SYNC = True
258
259
# Throttling factor for failed verifications (default: 1)
260
OTP_TOTP_THROTTLE_FACTOR = 2
261
262
# Issuer name for QR codes (default: None)
263
OTP_TOTP_ISSUER = "My Company"
264
265
# Image URL for QR codes (default: None)
266
OTP_TOTP_IMAGE = "https://example.com/logo.png"
267
```
268
269
## Security Considerations
270
271
### Time Synchronization
272
273
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.
274
275
### Throttling
276
277
TOTP devices inherit from `ThrottlingMixin`, which provides exponential backoff for failed verification attempts. Configure the throttle factor through `OTP_TOTP_THROTTLE_FACTOR`.
278
279
### Key Management
280
281
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.