CtrlK
CommunityDocumentationLog inGet started
Tessl Logo

tessl/pypi-flask-security

Simple security for Flask apps

Overview
Eval results
Files

forms.mddocs/

Forms and User Interface

Flask-Security provides WTForms-based form classes for handling common security-related user interactions including login, registration, password reset, and account confirmation. These forms include built-in validation, CSRF protection, and customization options.

Capabilities

Login Forms

Forms for user authentication and login.

class LoginForm:
    """Default login form with email, password, and remember fields"""
    
    # Fields
    email: StringField  # User email address
    password: PasswordField  # User password
    remember: BooleanField  # Remember me checkbox
    next: HiddenField  # Redirect URL after login
    submit: SubmitField  # Submit button
    
    def validate(self):
        """
        Custom validation logic for login form.
        
        Returns:
            bool: True if form data is valid
        """

Registration Forms

Forms for user account creation and registration.

class ConfirmRegisterForm:
    """Base registration form for confirmable registration"""
    
    # Fields
    email: StringField  # User email (with unique validation)
    password: PasswordField  # User password (with length validation)
    submit: SubmitField  # Submit button
    
    def to_dict(self):
        """
        Convert form data to dictionary.
        
        Returns:
            dict: Form field values
        """

class RegisterForm:
    """Default registration form extending ConfirmRegisterForm"""
    
    # Inherits email, password, submit from ConfirmRegisterForm
    password_confirm: PasswordField  # Password confirmation field
    next: HiddenField  # Redirect URL after registration

Password Management Forms

Forms for password reset and password change operations.

class ForgotPasswordForm:
    """Form for requesting password reset"""
    
    # Fields
    email: StringField  # Email address (with existing user validation)
    submit: SubmitField  # Submit button
    
    def validate(self):
        """
        Validate email and ensure user doesn't require confirmation.
        
        Returns:
            bool: True if form data is valid
        """

class ResetPasswordForm:
    """Form for resetting password with token"""
    
    # Fields
    password: PasswordField  # New password (with length validation)
    password_confirm: PasswordField  # Password confirmation
    submit: SubmitField  # Submit button

class ChangePasswordForm:
    """Form for changing existing password"""
    
    # Fields
    password: PasswordField  # Current password
    new_password: PasswordField  # New password
    new_password_confirm: PasswordField  # New password confirmation
    submit: SubmitField  # Submit button

Alternative Authentication Forms

Forms for passwordless login and confirmation management.

class PasswordlessLoginForm:
    """Form for passwordless login via email"""
    
    # Fields
    email: StringField  # Email address (with existing user validation)
    submit: SubmitField  # Submit button
    
    def validate(self):
        """
        Validate email and ensure user is active.
        
        Returns:
            bool: True if form data is valid
        """

class SendConfirmationForm:
    """Form for requesting email confirmation"""
    
    # Fields
    email: StringField  # Email address
    submit: SubmitField  # Submit button

Form Mixins

Base mixins that provide common form functionality.

class EmailFormMixin:
    """Mixin providing email field"""
    email: StringField

class PasswordFormMixin:
    """Mixin providing password field with validation"""
    password: PasswordField

class NewPasswordFormMixin:
    """Mixin providing new password field with validation"""
    password: PasswordField

class PasswordConfirmFormMixin:
    """Mixin providing password confirmation field"""
    password_confirm: PasswordField

class NextFormMixin:
    """Mixin providing next URL hidden field"""
    next: HiddenField

class RegisterFormMixin:
    """Mixin providing registration-specific functionality"""
    pass

Form Validators

Custom validators used by Flask-Security forms.

def unique_user_email(form, field):
    """
    Validator ensuring email address is unique.
    
    Args:
        form: Form instance
        field: Email field
        
    Raises:
        ValidationError: If email already exists
    """

def valid_user_email(form, field):
    """
    Validator ensuring email belongs to existing user.
    
    Args:
        form: Form instance
        field: Email field
        
    Raises:
        ValidationError: If user doesn't exist
    """

def get_form_field_label(key):
    """
    Get localized form field label.
    
    Args:
        key (str): Field label key
        
    Returns:
        str: Localized label text
    """

Usage Examples

Using Default Forms

from flask import render_template, request, redirect
from flask_security import Security, login_user
from flask_security.forms import LoginForm

@app.route('/login', methods=['GET', 'POST'])
def login():
    form = LoginForm()
    
    if form.validate_on_submit():
        user = user_datastore.find_user(email=form.email.data)
        if user and verify_password(form.password.data, user.password):
            login_user(user, remember=form.remember.data)
            return redirect(form.next.data or '/')
    
    return render_template('login.html', form=form)

Custom Form Classes

from flask_security.forms import LoginForm, RegisterForm
from wtforms import StringField, TextAreaField
from wtforms.validators import DataRequired

