User Authentication With Flask-Login

Introduction

Most apps need to know who is logged in. Flask-Login manages user sessions: login_user, logout_user, @login_required, and current_user in views and templates. This chapter stores hashed passwords with Werkzeug, implements registration and login, and adds simple role-based access control.

Prerequisites

Install and Setup

bash
pip install Flask-Login

app/extensions.py:

python
from flask_sqlalchemy import SQLAlchemy
from flask_login import LoginManager
 
db = SQLAlchemy()
login_manager = LoginManager()
login_manager.login_view = "auth.login"
login_manager.login_message_category = "warning"

app/__init__.py:

python
from app.extensions import db, login_manager
 
def create_app(config_name="development"):
    ...
    login_manager.init_app(app)
    ...
    return app

Code explanation:

  • login_view redirects unauthenticated users to the login route
  • login_message_category styles flash message from Flask-Login

User Model With UserMixin

app/models.py:

app/__init__.py or app/models.py:

python
from app.models import User
 
@login_manager.user_loader
def load_user(user_id):
    return db.session.get(User, int(user_id))

Code explanation:

  • UserMixin adds is_authenticated, is_active, get_id() for Flask-Login
  • user_loader reloads user from session ID on each request
  • Never store plain-text passwords—only password_hash

Warning

Never Store Plain Passwords

Use set_password() on registration; verify with check_password() on login.

Registration View

app/auth/routes.py:

Login and Logout

Code explanation:

  • remember=True sets longer-lived cookie via REMEMBER_COOKIE settings
  • Validate next URL to prevent open redirects—only allow relative paths

Protect Routes

python
from flask_login import login_required, current_user
 
@main_bp.route("/dashboard")
@login_required
def dashboard():
    return render_template("dashboard.html", user=current_user)

Template:

html
{% if current_user.is_authenticated %}
  <p>Hello, {{ current_user.username }}</p>
  <a href="{{ url_for('auth.logout') }}">Logout</a>
{% else %}
  <a href="{{ url_for('auth.login') }}">Login</a>
{% endif %}

Simple Role Decorator

app/auth/decorators.py:

python
from functools import wraps
from flask import abort
from flask_login import current_user
 
def admin_required(f):
    @wraps(f)
    def decorated(*args, **kwargs):
        if not current_user.is_authenticated or current_user.role != "admin":
            abort(403)
        return f(*args, **kwargs)
    return decorated

Usage:

python
@main_bp.route("/admin")
@login_required
@admin_required
def admin_panel():
    return render_template("admin.html")

Flash and redirect instead of abort(403) for friendlier UX:

python
if current_user.role != "admin":
    flash("Admin access required.", "danger")
    return redirect(url_for("main.index"))

Remember Me and Session Config

python
app.config["REMEMBER_COOKIE_DURATION"] = timedelta(days=14)
app.config["REMEMBER_COOKIE_HTTPONLY"] = True
app.config["REMEMBER_COOKIE_SECURE"] = True  # HTTPS only in production

FAQ

current_user is Anonymous?

User not logged in—current_user.is_authenticated is False.

load_user returns None?

Session has stale user ID—user deleted from DB; Flask-Login treats as logged out.

Login works but @login_required loops?

login_view endpoint wrong or login route also has @login_required.

bcrypt vs werkzeug hash?

Werkzeug defaults are fine for most apps; bcrypt via passlib for stricter policies.

Multiple login backends?

Flask-Login supports one user_loader—use custom logic inside it.

OAuth (Google/GitHub)?

Use Authlib or similar—Flask-Login handles session after OAuth callback.

Compare to Spring Security?

See Spring Boot JWT and security for enterprise patterns; Flask-Login fits server-rendered sessions first.