0
# Multi-Factor Authentication
1
2
TOTP authenticators, WebAuthn hardware keys, recovery codes, and MFA management flows. Provides enterprise-grade security with support for multiple authentication factors including time-based codes, hardware security keys, and backup recovery codes.
3
4
## Capabilities
5
6
### Models
7
8
Core models for managing MFA authenticators, recovery codes, and authentication sessions.
9
10
```python { .api }
11
class Authenticator(models.Model):
12
"""
13
Base model for MFA authenticators (TOTP, WebAuthn, etc.).
14
"""
15
user: User = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
16
type: str = models.CharField(max_length=20)
17
data: dict = models.JSONField(default=dict)
18
created_at: datetime = models.DateTimeField(auto_now_add=True)
19
last_used_at: Optional[datetime] = models.DateTimeField(null=True, blank=True)
20
21
def __str__(self) -> str: ...
22
def record_usage(self) -> None: ...
23
24
class RecoveryCode(models.Model):
25
"""
26
Recovery codes for MFA account recovery.
27
"""
28
user: User = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
29
code: str = models.CharField(max_length=16)
30
used_at: Optional[datetime] = models.DateTimeField(null=True, blank=True)
31
32
def is_used(self) -> bool: ...
33
def use_code(self) -> None: ...
34
35
class AuthenticationSession(models.Model):
36
"""
37
Track MFA authentication sessions.
38
"""
39
user: User = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
40
created_at: datetime = models.DateTimeField(auto_now_add=True)
41
expires_at: datetime = models.DateTimeField()
42
token: str = models.CharField(max_length=255)
43
44
def is_expired(self) -> bool: ...
45
```
46
47
### TOTP (Time-based One-Time Passwords)
48
49
TOTP authenticator implementation using standard RFC 6238.
50
51
```python { .api }
52
class TOTPAuthenticator:
53
"""
54
Time-based One-Time Password authenticator.
55
"""
56
57
def __init__(self, authenticator: Authenticator): ...
58
59
def generate_qr_code(self, name: str = None) -> str:
60
"""
61
Generate QR code URL for TOTP setup.
62
63
Parameters:
64
- name: Optional name for the account
65
66
Returns:
67
QR code data URL
68
"""
69
70
def get_secret_key(self) -> str:
71
"""
72
Get the secret key for manual entry.
73
74
Returns:
75
Base32-encoded secret key
76
"""
77
78
def validate_token(self, token: str) -> bool:
79
"""
80
Validate TOTP token.
81
82
Parameters:
83
- token: 6-digit TOTP code
84
85
Returns:
86
True if token is valid
87
"""
88
89
@classmethod
90
def generate_secret(cls) -> str:
91
"""
92
Generate new TOTP secret.
93
94
Returns:
95
Base32-encoded secret
96
"""
97
98
def get_totp_key(user: User) -> str:
99
"""
100
Get or generate TOTP secret key for user.
101
102
Parameters:
103
- user: User instance
104
105
Returns:
106
TOTP secret key
107
"""
108
109
def validate_totp_token(user: User, token: str) -> bool:
110
"""
111
Validate TOTP token for user.
112
113
Parameters:
114
- user: User instance
115
- token: TOTP token to validate
116
117
Returns:
118
True if token is valid
119
"""
120
```
121
122
### WebAuthn (Hardware Security Keys)
123
124
WebAuthn implementation for hardware security keys and platform authenticators.
125
126
```python { .api }
127
class WebAuthnAuthenticator:
128
"""
129
WebAuthn hardware security key authenticator.
130
"""
131
132
def __init__(self, authenticator: Authenticator): ...
133
134
def get_credential_creation_options(self, request: HttpRequest) -> Dict[str, Any]:
135
"""
136
Get options for WebAuthn credential creation.
137
138
Parameters:
139
- request: HTTP request object
140
141
Returns:
142
WebAuthn creation options dictionary
143
"""
144
145
def verify_registration(self, request: HttpRequest, credential: Dict[str, Any]) -> bool:
146
"""
147
Verify WebAuthn registration.
148
149
Parameters:
150
- request: HTTP request object
151
- credential: WebAuthn credential data
152
153
Returns:
154
True if registration is valid
155
"""
156
157
def get_credential_request_options(self, request: HttpRequest) -> Dict[str, Any]:
158
"""
159
Get options for WebAuthn authentication.
160
161
Parameters:
162
- request: HTTP request object
163
164
Returns:
165
WebAuthn request options dictionary
166
"""
167
168
def verify_authentication(self, request: HttpRequest, credential: Dict[str, Any]) -> bool:
169
"""
170
Verify WebAuthn authentication.
171
172
Parameters:
173
- request: HTTP request object
174
- credential: WebAuthn credential data
175
176
Returns:
177
True if authentication is valid
178
"""
179
180
def get_webauthn_rp_id(request: HttpRequest) -> str:
181
"""
182
Get WebAuthn Relying Party ID from request.
183
184
Parameters:
185
- request: HTTP request object
186
187
Returns:
188
RP ID string
189
"""
190
191
def get_webauthn_origin(request: HttpRequest) -> str:
192
"""
193
Get WebAuthn origin from request.
194
195
Parameters:
196
- request: HTTP request object
197
198
Returns:
199
Origin URL string
200
"""
201
```
202
203
### Recovery Codes
204
205
Recovery code generation and management for MFA account recovery.
206
207
```python { .api }
208
def generate_recovery_codes(user: User, count: int = 10) -> List[str]:
209
"""
210
Generate recovery codes for user.
211
212
Parameters:
213
- user: User instance
214
- count: Number of codes to generate
215
216
Returns:
217
List of recovery code strings
218
"""
219
220
def validate_recovery_code(user: User, code: str) -> bool:
221
"""
222
Validate and consume recovery code.
223
224
Parameters:
225
- user: User instance
226
- code: Recovery code to validate
227
228
Returns:
229
True if code is valid and unused
230
"""
231
232
def get_unused_recovery_codes(user: User) -> List[RecoveryCode]:
233
"""
234
Get unused recovery codes for user.
235
236
Parameters:
237
- user: User instance
238
239
Returns:
240
List of unused RecoveryCode instances
241
"""
242
243
def regenerate_recovery_codes(user: User) -> List[str]:
244
"""
245
Regenerate all recovery codes for user.
246
247
Parameters:
248
- user: User instance
249
250
Returns:
251
List of new recovery code strings
252
"""
253
```
254
255
### Views
256
257
Django views for MFA setup, authentication, and management.
258
259
```python { .api }
260
class AuthenticateView(FormView):
261
"""
262
MFA authentication view.
263
"""
264
template_name: str = "mfa/authenticate.html"
265
form_class: type = AuthenticateForm
266
267
def get_form_kwargs(self) -> Dict[str, Any]: ...
268
def form_valid(self, form: AuthenticateForm) -> HttpResponse: ...
269
270
class TOTPAddView(FormView):
271
"""
272
Add TOTP authenticator view.
273
"""
274
template_name: str = "mfa/totp/add.html"
275
form_class: type = TOTPAddForm
276
277
def get_context_data(self, **kwargs) -> Dict[str, Any]: ...
278
def form_valid(self, form: TOTPAddForm) -> HttpResponse: ...
279
280
class TOTPRemoveView(FormView):
281
"""
282
Remove TOTP authenticator view.
283
"""
284
template_name: str = "mfa/totp/remove.html"
285
form_class: type = TOTPRemoveForm
286
287
class WebAuthnAddView(FormView):
288
"""
289
Add WebAuthn authenticator view.
290
"""
291
template_name: str = "mfa/webauthn/add.html"
292
form_class: type = WebAuthnAddForm
293
294
def get_context_data(self, **kwargs) -> Dict[str, Any]: ...
295
296
class RecoveryCodesView(TemplateView):
297
"""
298
Recovery codes management view.
299
"""
300
template_name: str = "mfa/recovery_codes/index.html"
301
302
def get_context_data(self, **kwargs) -> Dict[str, Any]: ...
303
304
class RecoveryCodesGenerateView(FormView):
305
"""
306
Generate new recovery codes view.
307
"""
308
template_name: str = "mfa/recovery_codes/generate.html"
309
form_class: type = GenerateRecoveryCodesForm
310
311
def form_valid(self, form: GenerateRecoveryCodesForm) -> HttpResponse: ...
312
```
313
314
### Forms
315
316
Django forms for MFA setup and authentication.
317
318
```python { .api }
319
class AuthenticateForm(forms.Form):
320
"""
321
MFA authentication form.
322
"""
323
code: str = forms.CharField(max_length=16)
324
325
def __init__(self, user: User, request: HttpRequest, *args, **kwargs): ...
326
def clean_code(self) -> str: ...
327
328
class TOTPAddForm(forms.Form):
329
"""
330
TOTP setup form.
331
"""
332
secret: str = forms.CharField(widget=forms.HiddenInput)
333
code: str = forms.CharField(max_length=6)
334
335
def __init__(self, user: User, *args, **kwargs): ...
336
def clean_code(self) -> str: ...
337
def save(self) -> Authenticator: ...
338
339
class TOTPRemoveForm(forms.Form):
340
"""
341
TOTP removal form.
342
"""
343
authenticator: int = forms.ModelChoiceField(queryset=None)
344
345
def __init__(self, user: User, *args, **kwargs): ...
346
def save(self) -> None: ...
347
348
class WebAuthnAddForm(forms.Form):
349
"""
350
WebAuthn setup form.
351
"""
352
name: str = forms.CharField(max_length=100)
353
credential: str = forms.CharField(widget=forms.HiddenInput)
354
355
def __init__(self, user: User, request: HttpRequest, *args, **kwargs): ...
356
def save(self) -> Authenticator: ...
357
358
class GenerateRecoveryCodesForm(forms.Form):
359
"""
360
Recovery code generation form.
361
"""
362
363
def __init__(self, user: User, *args, **kwargs): ...
364
def save(self) -> List[str]: ...
365
```
366
367
### Utilities
368
369
MFA utility functions and helpers.
370
371
```python { .api }
372
def is_mfa_enabled(user: User) -> bool:
373
"""
374
Check if user has MFA enabled.
375
376
Parameters:
377
- user: User instance
378
379
Returns:
380
True if user has any MFA authenticators
381
"""
382
383
def get_user_authenticators(user: User, authenticator_type: str = None) -> QuerySet:
384
"""
385
Get user's MFA authenticators.
386
387
Parameters:
388
- user: User instance
389
- authenticator_type: Optional filter by authenticator type
390
391
Returns:
392
QuerySet of Authenticator instances
393
"""
394
395
def create_authentication_session(user: User) -> AuthenticationSession:
396
"""
397
Create MFA authentication session.
398
399
Parameters:
400
- user: User instance
401
402
Returns:
403
AuthenticationSession instance
404
"""
405
406
def verify_authentication_session(token: str) -> Optional[AuthenticationSession]:
407
"""
408
Verify MFA authentication session token.
409
410
Parameters:
411
- token: Session token
412
413
Returns:
414
AuthenticationSession instance if valid, None otherwise
415
"""
416
```
417
418
## Usage Examples
419
420
### TOTP Setup
421
422
```python
423
from allauth.mfa.totp import TOTPAuthenticator, get_totp_key
424
from allauth.mfa.models import Authenticator
425
426
# Generate TOTP secret for user
427
secret = get_totp_key(user)
428
429
# Create authenticator
430
authenticator = Authenticator.objects.create(
431
user=user,
432
type='totp',
433
data={'secret': secret}
434
)
435
436
# Generate QR code for setup
437
totp_auth = TOTPAuthenticator(authenticator)
438
qr_code_url = totp_auth.generate_qr_code(f"{user.username}@example.com")
439
440
# Validate TOTP token
441
is_valid = totp_auth.validate_token('123456')
442
```
443
444
### WebAuthn Setup
445
446
```python
447
from allauth.mfa.webauthn import WebAuthnAuthenticator
448
from allauth.mfa.models import Authenticator
449
450
# Create WebAuthn authenticator
451
authenticator = Authenticator.objects.create(
452
user=user,
453
type='webauthn',
454
data={}
455
)
456
457
webauthn_auth = WebAuthnAuthenticator(authenticator)
458
459
# Get registration options (send to frontend)
460
creation_options = webauthn_auth.get_credential_creation_options(request)
461
462
# Verify registration (after credential creation)
463
credential_data = {
464
'id': 'credential_id',
465
'rawId': 'raw_credential_id',
466
'response': {
467
'attestationObject': 'attestation_data',
468
'clientDataJSON': 'client_data'
469
}
470
}
471
472
is_valid = webauthn_auth.verify_registration(request, credential_data)
473
```
474
475
### Recovery Codes
476
477
```python
478
from allauth.mfa.recovery_codes import (
479
generate_recovery_codes,
480
validate_recovery_code,
481
get_unused_recovery_codes
482
)
483
484
# Generate recovery codes
485
codes = generate_recovery_codes(user, count=10)
486
print("Recovery codes:", codes)
487
488
# Validate and use recovery code
489
is_valid = validate_recovery_code(user, 'ABC123DEF')
490
491
# Check remaining codes
492
remaining_codes = get_unused_recovery_codes(user)
493
print(f"Remaining codes: {len(remaining_codes)}")
494
```
495
496
### MFA Authentication Flow
497
498
```python
499
from allauth.mfa.utils import is_mfa_enabled, create_authentication_session
500
501
# Check if user has MFA enabled
502
if is_mfa_enabled(user):
503
# Create authentication session
504
session = create_authentication_session(user)
505
506
# Store session token (e.g., in session or redirect parameter)
507
request.session['mfa_session_token'] = session.token
508
509
# Redirect to MFA authentication page
510
return redirect('mfa_authenticate')
511
```
512
513
### Template Usage
514
515
```html
516
<!-- MFA status in user profile -->
517
{% if user.is_authenticated %}
518
{% load mfa_tags %}
519
520
<h3>Multi-Factor Authentication</h3>
521
{% if user|is_mfa_enabled %}
522
<p class="text-success">✓ MFA is enabled</p>
523
524
<!-- Show authenticators -->
525
{% for auth in user.authenticator_set.all %}
526
<div class="authenticator">
527
<strong>{{ auth.get_type_display }}</strong>
528
<small>Added {{ auth.created_at|date }}</small>
529
</div>
530
{% endfor %}
531
532
<a href="{% url 'mfa_recovery_codes' %}">View Recovery Codes</a>
533
{% else %}
534
<p class="text-warning">⚠ MFA is not enabled</p>
535
<a href="{% url 'mfa_add_totp' %}" class="btn btn-primary">Enable TOTP</a>
536
<a href="{% url 'mfa_add_webauthn' %}" class="btn btn-secondary">Add Security Key</a>
537
{% endif %}
538
{% endif %}
539
```
540
541
### Settings Configuration
542
543
```python
544
# In Django settings.py
545
546
# Enable MFA
547
INSTALLED_APPS = [
548
# ...
549
'allauth.mfa',
550
]
551
552
# MFA settings
553
MFA_TOTP_ISSUER = 'My Application'
554
MFA_RECOVERY_CODE_COUNT = 10
555
MFA_WEBAUTHN_RP_NAME = 'My Application'
556
557
# Require MFA for certain views
558
MFA_REQUIRED_FOR_STAFF = True
559
MFA_GRACE_PERIOD_SECONDS = 300 # 5 minutes
560
```