CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/pypi-flask-security

Quickly add security features to your Flask application.

Pending
Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

SecuritybySnyk

Pending

The risk profile of this skill

Overview
Eval results
Files

password-management.mddocs/

Password Management

Password hashing, validation, reset workflows, security utilities, and password policy enforcement for managing user credentials securely in Flask applications.

Capabilities

Password Hashing and Verification

Core functions for secure password storage and verification using modern hashing algorithms.

def hash_password(password):
    """
    Hash a password using the configured hashing algorithm.
    
    Parameters:
    - password: Plain text password to hash
    
    Returns:
    Hashed password string suitable for database storage
    """

def verify_password(password, password_hash):
    """
    Verify a password against its stored hash.
    
    Parameters:
    - password: Plain text password to verify
    - password_hash: Stored password hash
    
    Returns:
    True if password matches hash, False otherwise
    """

def verify_and_update_password(password, user):
    """
    Verify password and update hash if algorithm upgrade is needed.
    
    Parameters:
    - password: Plain text password to verify
    - user: User object with password hash
    
    Returns:
    True if password matches, False otherwise
    """

Password Change Forms

Forms for users to change their existing passwords.

class ChangePasswordForm(Form):
    """
    Form for users to change their current password.
    
    Fields:
    - password: Current password field for verification
    - new_password: New password field
    - new_password_confirm: New password confirmation field
    - submit: Submit button
    """
    password: PasswordField
    new_password: PasswordField
    new_password_confirm: PasswordField
    submit: SubmitField

Password Reset Forms

Forms for password recovery workflow when users forget their passwords.

class ForgotPasswordForm(Form):
    """
    Form for requesting password reset instructions.
    
    Fields:
    - user: Email or username field for identity lookup
    - submit: Submit button
    """
    user: StringField
    submit: SubmitField

class ResetPasswordForm(Form):
    """
    Form for setting new password during reset process.
    
    Fields:
    - password: New password field
    - password_confirm: New password confirmation field
    - submit: Submit button
    """
    password: PasswordField
    password_confirm: PasswordField
    submit: SubmitField

Password Reset Functions

Functions for managing the password reset workflow and token validation.

def send_reset_password_instructions(user):
    """
    Send password reset instructions via email.
    
    Parameters:
    - user: User object to send reset instructions to
    
    Returns:
    True if email sent successfully, False otherwise
    """

def send_password_reset_notice(user):
    """
    Send notification that password was reset (security notice).
    
    Parameters:
    - user: User object whose password was reset
    
    Returns:
    True if email sent successfully, False otherwise
    """

def generate_reset_password_token(user):
    """
    Generate password reset token for user.
    
    Parameters:
    - user: User object to generate token for
    
    Returns:
    Reset token string
    """

def reset_password_token_status(token):
    """
    Validate password reset token and return status information.
    
    Parameters:
    - token: Reset token to validate
    
    Returns:
    Tuple of (expired: bool, invalid: bool, user: User|None)
    """

def update_password(user, password):
    """
    Update user's password with proper hashing and validation.
    
    Parameters:
    - user: User object to update password for
    - password: New plain text password
    
    Returns:
    True if password updated successfully, False otherwise
    """

Administrative Password Functions

Functions for administrators to manage user passwords.

def admin_change_password(user, password, notify=True):
    """
    Administrative function to change user's password.
    
    Parameters:
    - user: User object whose password to change
    - password: New plain text password
    - notify: Whether to send notification email (default: True)
    
    Returns:
    True if password changed successfully, False otherwise
    """

Password Validation Functions

Functions for validating password strength and security requirements.

def password_length_validator(password, min_length=8, max_length=128):
    """
    Validate password length requirements.
    
    Parameters:
    - password: Password to validate
    - min_length: Minimum password length (default: 8)
    - max_length: Maximum password length (default: 128)
    
    Returns:
    True if valid, False otherwise
    
    Raises:
    ValidationError: If password length is invalid
    """

def password_complexity_validator(password):
    """
    Validate password complexity requirements.
    
    Parameters:
    - password: Password to validate
    
    Returns:
    True if password meets complexity requirements
    
    Raises:
    ValidationError: If password doesn't meet complexity requirements
    """