class CustomLoginForm(LoginForm):
    username = StringField('Username or Email', validators=[DataRequired()])
    
    def validate(self):
        # Custom validation logic
        if not super().validate():
            return False
        
        # Allow login with username or email
        user = (user_datastore.find_user(email=self.username.data) or
                user_datastore.find_user(username=self.username.data))
        
        if not user:
            self.username.errors.append('Invalid username or email')
            return False
        
        return True

class ExtendedRegisterForm(RegisterForm):
    first_name = StringField('First Name', validators=[DataRequired()])
    last_name = StringField('Last Name', validators=[DataRequired()])
    bio = TextAreaField('Bio')
    
    def validate(self):
        if not super().validate():
            return False
        
        # Custom validation for names
        if len(self.first_name.data) < 2:
            self.first_name.errors.append('First name too short')
            return False
        
        return True

# Register custom forms with Flask-Security
security = Security(app, user_datastore,
                   login_form=CustomLoginForm,
                   register_form=ExtendedRegisterForm)

Form Customization in Templates

<!-- login.html template -->
<form method="POST">
    {{ form.hidden_tag() }}
    
    <div class="form-group">
        {{ form.email.label(class="form-label") }}
        {{ form.email(class="form-control") }}
        {% for error in form.email.errors %}
            <div class="text-danger">{{ error }}</div>
        {% endfor %}
    </div>
    
    <div class="form-group">
        {{ form.password.label(class="form-label") }}
        {{ form.password(class="form-control") }}
        {% for error in form.password.errors %}
            <div class="text-danger">{{ error }}</div>
        {% endfor %}
    </div>
    
    <div class="form-check">
        {{ form.remember(class="form-check-input") }}
        {{ form.remember.label(class="form-check-label") }}
    </div>
    
    {{ form.submit(class="btn btn-primary") }}
</form>

Handling Form Validation

from flask_security.forms import RegisterForm
from flask_security import hash_password

@app.route('/register', methods=['GET', 'POST'])
def register():
    form = RegisterForm()
    
    if form.validate_on_submit():
        # Create user with form data
        user = user_datastore.create_user(
            email=form.email.data,
            password=hash_password(form.password.data)
        )
        user_datastore.commit()
        
        # Send confirmation email if enabled
        if app.config.get('SECURITY_CONFIRMABLE'):
            send_confirmation_instructions(user)
            message = 'Check your email to confirm your account'
        else:
            login_user(user)
            message = 'Registration successful'
        
        flash(message)
        return redirect('/')
    
    return render_template('register.html', form=form)

Custom Validators

from wtforms.validators import ValidationError
from flask_security.forms import RegisterForm

def validate_strong_password(form, field):
    """Custom validator for strong passwords"""
    password = field.data
    if len(password) < 8:
        raise ValidationError('Password must be at least 8 characters')
    if not any(c.isupper() for c in password):
        raise ValidationError('Password must contain an uppercase letter')
    if not any(c.islower() for c in password):
        raise ValidationError('Password must contain a lowercase letter')
    if not any(c.isdigit() for c in password):
        raise ValidationError('Password must contain a number')

class StrongPasswordRegisterForm(RegisterForm):
    password = PasswordField('Password', validators=[
        DataRequired(),
        validate_strong_password
    ])

AJAX Form Handling

from flask import jsonify
from flask_security.forms import LoginForm

@app.route('/api/login', methods=['POST'])
def api_login():
    form = LoginForm()
    
    if form.validate_on_submit():
        user = user_datastore.find_user(email=form.email.data)
        if user and verify_password(form.password.data, user.password):
            login_user(user, remember=form.remember.data)
            return jsonify({
                'success': True,
                'user': user.get_security_payload(),
                'auth_token': user.get_auth_token()
            })
    
    # Return validation errors
    return jsonify({
        'success': False,
        'errors': form.errors
    }), 400

Multi-Step Forms

from flask import session
from flask_security.forms import RegisterForm

class Step1RegisterForm(Form):
    email = StringField('Email', validators=[DataRequired(), Email()])
    submit = SubmitField('Next')

class Step2RegisterForm(Form):
    password = PasswordField('Password', validators=[DataRequired()])
    password_confirm = PasswordField('Confirm Password')
    submit = SubmitField('Complete Registration')

@app.route('/register/step1', methods=['GET', 'POST'])
def register_step1():
    form = Step1RegisterForm()
    if form.validate_on_submit():
        session['registration_email'] = form.email.data
        return redirect('/register/step2')
    return render_template('register_step1.html', form=form)

@app.route('/register/step2', methods=['GET', 'POST'])
def register_step2():
    if 'registration_email' not in session:
        return redirect('/register/step1')
    
    form = Step2RegisterForm()
    if form.validate_on_submit():
        user = user_datastore.create_user(
            email=session.pop('registration_email'),
            password=hash_password(form.password.data)
        )
        user_datastore.commit()
        return redirect('/login')
    
    return render_template('register_step2.html', form=form)
tessl i tessl/pypi-flask-security@3.0.0

docs

authentication.md

authorization.md

core-extension.md

data-storage.md

forms.md

index.md

security-features.md

signals.md

user-role-models.md

tile.json