Quickly add security features to your Flask application.
—
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Pending
The risk profile of this skill
TOTP-based two-factor authentication, SMS support, recovery codes, backup authentication methods, and multi-factor security for enhanced account protection in Flask applications.
Forms for setting up and verifying two-factor authentication.
class TwoFactorSetupForm(Form):
"""
Form for setting up two-factor authentication.
Fields:
- setup: Choice field for 2FA method selection
- phone: Phone number field (for SMS)
- submit: Submit button
"""
setup: SelectField
phone: StringField
submit: SubmitField
class TwoFactorVerifyCodeForm(Form):
"""
Form for verifying two-factor authentication codes.
Fields:
- code: Code verification field
- submit: Submit button
"""
code: StringField
submit: SubmitField
class TwoFactorRescueForm(Form):
"""
Form for two-factor authentication recovery.
Fields:
- help_setup: Choice field for recovery method
- submit: Submit button
"""
help_setup: SelectField
submit: SubmitField
class TwoFactorSelectForm(Form):
"""
Form for selecting two-factor authentication method.
Fields:
- which: Choice field for method selection
- submit: Submit button
"""
which: SelectField
submit: SubmitFieldFunctions for sending and managing two-factor authentication tokens.
def tf_send_security_token(user, method, phone_number=None, **kwargs):
"""
Send two-factor security token to user via specified method.
Parameters:
- user: User object to send token to
- method: Delivery method ('sms', 'google-authenticator', 'mail')
- phone_number: Phone number for SMS delivery (optional)
- kwargs: Additional method-specific parameters
Returns:
True if token sent successfully, False otherwise
"""Class for managing TOTP-based two-factor authentication using authenticator apps.
class Totp:
"""
Time-based One-Time Password utility for authenticator app integration.
"""
def __init__(self, secrets=None):
"""
Initialize TOTP utility.
Parameters:
- secrets: Secret key for TOTP generation (optional)
"""
def generate_password(self, timestamp=None):
"""
Generate TOTP password for current time.
Parameters:
- timestamp: Custom timestamp (optional, defaults to current time)
Returns:
6-digit TOTP code as string
"""
def verify(self, token, window=0, timestamp=None):
"""
Verify TOTP token against expected value.
Parameters:
- token: TOTP token to verify
- window: Time window for verification (default: 0)
- timestamp: Custom timestamp (optional)
Returns:
True if token is valid, False otherwise
"""
def get_totp_uri(self, username, issuer_name=None):
"""
Generate TOTP URI for QR code generation.
Parameters:
- username: Username for TOTP account
- issuer_name: Name of issuing service (optional)
Returns:
TOTP URI string for QR code
"""
@property
def secrets(self):
"""Get TOTP secret key."""
@secrets.setter
def secrets(self, value):
"""Set TOTP secret key."""Class for managing multi-factor recovery codes as backup authentication method.
class MfRecoveryCodesUtil:
"""
Multi-factor recovery codes utility for backup authentication.
"""
def __init__(self, app=None):
"""Initialize recovery codes utility."""
def generate_codes(self, user, count=10):
"""
Generate recovery codes for user.
Parameters:
- user: User object to generate codes for
- count: Number of codes to generate (default: 10)
Returns:
List of recovery code strings
"""
def verify_code(self, user, code):
"""
Verify and consume recovery code.
Parameters:
- user: User object
- code: Recovery code to verify
Returns:
True if code is valid and consumed, False otherwise
"""
def get_remaining_codes(self, user):
"""
Get count of remaining unused recovery codes.
Parameters:
- user: User object
Returns:
Number of remaining recovery codes
"""Forms for managing recovery codes.
class MfRecoveryCodesForm(Form):
"""
Form for generating and displaying recovery codes.
Fields:
- submit: Submit button for code generation
"""
submit: SubmitField
class MfRecoveryForm(Form):
"""
Form for recovery code authentication.
Fields:
- code: Recovery code field
- submit: Submit button
"""
code: StringField
submit: SubmitFieldBase classes for extending two-factor authentication with custom methods.
class TfPluginBase:
"""
Base class for two-factor authentication plugins.
"""
def get_setup_form(self):
"""Return setup form for this 2FA method."""
def setup_tf(self, user, setup_data):
"""Setup two-factor authentication for user."""
def send_token(self, user):
"""Send authentication token to user."""
def verify_token(self, user, token):
"""Verify authentication token."""
def is_setup(self, user):
"""Check if 2FA is set up for user."""
def remove_tf(self, user):
"""Remove 2FA setup for user."""
class TfPlugin:
"""
Two-factor authentication plugin manager.
"""
def __init__(self, app=None):
"""Initialize plugin manager."""
def register_plugin(self, name, plugin_class):
"""Register a 2FA plugin."""
def get_plugin(self, name):
"""Get registered plugin by name."""# Enable two-factor authentication
app.config['SECURITY_TWO_FACTOR'] = True
app.config['SECURITY_TWO_FACTOR_REQUIRED'] = False # Optional by default
app.config['SECURITY_TWO_FACTOR_SMS_SERVICE'] = 'Dummy' # For development
security = Security(app, user_datastore)from flask_security import Totp
@app.route('/setup-authenticator')
@login_required
def setup_authenticator():
if current_user.tf_totp_secret:
flash('Two-factor authentication is already set up')
return redirect('/profile')
# Generate new TOTP secret
totp = Totp()
current_user.tf_totp_secret = totp.secrets
# Generate QR code URI
qr_uri = totp.get_totp_uri(
username=current_user.email,
issuer_name='My App'
)
db.session.commit()
return render_template('setup_totp.html',
secret=totp.secrets,
qr_uri=qr_uri)
@app.route('/verify-totp', methods=['POST'])
@login_required
def verify_totp():
code = request.form.get('code')
if current_user.tf_totp_secret:
totp = Totp(current_user.tf_totp_secret)
if totp.verify(code, window=1):
current_user.tf_primary_method = 'authenticator'
db.session.commit()
flash('Two-factor authentication enabled successfully')
return redirect('/profile')
flash('Invalid verification code')
return redirect('/setup-authenticator')from flask_security import tf_send_security_token
@app.route('/setup-sms-2fa', methods=['POST'])
@login_required
def setup_sms_2fa():
phone_number = request.form.get('phone')
# Validate and save phone number
current_user.tf_phone_number = phone_number
current_user.tf_primary_method = 'sms'
# Send verification code
if tf_send_security_token(current_user, 'sms', phone_number=phone_number):
flash('Verification code sent to your phone')
return redirect('/verify-sms-2fa')
else:
flash('Failed to send verification code')
return redirect('/setup-2fa')
@app.route('/verify-sms-2fa', methods=['POST'])
@login_required
def verify_sms_2fa():
code = request.form.get('code')
# Verify SMS code (implementation depends on SMS service)
if verify_sms_code(current_user, code):
db.session.commit()
flash('SMS two-factor authentication enabled')
return redirect('/profile')
else:
flash('Invalid verification code')
return redirect('/verify-sms-2fa')from flask_security import MfRecoveryCodesUtil
@app.route('/generate-recovery-codes')
@login_required
def generate_recovery_codes():
if not current_user.has_tf_setup():
flash('Set up two-factor authentication first')
return redirect('/setup-2fa')
recovery_util = MfRecoveryCodesUtil()
codes = recovery_util.generate_codes(current_user, count=10)
return render_template('recovery_codes.html', codes=codes)
@app.route('/verify-recovery-code', methods=['POST'])
def verify_recovery_code():
code = request.form.get('code')
user = get_user_from_session() # Get user being verified
recovery_util = MfRecoveryCodesUtil()
if recovery_util.verify_code(user, code):
# Log user in and mark as verified
login_user(user)
flash('Logged in using recovery code')
return redirect('/profile')
else:
flash('Invalid recovery code')
return redirect('/2fa-verify')from flask_security import TfPluginBase
class EmailTfPlugin(TfPluginBase):
"""Custom email-based 2FA plugin."""
def setup_tf(self, user, setup_data):
"""Setup email 2FA for user."""
user.tf_email_enabled = True
return True
def send_token(self, user):
"""Send 2FA code via email."""
code = generate_random_code(6)
user.tf_email_code = code
user.tf_email_code_expires = datetime.utcnow() + timedelta(minutes=5)
send_mail(
subject='Your verification code',
recipient=user.email,
template='email_2fa_code.html',
code=code
)
return True
def verify_token(self, user, token):
"""Verify email 2FA code."""
if (user.tf_email_code == token and
user.tf_email_code_expires > datetime.utcnow()):
# Clear used code
user.tf_email_code = None
user.tf_email_code_expires = None
return True
return False
def is_setup(self, user):
"""Check if email 2FA is set up."""
return getattr(user, 'tf_email_enabled', False)
# Register custom plugin
tf_plugin = TfPlugin(app)
tf_plugin.register_plugin('email', EmailTfPlugin())from flask_security import current_user
from functools import wraps
def tf_required(f):
"""Decorator to require two-factor authentication."""
@wraps(f)
def decorated_function(*args, **kwargs):
if not current_user.is_authenticated:
return redirect(url_for_security('login'))
if not current_user.has_tf_setup():
flash('Two-factor authentication is required')
return redirect('/setup-2fa')
if not session.get('tf_verified'):
return redirect('/2fa-verify')
return f(*args, **kwargs)
return decorated_function
@app.route('/admin-panel')
@tf_required
def admin_panel():
return render_template('admin.html')from flask_security import auth_required
@app.route('/api/sensitive-data')
@auth_required('token')
def sensitive_api_data():
# Check if user has 2FA enabled and verified
if current_user.has_tf_setup() and not session.get('tf_verified'):
return jsonify({
'error': 'Two-factor authentication required',
'tf_required': True,
'tf_methods': current_user.get_tf_methods()
}), 403
return jsonify({'data': 'sensitive information'})
@app.route('/api/verify-2fa', methods=['POST'])
@auth_required('token')
def api_verify_2fa():
code = request.json.get('code')
method = request.json.get('method', 'authenticator')
if method == 'authenticator' and current_user.tf_totp_secret:
totp = Totp(current_user.tf_totp_secret)
if totp.verify(code, window=1):
session['tf_verified'] = True
return jsonify({'success': True})
return jsonify({'error': 'Invalid verification code'}), 400Key configuration variables for two-factor authentication:
# Two-factor authentication
app.config['SECURITY_TWO_FACTOR'] = True
app.config['SECURITY_TWO_FACTOR_REQUIRED'] = False
app.config['SECURITY_TWO_FACTOR_SMS_SERVICE'] = 'Dummy' # or 'Twilio', etc.
# TOTP settings
app.config['SECURITY_TOTP_SECRETS'] = {'1': 'secret-key-here'}
app.config['SECURITY_TOTP_ISSUER'] = 'My Application'
# SMS settings
app.config['SECURITY_SMS_SERVICE'] = 'Dummy'
app.config['SECURITY_SMS_SERVICE_CONFIG'] = {}
# Recovery codes
app.config['SECURITY_RECOVERY_CODES'] = True
app.config['SECURITY_RECOVERY_CODES_N'] = 10
# Two-factor URLs
app.config['SECURITY_TWO_FACTOR_SETUP_URL'] = '/tf-setup'
app.config['SECURITY_TWO_FACTOR_TOKEN_VALIDATION_URL'] = '/tf-validate'
app.config['SECURITY_TWO_FACTOR_RESCUE_URL'] = '/tf-rescue'