0
# Two-Factor Authentication
1
2
TOTP-based two-factor authentication, SMS support, recovery codes, backup authentication methods, and multi-factor security for enhanced account protection in Flask applications.
3
4
## Capabilities
5
6
### Two-Factor Authentication Forms
7
8
Forms for setting up and verifying two-factor authentication.
9
10
```python { .api }
11
class TwoFactorSetupForm(Form):
12
"""
13
Form for setting up two-factor authentication.
14
15
Fields:
16
- setup: Choice field for 2FA method selection
17
- phone: Phone number field (for SMS)
18
- submit: Submit button
19
"""
20
setup: SelectField
21
phone: StringField
22
submit: SubmitField
23
24
class TwoFactorVerifyCodeForm(Form):
25
"""
26
Form for verifying two-factor authentication codes.
27
28
Fields:
29
- code: Code verification field
30
- submit: Submit button
31
"""
32
code: StringField
33
submit: SubmitField
34
35
class TwoFactorRescueForm(Form):
36
"""
37
Form for two-factor authentication recovery.
38
39
Fields:
40
- help_setup: Choice field for recovery method
41
- submit: Submit button
42
"""
43
help_setup: SelectField
44
submit: SubmitField
45
46
class TwoFactorSelectForm(Form):
47
"""
48
Form for selecting two-factor authentication method.
49
50
Fields:
51
- which: Choice field for method selection
52
- submit: Submit button
53
"""
54
which: SelectField
55
submit: SubmitField
56
```
57
58
### Two-Factor Token Functions
59
60
Functions for sending and managing two-factor authentication tokens.
61
62
```python { .api }
63
def tf_send_security_token(user, method, phone_number=None, **kwargs):
64
"""
65
Send two-factor security token to user via specified method.
66
67
Parameters:
68
- user: User object to send token to
69
- method: Delivery method ('sms', 'google-authenticator', 'mail')
70
- phone_number: Phone number for SMS delivery (optional)
71
- kwargs: Additional method-specific parameters
72
73
Returns:
74
True if token sent successfully, False otherwise
75
"""
76
```
77
78
### TOTP (Time-based One-Time Password) Utility
79
80
Class for managing TOTP-based two-factor authentication using authenticator apps.
81
82
```python { .api }
83
class Totp:
84
"""
85
Time-based One-Time Password utility for authenticator app integration.
86
"""
87
88
def __init__(self, secrets=None):
89
"""
90
Initialize TOTP utility.
91
92
Parameters:
93
- secrets: Secret key for TOTP generation (optional)
94
"""
95
96
def generate_password(self, timestamp=None):
97
"""
98
Generate TOTP password for current time.
99
100
Parameters:
101
- timestamp: Custom timestamp (optional, defaults to current time)
102
103
Returns:
104
6-digit TOTP code as string
105
"""
106
107
def verify(self, token, window=0, timestamp=None):
108
"""
109
Verify TOTP token against expected value.
110
111
Parameters:
112
- token: TOTP token to verify
113
- window: Time window for verification (default: 0)
114
- timestamp: Custom timestamp (optional)
115
116
Returns:
117
True if token is valid, False otherwise
118
"""
119
120
def get_totp_uri(self, username, issuer_name=None):
121
"""
122
Generate TOTP URI for QR code generation.
123
124
Parameters:
125
- username: Username for TOTP account
126
- issuer_name: Name of issuing service (optional)
127
128
Returns:
129
TOTP URI string for QR code
130
"""
131
132
@property
133
def secrets(self):
134
"""Get TOTP secret key."""
135
136
@secrets.setter
137
def secrets(self, value):
138
"""Set TOTP secret key."""
139
```
140
141
### Recovery Codes Utility
142
143
Class for managing multi-factor recovery codes as backup authentication method.
144
145
```python { .api }
146
class MfRecoveryCodesUtil:
147
"""
148
Multi-factor recovery codes utility for backup authentication.
149
"""
150
151
def __init__(self, app=None):
152
"""Initialize recovery codes utility."""
153
154
def generate_codes(self, user, count=10):
155
"""
156
Generate recovery codes for user.
157
158
Parameters:
159
- user: User object to generate codes for
160
- count: Number of codes to generate (default: 10)
161
162
Returns:
163
List of recovery code strings
164
"""
165
166
def verify_code(self, user, code):
167
"""
168
Verify and consume recovery code.
169
170
Parameters:
171
- user: User object
172
- code: Recovery code to verify
173
174
Returns:
175
True if code is valid and consumed, False otherwise
176
"""
177
178
def get_remaining_codes(self, user):
179
"""
180
Get count of remaining unused recovery codes.
181
182
Parameters:
183
- user: User object
184
185
Returns:
186
Number of remaining recovery codes
187
"""
188
```
189
190
### Recovery Code Forms
191
192
Forms for managing recovery codes.
193
194
```python { .api }
195
class MfRecoveryCodesForm(Form):
196
"""
197
Form for generating and displaying recovery codes.
198
199
Fields:
200
- submit: Submit button for code generation
201
"""
202
submit: SubmitField
203
204
class MfRecoveryForm(Form):
205
"""
206
Form for recovery code authentication.
207
208
Fields:
209
- code: Recovery code field
210
- submit: Submit button
211
"""
212
code: StringField
213
submit: SubmitField
214
```
215
216
### Two-Factor Plugin System
217
218
Base classes for extending two-factor authentication with custom methods.
219
220
```python { .api }
221
class TfPluginBase:
222
"""
223
Base class for two-factor authentication plugins.
224
"""
225
226
def get_setup_form(self):
227
"""Return setup form for this 2FA method."""
228
229
def setup_tf(self, user, setup_data):
230
"""Setup two-factor authentication for user."""
231
232
def send_token(self, user):
233
"""Send authentication token to user."""
234
235
def verify_token(self, user, token):
236
"""Verify authentication token."""
237
238
def is_setup(self, user):
239
"""Check if 2FA is set up for user."""
240
241
def remove_tf(self, user):
242
"""Remove 2FA setup for user."""
243
244
class TfPlugin:
245
"""
246
Two-factor authentication plugin manager.
247
"""
248
249
def __init__(self, app=None):
250
"""Initialize plugin manager."""
251
252
def register_plugin(self, name, plugin_class):
253
"""Register a 2FA plugin."""
254
255
def get_plugin(self, name):
256
"""Get registered plugin by name."""
257
```
258
259
## Usage Examples
260
261
### Basic Two-Factor Setup
262
263
```python
264
# Enable two-factor authentication
265
app.config['SECURITY_TWO_FACTOR'] = True
266
app.config['SECURITY_TWO_FACTOR_REQUIRED'] = False # Optional by default
267
app.config['SECURITY_TWO_FACTOR_SMS_SERVICE'] = 'Dummy' # For development
268
269
security = Security(app, user_datastore)
270
```
271
272
### TOTP Authenticator App Setup
273
274
```python
275
from flask_security import Totp
276
277
@app.route('/setup-authenticator')
278
@login_required
279
def setup_authenticator():
280
if current_user.tf_totp_secret:
281
flash('Two-factor authentication is already set up')
282
return redirect('/profile')
283
284
# Generate new TOTP secret
285
totp = Totp()
286
current_user.tf_totp_secret = totp.secrets
287
288
# Generate QR code URI
289
qr_uri = totp.get_totp_uri(
290
username=current_user.email,
291
issuer_name='My App'
292
)
293
294
db.session.commit()
295
296
return render_template('setup_totp.html',
297
secret=totp.secrets,
298
qr_uri=qr_uri)
299
300
@app.route('/verify-totp', methods=['POST'])
301
@login_required
302
def verify_totp():
303
code = request.form.get('code')
304
305
if current_user.tf_totp_secret:
306
totp = Totp(current_user.tf_totp_secret)
307
if totp.verify(code, window=1):
308
current_user.tf_primary_method = 'authenticator'
309
db.session.commit()
310
flash('Two-factor authentication enabled successfully')
311
return redirect('/profile')
312
313
flash('Invalid verification code')
314
return redirect('/setup-authenticator')
315
```
316
317
### SMS Two-Factor Authentication
318
319
```python
320
from flask_security import tf_send_security_token
321
322
@app.route('/setup-sms-2fa', methods=['POST'])
323
@login_required
324
def setup_sms_2fa():
325
phone_number = request.form.get('phone')
326
327
# Validate and save phone number
328
current_user.tf_phone_number = phone_number
329
current_user.tf_primary_method = 'sms'
330
331
# Send verification code
332
if tf_send_security_token(current_user, 'sms', phone_number=phone_number):
333
flash('Verification code sent to your phone')
334
return redirect('/verify-sms-2fa')
335
else:
336
flash('Failed to send verification code')
337
return redirect('/setup-2fa')
338
339
@app.route('/verify-sms-2fa', methods=['POST'])
340
@login_required
341
def verify_sms_2fa():
342
code = request.form.get('code')
343
344
# Verify SMS code (implementation depends on SMS service)
345
if verify_sms_code(current_user, code):
346
db.session.commit()
347
flash('SMS two-factor authentication enabled')
348
return redirect('/profile')
349
else:
350
flash('Invalid verification code')
351
return redirect('/verify-sms-2fa')
352
```
353
354
### Recovery Codes Setup
355
356
```python
357
from flask_security import MfRecoveryCodesUtil
358
359
@app.route('/generate-recovery-codes')
360
@login_required
361
def generate_recovery_codes():
362
if not current_user.has_tf_setup():
363
flash('Set up two-factor authentication first')
364
return redirect('/setup-2fa')
365
366
recovery_util = MfRecoveryCodesUtil()
367
codes = recovery_util.generate_codes(current_user, count=10)
368
369
return render_template('recovery_codes.html', codes=codes)
370
371
@app.route('/verify-recovery-code', methods=['POST'])
372
def verify_recovery_code():
373
code = request.form.get('code')
374
user = get_user_from_session() # Get user being verified
375
376
recovery_util = MfRecoveryCodesUtil()
377
if recovery_util.verify_code(user, code):
378
# Log user in and mark as verified
379
login_user(user)
380
flash('Logged in using recovery code')
381
return redirect('/profile')
382
else:
383
flash('Invalid recovery code')
384
return redirect('/2fa-verify')
385
```
386
387
### Custom Two-Factor Plugin
388
389
```python
390
from flask_security import TfPluginBase
391
392
class EmailTfPlugin(TfPluginBase):
393
"""Custom email-based 2FA plugin."""
394
395
def setup_tf(self, user, setup_data):
396
"""Setup email 2FA for user."""
397
user.tf_email_enabled = True
398
return True
399
400
def send_token(self, user):
401
"""Send 2FA code via email."""
402
code = generate_random_code(6)
403
user.tf_email_code = code
404
user.tf_email_code_expires = datetime.utcnow() + timedelta(minutes=5)
405
406
send_mail(
407
subject='Your verification code',
408
recipient=user.email,
409
template='email_2fa_code.html',
410
code=code
411
)
412
return True
413
414
def verify_token(self, user, token):
415
"""Verify email 2FA code."""
416
if (user.tf_email_code == token and
417
user.tf_email_code_expires > datetime.utcnow()):
418
# Clear used code
419
user.tf_email_code = None
420
user.tf_email_code_expires = None
421
return True
422
return False
423
424
def is_setup(self, user):
425
"""Check if email 2FA is set up."""
426
return getattr(user, 'tf_email_enabled', False)
427
428
# Register custom plugin
429
tf_plugin = TfPlugin(app)
430
tf_plugin.register_plugin('email', EmailTfPlugin())
431
```
432
433
### Enforcing Two-Factor Authentication
434
435
```python
436
from flask_security import current_user
437
from functools import wraps
438
439
def tf_required(f):
440
"""Decorator to require two-factor authentication."""
441
@wraps(f)
442
def decorated_function(*args, **kwargs):
443
if not current_user.is_authenticated:
444
return redirect(url_for_security('login'))
445
446
if not current_user.has_tf_setup():
447
flash('Two-factor authentication is required')
448
return redirect('/setup-2fa')
449
450
if not session.get('tf_verified'):
451
return redirect('/2fa-verify')
452
453
return f(*args, **kwargs)
454
return decorated_function
455
456
@app.route('/admin-panel')
457
@tf_required
458
def admin_panel():
459
return render_template('admin.html')
460
```
461
462
### Two-Factor Authentication in API
463
464
```python
465
from flask_security import auth_required
466
467
@app.route('/api/sensitive-data')
468
@auth_required('token')
469
def sensitive_api_data():
470
# Check if user has 2FA enabled and verified
471
if current_user.has_tf_setup() and not session.get('tf_verified'):
472
return jsonify({
473
'error': 'Two-factor authentication required',
474
'tf_required': True,
475
'tf_methods': current_user.get_tf_methods()
476
}), 403
477
478
return jsonify({'data': 'sensitive information'})
479
480
@app.route('/api/verify-2fa', methods=['POST'])
481
@auth_required('token')
482
def api_verify_2fa():
483
code = request.json.get('code')
484
method = request.json.get('method', 'authenticator')
485
486
if method == 'authenticator' and current_user.tf_totp_secret:
487
totp = Totp(current_user.tf_totp_secret)
488
if totp.verify(code, window=1):
489
session['tf_verified'] = True
490
return jsonify({'success': True})
491
492
return jsonify({'error': 'Invalid verification code'}), 400
493
```
494
495
## Configuration Options
496
497
Key configuration variables for two-factor authentication:
498
499
```python
500
# Two-factor authentication
501
app.config['SECURITY_TWO_FACTOR'] = True
502
app.config['SECURITY_TWO_FACTOR_REQUIRED'] = False
503
app.config['SECURITY_TWO_FACTOR_SMS_SERVICE'] = 'Dummy' # or 'Twilio', etc.
504
505
# TOTP settings
506
app.config['SECURITY_TOTP_SECRETS'] = {'1': 'secret-key-here'}
507
app.config['SECURITY_TOTP_ISSUER'] = 'My Application'
508
509
# SMS settings
510
app.config['SECURITY_SMS_SERVICE'] = 'Dummy'
511
app.config['SECURITY_SMS_SERVICE_CONFIG'] = {}
512
513
# Recovery codes
514
app.config['SECURITY_RECOVERY_CODES'] = True
515
app.config['SECURITY_RECOVERY_CODES_N'] = 10
516
517
# Two-factor URLs
518
app.config['SECURITY_TWO_FACTOR_SETUP_URL'] = '/tf-setup'
519
app.config['SECURITY_TWO_FACTOR_TOKEN_VALIDATION_URL'] = '/tf-validate'
520
app.config['SECURITY_TWO_FACTOR_RESCUE_URL'] = '/tf-rescue'
521
```