Flask Blog Login
In this chapter, you will learn to implement Session user authentication with Flask-Login, which is the most important security guarantee in Flask web development.
* * *
## What is Flask-Login?
HTTP requests in web applications are essentially stateless β the server doesn't automatically remember who you are.
Flask-Login implements user state maintenance through the Session mechanism: after login, the server stores the user ID in the Session, and subsequent requests automatically identify the current user.
* * *
## Installation and Initialization
(venv) $ pip install flask-login
## Example
# File path: app.py
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from flask_login import LoginManager
app = Flask( __name__ )
app.config['SQLALCHEMY_DATABASE_URI']='sqlite:///blog.db'
app.config['SECRET_KEY']='your-secret-key-here'# Session encryption key (use environment variable in production)
db = SQLAlchemy(app)
# Initialize LoginManager
login_manager = LoginManager()
login_manager.init_app(app)
login_manager.login_view='auth.login'# Redirect unlogged-in users to login page
login_manager.login_message='Please log in first.'
> `SECRET_KEY` is the cornerstone of Flask's security system β Session encryption, CSRF tokens, and Flash messages all depend on it. In production, it must be read from environment variables and never hardcoded.
* * *
## Defining the User Model
Flask-Login requires the User model to implement several methods provided by UserMixin.
## Example
# File path: models.py (new)
from flask_login import UserMixin
from werkzeug.security import generate_password_hash, check_password_hash
class User(UserMixin, db.Model):
"""User model"""
__tablename__ ='users'
id= db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(50), unique=True, nullable=False)
email= db.Column(db.String(120), unique=True, nullable=False)
password_hash = db.Column(db.String(256), nullable=False)
def set_password(self, password):
"""Hash and encrypt the password (never store plaintext passwords)"""
self.password_hash= generate_password_hash(password)
def check_password(self, password):
"""Verify if password matches"""
return check_password_hash(self.password_hash, password)
def __repr__ (self):
return f''
# Define user_loader: Flask-Login uses this function to restore user object from session
@login_manager.user_loader
def load_user(user_id):
return User.query.get(int(user_id))
UserMixin automatically injects the following properties and methods into the User model:
| Property/Method | Description |
| --- | --- |
| is_authenticated | Whether the user is logged in |
| is_active | Whether the user is active |
| is_anonymous | Whether it's an anonymous user (not logged in) |
| get_id() | Get the user primary key ID |
Generate and execute the migration:
(venv) $ flask db migrate -m "Add User model"
(venv) $ flask db upgrade
* * *
## Creating Authentication Blueprint
## Example
# File path: app/blueprints/auth.py
from flask import Blueprint, render_template, redirect, url_for, request, flash
from flask_login import login_user, logout_user, login_required, current_user
from app.models import User, db
auth_bp = Blueprint('auth', __name__)
@auth_bp.route("/register", methods=['GET','POST'])
def register():
"""User registration"""
if current_user.is_authenticated:
return redirect(url_for('main.index'))
if request.method=='POST':
username = request.form.get('username','').strip()
email= request.form.get('email','').strip()
password = request.form.get('password','')
# Validate required fields
if not username or not email or not password:
flash('All fields are required.','error')
return render_template('register.html')
# Check if username and email already exist
if User.query.filter_by(username=username).first():
flash('Username already taken.','error')
return render_template('register.html')
if User.query.filter_by(email=email).first():
flash('Email already registered.','error')
return render_template('register.html')
# Create user
user= User(username=username,email=email)
user.set_password(password)# Encrypt and store password
db.session.add(user)
db.session.commit()
login_user(user)# Auto login after registration
flash(f'Registration successful, welcome {username}!','success')
return redirect(url_for('main.index'))
return render_template('register.html')
@auth_bp.route("/login", methods=['GET','POST'])
def login():
"""User login"""
if current_user.is_authenticated:
return redirect(url_for('main.index'))
if request.method=='POST':
username = request.form.get('username','').strip()
password = request.form.get('password','')
# user_loader finds user from database
user= User.query.filter_by(username=username).first()
# Verify password (password itself won't be logged)
if user is None or not user.check_password(password):
flash('Incorrect username or password.','error')
return render_template('login.html')
login_user(user, remember=request.form.get('remember'))
flash(f'Welcome back, {username}!','success')
# After login, redirect to the page before login (next parameter in URL)
next_page = request.args.get('next')
return redirect(next_page or url_for('main.index'))
return render_template('login.html')
@auth_bp.route("/logout")
@login_required # Only logged-in users can logout
def logout():
logout_user()
flash('Logged out successfully.','info')
return redirect(url_for('main.index'))
### Required References in App Design
## Example
# File path: app.py - register auth Blueprint
from app.blueprints.auth import auth_bp
app.register_blueprint(auth_bp)
> Never store passwords in plaintext! `generate_password_hash()` uses the PBKDF2 algorithm to perform salted hash on passwords. Even if the database is leaked, attackers cannot reverse-engineer the passwords. Use `check_password_hash()` when verifying passwords.
* * *
## Creating Login and Registration Templates
## Example
YouTip