def password_breached_validator(password):
    """
    Check if password appears in known breach databases.
    
    Parameters:
    - password: Password to check
    
    Returns:
    True if password is safe to use
    
    Raises:
    ValidationError: If password is found in breach database
    """

def pwned(password):
    """
    Check if password appears in HaveIBeenPwned database.
    
    Parameters:
    - password: Password to check
    
    Returns:
    Number of times password appears in breaches (0 if safe)
    """

Password Utility Class

Utility class for advanced password management operations.

class PasswordUtil:
    """
    Utility class for password management operations.
    """
    
    def __init__(self, app=None):
        """Initialize password utility with Flask app."""
    
    def hash_password(self, password):
        """Hash password using configured algorithm."""
    
    def verify_password(self, password, password_hash):
        """Verify password against hash."""
    
    def generate_password(self, length=12, include_symbols=True):
        """Generate secure random password."""
    
    def check_password_strength(self, password):
        """Analyze password strength and return score."""

Usage Examples

Basic Password Change

from flask_security import current_user, hash_password

@app.route('/change-password', methods=['GET', 'POST'])
@login_required
def change_password():
    form = ChangePasswordForm()
    if form.validate_on_submit():
        if verify_password(form.password.data, current_user.password):
            current_user.password = hash_password(form.new_password.data)
            db.session.commit()
            flash('Password changed successfully')
            return redirect('/profile')
        else:
            flash('Current password is incorrect')
    
    return render_template('change_password.html', form=form)

Password Reset Workflow

from flask_security import send_reset_password_instructions, reset_password_token_status

@app.route('/forgot-password', methods=['GET', 'POST'])
def forgot_password():
    form = ForgotPasswordForm()
    if form.validate_on_submit():
        user = lookup_identity(form.user.data)
        if user:
            send_reset_password_instructions(user)
        # Always show success message for security
        flash('If your email is registered, you will receive reset instructions')
        return redirect('/login')
    
    return render_template('forgot_password.html', form=form)

@app.route('/reset-password/<token>', methods=['GET', 'POST'])
def reset_password(token):
    expired, invalid, user = reset_password_token_status(token)
    
    if expired:
        flash('The reset link has expired')
        return redirect('/forgot-password')
    
    if invalid or not user:
        flash('Invalid reset link')
        return redirect('/login')
    
    form = ResetPasswordForm()
    if form.validate_on_submit():
        update_password(user, form.password.data)
        db.session.commit()
        flash('Password reset successfully')
        return redirect('/login')
    
    return render_template('reset_password.html', form=form, token=token)

Administrative Password Management

from flask_security import admin_change_password

@app.route('/admin/reset-user-password/<int:user_id>', methods=['POST'])
@roles_required('admin')
def admin_reset_password(user_id):
    user = User.query.get_or_404(user_id)
    new_password = request.form.get('new_password')
    
    if admin_change_password(user, new_password, notify=True):
        db.session.commit()
        flash(f'Password reset for user {user.email}')
    else:
        flash('Failed to reset password')
    
    return redirect(f'/admin/user/{user_id}')

Password Strength Validation

from flask_security import password_length_validator, password_complexity_validator
from wtforms.validators import ValidationError

class StrongPasswordForm(Form):
    password = PasswordField('Password', validators=[
        DataRequired(),
        Length(min=12, message='Password must be at least 12 characters'),
        password_complexity_validator,
        password_breached_validator
    ])
    
    def validate_password(self, field):
        # Custom password validation
        password = field.data
        
        # Check for common patterns
        if password.lower() in ['password', '123456', 'qwerty']:
            raise ValidationError('Password is too common')
        
        # Require mixed case, numbers, and symbols
        has_upper = any(c.isupper() for c in password)
        has_lower = any(c.islower() for c in password)
        has_digit = any(c.isdigit() for c in password)
        has_symbol = any(c in '!@#$%^&*()' for c in password)
        
        if not all([has_upper, has_lower, has_digit, has_symbol]):
            raise ValidationError(
                'Password must contain uppercase, lowercase, numbers, and symbols'
            )

Password Breach Checking

from flask_security import pwned

