Flask User Registration
User registration is a fundamental feature in many web applications, allowing visitors to create accounts, save preferences, and access protected content. In this tutorial, we'll learn how to build a robust user registration system using Flask.
Introduction to User Registration
A user registration system typically consists of:
- A registration form that collects user information
- Validation logic to ensure data integrity
- Password security measures
- Database storage for user credentials
- Feedback mechanisms to guide users
Flask makes implementing these features straightforward with its extensions and flexible architecture.
Prerequisites
Before starting, make sure you have:
- Basic knowledge of Flask
- Python 3.6 or higher installed
- Understanding of HTML forms
- Basic SQL knowledge
Setting Up Your Flask Project
Let's start by setting up a basic Flask application structure:
mkdir flask_user_registration
cd flask_user_registration
python -m venv venv
# On Windows
venv\Scripts\activate
# On macOS/Linux
source venv/bin/activate
pip install flask flask-sqlalchemy flask-wtf email-validator flask-bcrypt
Now create a basic project structure:
flask_user_registration/
├── app.py
├── models.py
├── forms.py
├── templates/
│ ├── layout.html
│ ├── home.html
│ └── register.html
└── static/
└── styles.css
Creating the Database Model
First, let's define our user model in models.py
:
from flask_sqlalchemy import SQLAlchemy
from datetime import datetime
db = SQLAlchemy()
class User(db.Model):
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(20), unique=True, nullable=False)
email = db.Column(db.String(120), unique=True, nullable=False)
password = db.Column(db.String(60), nullable=False)
date_joined = db.Column(db.DateTime, nullable=False, default=datetime.utcnow)
def __repr__(self):
return f"User('{self.username}', '{self.email}')"
This model defines a User table with fields for username, email, password (which we'll hash for security), and registration date.
Building the Registration Form
Next, let's create our registration form using Flask-WTF in forms.py
:
from flask_wtf import FlaskForm
from wtforms import StringField, PasswordField, SubmitField
from wtforms.validators import DataRequired, Length, Email, EqualTo, ValidationError
from models import User
class RegistrationForm(FlaskForm):
username = StringField('Username',
validators=[DataRequired(), Length(min=2, max=20)])
email = StringField('Email',
validators=[DataRequired(), Email()])
password = PasswordField('Password',
validators=[DataRequired()])
confirm_password = PasswordField('Confirm Password',
validators=[DataRequired(), EqualTo('password')])
submit = SubmitField('Sign Up')
# Custom validation to check if username already exists
def validate_username(self, username):
user = User.query.filter_by(username=username.data).first()
if user:
raise ValidationError('That username is already taken. Please choose another one.')
# Custom validation to check if email already exists
def validate_email(self, email):
user = User.query.filter_by(email=email.data).first()
if user:
raise ValidationError('That email is already registered. Please use another one.')
This form includes fields for username, email, password with confirmation, and custom validation methods to ensure usernames and emails are unique.
Creating HTML Templates
Now let's create our templates. First, the base layout in templates/layout.html
:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Flask User Registration</title>
<link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='styles.css') }}">
</head>
<body>
<header>
<nav>
<div class="container">
<h1>Flask Auth Demo</h1>
<div class="nav-links">
<a href="{{ url_for('home') }}">Home</a>
<a href="{{ url_for('register') }}">Register</a>
</div>
</div>
</nav>
</header>
<main class="container">
{% with messages = get_flashed_messages(with_categories=true) %}
{% if messages %}
{% for category, message in messages %}
<div class="alert alert-{{ category }}">
{{ message }}
</div>
{% endfor %}
{% endif %}
{% endwith %}
{% block content %}{% endblock %}
</main>
</body>
</html>
Next, create a simple home page in templates/home.html
:
{% extends "layout.html" %}
{% block content %}
<section class="welcome-section">
<h2>Welcome to Flask Authentication Demo</h2>
<p>This simple application demonstrates user registration in Flask.</p>
<div class="cta-buttons">
<a href="{{ url_for('register') }}" class="button">Register Now</a>
</div>
</section>
{% endblock %}
Finally, the registration form in templates/register.html
:
{% extends "layout.html" %}
{% block content %}
<div class="form-container">
<h2>Create an Account</h2>
<form method="POST" action="">
{{ form.hidden_tag() }}
<div class="form-group">
{{ form.username.label(class="form-label") }}
{% if form.username.errors %}
{{ form.username(class="form-input is-invalid") }}
<div class="invalid-feedback">
{% for error in form.username.errors %}
<span>{{ error }}</span>
{% endfor %}
</div>
{% else %}
{{ form.username(class="form-input") }}
{% endif %}
</div>
<div class="form-group">
{{ form.email.label(class="form-label") }}
{% if form.email.errors %}
{{ form.email(class="form-input is-invalid") }}
<div class="invalid-feedback">
{% for error in form.email.errors %}
<span>{{ error }}</span>
{% endfor %}
</div>
{% else %}
{{ form.email(class="form-input") }}
{% endif %}
</div>
<div class="form-group">
{{ form.password.label(class="form-label") }}
{% if form.password.errors %}
{{ form.password(class="form-input is-invalid") }}
<div class="invalid-feedback">
{% for error in form.password.errors %}
<span>{{ error }}</span>
{% endfor %}
</div>
{% else %}
{{ form.password(class="form-input") }}
{% endif %}
</div>
<div class="form-group">
{{ form.confirm_password.label(class="form-label") }}
{% if form.confirm_password.errors %}
{{ form.confirm_password(class="form-input is-invalid") }}
<div class="invalid-feedback">
{% for error in form.confirm_password.errors %}
<span>{{ error }}</span>
{% endfor %}
</div>
{% else %}
{{ form.confirm_password(class="form-input") }}
{% endif %}
</div>
<div class="form-group">
{{ form.submit(class="button") }}
</div>
</form>
<div class="form-footer">
<p>Already have an account? <a href="#">Login here</a></p>
</div>
</div>
{% endblock %}
Adding Some Basic Styling
Create a minimal stylesheet in static/styles.css
:
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
line-height: 1.6;
background-color: #f7f9fc;
color: #333;
}
.container {
width: 90%;
max-width: 1200px;
margin: 0 auto;
padding: 0 15px;
}
header {
background-color: #4a6fa5;
color: white;
padding: 1rem 0;
margin-bottom: 2rem;
}
nav .container {
display: flex;
justify-content: space-between;
align-items: center;
}
.nav-links a {
color: white;
margin-left: 1rem;
text-decoration: none;
}
.form-container {
background-color: white;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
padding: 2rem;
max-width: 600px;
margin: 0 auto;
}
.form-container h2 {
margin-bottom: 1.5rem;
text-align: center;
color: #4a6fa5;
}
.form-group {
margin-bottom: 1rem;
}
.form-label {
display: block;
margin-bottom: 0.5rem;
font-weight: 500;
}
.form-input {
width: 100%;
padding: 0.75rem;
border: 1px solid #ddd;
border-radius: 4px;
font-size: 1rem;
}
.form-input.is-invalid {
border-color: #dc3545;
}
.invalid-feedback {
color: #dc3545;
font-size: 0.875rem;
margin-top: 0.25rem;
}
.button {
display: inline-block;
background-color: #4a6fa5;
color: white;
border: none;
padding: 0.75rem 1.5rem;
font-size: 1rem;
border-radius: 4px;
cursor: pointer;
text-decoration: none;
}
.button:hover {
background-color: #3a5a80;
}
.form-footer {
text-align: center;
margin-top: 1.5rem;
}
.alert {
padding: 1rem;
margin-bottom: 1rem;
border-radius: 4px;
}
.alert-success {
background-color: #d4edda;
color: #155724;
}
.alert-danger {
background-color: #f8d7da;
color: #721c24;
}
.welcome-section {
text-align: center;
padding: 2rem 0;
}
.welcome-section h2 {
margin-bottom: 1rem;
color: #4a6fa5;
}
.cta-buttons {
margin-top: 2rem;
}
Implementing the Flask Application
Finally, let's implement the main Flask application in app.py
:
from flask import Flask, render_template, url_for, flash, redirect
from flask_bcrypt import Bcrypt
from forms import RegistrationForm
from models import db, User
app = Flask(__name__)
app.config['SECRET_KEY'] = 'your_secret_key_here' # Change this in production!
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///site.db'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
# Initialize extensions
bcrypt = Bcrypt(app)
db.init_app(app)
# Create database tables
@app.before_first_request
def create_tables():
db.create_all()
@app.route('/')
@app.route('/home')
def home():
return render_template('home.html')
@app.route('/register', methods=['GET', 'POST'])
def register():
form = RegistrationForm()
if form.validate_on_submit():
# Hash the password
hashed_password = bcrypt.generate_password_hash(form.password.data).decode('utf-8')
# Create new user
user = User(username=form.username.data,
email=form.email.data,
password=hashed_password)
# Add to database
db.session.add(user)
db.session.commit()
flash(f'Account created for {form.username.data}! You can now log in.', 'success')
return redirect(url_for('home'))
return render_template('register.html', form=form)
if __name__ == '__main__':
app.run(debug=True)
Running the Application
Now you can run your Flask application:
python app.py
Visit http://127.0.0.1:5000/
in your browser to see the home page, and navigate to the registration page to test your new user registration system.
Security Considerations
Our implementation includes several security best practices:
- Password Hashing: We use Flask-Bcrypt to securely hash passwords before storing them
- Form Validation: Flask-WTF provides CSRF protection and data validation
- Feedback: Users receive clear feedback on registration success or errors
- Field Uniqueness: The system prevents duplicate usernames and emails
Adding Additional Features
Here are some ways to enhance your registration system:
Email Confirmation
To add email verification:
- Generate a token when a user registers
- Send an email with a confirmation link
- Verify the token when the user clicks the link
- Only activate the account after verification
Password Strength Requirements
Enhance the password field validation:
from wtforms.validators import Regexp
password = PasswordField('Password',
validators=[
DataRequired(),
Length(min=8),
Regexp('^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]{8,}$',
message='Password must contain at least one uppercase letter, one lowercase letter, one number, and one special character')
])
CAPTCHA Integration
To prevent automated registrations, you could add a CAPTCHA using flask-recaptcha
:
from flask_recaptcha import ReCaptcha
# In your app setup
recaptcha = ReCaptcha(app=app)
app.config['RECAPTCHA_SITE_KEY'] = 'your-site-key'
app.config['RECAPTCHA_SECRET_KEY'] = 'your-secret-key'
# In your form
from flask_recaptcha import ReCaptcha
recaptcha = ReCaptcha()
# In your template
{{ recaptcha }}
# In your route
if form.validate_on_submit() and recaptcha.verify():
# Process registration
Summary
In this tutorial, we've built a complete user registration system in Flask that:
- Creates a registration form with validation
- Securely hashes passwords
- Stores user information in a database
- Provides feedback to users
- Prevents duplicate registrations
This registration system provides a solid foundation that you can extend with additional features like email verification, social login, or password strength requirements.
Additional Resources
- Flask Documentation
- Flask-WTF Documentation
- Flask-SQLAlchemy Documentation
- Flask-Bcrypt Documentation
- OWASP Authentication Cheat Sheet
Exercises
- Add a login page that authenticates users against the registered credentials
- Implement password reset functionality
- Add email verification using tokens
- Create a user profile page that shows registration date
- Enhance the registration form with additional fields like first name, last name, etc.
By completing this tutorial, you now have a solid understanding of how to implement user registration in Flask applications. This knowledge forms the foundation for building secure, user-centric web applications.
If you spot any mistakes on this website, please let me know at [email protected]. I’d greatly appreciate your feedback! :)