@app.route('/check-password-safety', methods=['POST'])
@login_required
def check_password_safety():
    password = request.form.get('password')
    
    try:
        breach_count = pwned(password)
        if breach_count > 0:
            return jsonify({
                'safe': False,
                'message': f'Password found in {breach_count} data breaches',
                'recommendation': 'Choose a different password'
            })
        else:
            return jsonify({
                'safe': True,
                'message': 'Password not found in known breaches'
            })
    except Exception as e:
        return jsonify({
            'safe': None,
            'message': 'Unable to check password safety',
            'error': str(e)
        })

Custom Password Policy

from flask_security import Security
from passlib.context import CryptContext

# Custom password context with specific algorithms
pwd_context = CryptContext(
    schemes=['bcrypt', 'argon2'],
    default='argon2',
    bcrypt__min_rounds=12,
    argon2__time_cost=16,
    argon2__memory_cost=102400,
    argon2__parallelism=8
)

app.config['SECURITY_PASSWORD_HASH'] = 'argon2'
app.config['SECURITY_PASSWORD_LENGTH_MIN'] = 12
app.config['SECURITY_PASSWORD_COMPLEXITY_CHECKER'] = 'zxcvbn'
app.config['SECURITY_PASSWORD_CHECK_BREACHED'] = True
app.config['SECURITY_PASSWORD_BREACHED_COUNT'] = 1

security = Security(app, user_datastore)

Password History Prevention

class UserWithPasswordHistory(UserMixin, db.Model):
    id = db.Column(db.Integer, primary_key=True)
    email = db.Column(db.String(255), unique=True)
    password = db.Column(db.String(255))
    password_history = db.Column(db.Text)  # JSON field for password history
    
    def add_password_to_history(self, password_hash):
        """Add password hash to history."""
        history = json.loads(self.password_history or '[]')
        history.append({
            'hash': password_hash,
            'created_at': datetime.utcnow().isoformat()
        })
        # Keep only last 5 passwords
        self.password_history = json.dumps(history[-5:])
    
    def is_password_in_history(self, password):
        """Check if password was used previously."""
        history = json.loads(self.password_history or '[]')
        return any(
            verify_password(password, entry['hash']) 
            for entry in history
        )

# Custom password change with history check
@app.route('/secure-change-password', methods=['POST'])
@login_required
def secure_change_password():
    new_password = request.form.get('new_password')
    
    if current_user.is_password_in_history(new_password):
        flash('Cannot reuse a recent password')
        return redirect('/change-password')
    
    # Add current password to history before changing
    current_user.add_password_to_history(current_user.password)
    current_user.password = hash_password(new_password)
    db.session.commit()
    
    flash('Password changed successfully')
    return redirect('/profile')

Configuration Options

Key configuration variables for password management:

# Password hashing
app.config['SECURITY_PASSWORD_HASH'] = 'bcrypt'  # or 'argon2', 'pbkdf2_sha512'
app.config['SECURITY_PASSWORD_SALT'] = 'your-secret-salt'

# Password validation
app.config['SECURITY_PASSWORD_LENGTH_MIN'] = 8
app.config['SECURITY_PASSWORD_LENGTH_MAX'] = 128
app.config['SECURITY_PASSWORD_COMPLEXITY_CHECKER'] = 'zxcvbn'
app.config['SECURITY_PASSWORD_CHECK_BREACHED'] = True
app.config['SECURITY_PASSWORD_BREACHED_COUNT'] = 1

# Password reset
app.config['SECURITY_RECOVERABLE'] = True
app.config['SECURITY_RESET_PASSWORD_WITHIN'] = '5 days'
app.config['SECURITY_RESET_URL'] = '/reset-password'

# Change password
app.config['SECURITY_CHANGEABLE'] = True
app.config['SECURITY_CHANGE_URL'] = '/change-password'
app.config['SECURITY_POST_CHANGE_REDIRECT_ENDPOINT'] = '/profile'

# Send password change notice
app.config['SECURITY_SEND_PASSWORD_CHANGE_EMAIL'] = True
app.config['SECURITY_SEND_PASSWORD_RESET_NOTICE_EMAIL'] = True

docs

authentication.md

authorization.md

core-setup.md

database.md

index.md

password-management.md

registration.md

two-factor.md

unified-signin.md

utilities.md

webauthn.md

tile.json