Flask-Login
raw JSON → 0.6.3 verified Tue May 12 auth: no python install: verified
Flask-Login is a popular extension for Flask that provides user session management. It handles the common tasks of logging in, logging out, and remembering user sessions over extended periods. Version 0.6.3 is currently active, with a release cadence that addresses compatibility with newer Flask and Werkzeug versions, and ongoing maintenance.
pip install flask-login Common errors
error ModuleNotFoundError: No module named 'flask_login' ↓
cause The `flask-login` package is either not installed, or the Python interpreter being used is not the one where the package was installed (e.g., virtual environment mismatch).
fix
Ensure
flask-login is installed in your active Python environment. If using a virtual environment, activate it first. Then run: pip install Flask-Login error Exception: Missing user_loader or request_loader. Refer to http://flask-login.readthedocs.io/#how-it-works for more info. ↓
cause Flask-Login requires a callback function to load a user from a user ID stored in the session. This `user_loader` function must be registered with the `LoginManager` instance.
fix
Define a
user_loader function that takes a user_id (string) and returns the corresponding user object, or None if the user does not exist, and register it using the @login_manager.user_loader decorator. error AttributeError: 'AnonymousUserMixin' object has no attribute 'id' ↓
cause This error occurs when you try to access an attribute (like 'id', 'is_active', or other custom user model attributes) on `current_user` when no user is logged in. In such cases, `current_user` is an `AnonymousUserMixin` object, which does not have these attributes by default.
fix
Before accessing user-specific attributes, always check if a user is authenticated using
current_user.is_authenticated. For example: if current_user.is_authenticated: user_id = current_user.id error AttributeError: 'Flask' object has no attribute 'login_manager' ↓
cause This error typically means that the `LoginManager` has not been correctly initialized with your Flask application instance using `login_manager.init_app(app)`.
fix
Ensure you instantiate
LoginManager() and then call login_manager.init_app(app) after your Flask app object (app) has been created, usually near the application setup. error TypeError: The view function did not return a valid response. The function either returned None or ended without a return statement. ↓
cause While not exclusive to Flask-Login, this error often appears in login/logout routes or views protected by `@login_required` if a `return` statement is omitted or returns `None` under certain conditions (e.g., failed login, unauthorized access without a specified `login_view`). Flask view functions must always return a valid response object, a string, a tuple (response, status), or a redirect.
fix
Ensure all execution paths within your view functions explicitly return a valid Flask response (e.g.,
render_template(), redirect(url_for('login')), jsonify(), or a direct response object). Warnings
breaking Version 0.6.0 dropped support for Python 2.7, 3.5, and 3.6. It also increased the minimum required versions for Flask to 1.0.4 and Werkzeug to 1.0.1. ↓
fix Ensure your project uses Python 3.7+ and updates Flask to at least 1.0.4 and Werkzeug to 1.0.1. For Flask 3.x and Werkzeug 3.x, use Flask-Login 0.6.3 or newer.
gotcha A `SECRET_KEY` is absolutely essential for session security. Flask-Login relies on Flask's session management, which uses the `SECRET_KEY` to sign session cookies. Using a weak or default key in production is a critical security vulnerability. ↓
fix Always set a strong, unique, and securely managed `app.config['SECRET_KEY']` in your Flask application, especially in production environments. Use environment variables or a configuration management system.
gotcha Flask-Login handles user session management but does NOT dictate how you store users or handle authentication logic (e.g., password hashing, database interactions). You must provide your own user model and authentication checks. ↓
fix Implement your user loading and verification logic (e.g., checking passwords against a hashed stored value, querying a database) within your application. Flask-Login provides `UserMixin` to help your user model conform to its requirements, and functions like `login_user` to manage the session once authentication is successful.
gotcha The `user_loader` callback must return `None` if the provided user ID is not valid. Raising an exception instead of returning `None` will result in an error and prevent proper session management. ↓
fix Modify your `@login_manager.user_loader` function to explicitly return `None` when a user cannot be found for the given `user_id`.
deprecated Older tutorials or examples might use `from flask.ext.login import ...`. This import path is deprecated and will fail in modern Flask applications. ↓
fix Always use `from flask_login import ...` for importing Flask-Login components.
gotcha If deploying with WSGI servers like Gunicorn, using `app.app_context().push()` outside a `with` statement or without a corresponding `pop()` can lead to session and CSRF token issues as the context is not properly closed. ↓
fix Ensure that `app.app_context()` is used as a context manager (`with app.app_context():`) or that contexts are properly pushed and popped in your application's entry point if not using the context manager approach.
breaking Flask-Login might not be fully compatible with very recent or pre-release Python versions (e.g., Python 3.13). While it typically states `Requires-Python: >=3.7`, compatibility with unreleased or newly released major Python versions is not immediately guaranteed and can lead to unexpected behavior or failures, including timeouts. ↓
fix When using Flask-Login, ensure your project runs on stable, officially released Python versions that are explicitly supported by the Flask-Login version you are using. Monitor Flask-Login's official changelog and issue tracker for compatibility updates with new Python releases.
Install compatibility verified last tested: 2026-05-12
python os / libc status wheel install import disk
3.10 alpine (musl) wheel - 0.55s 22.5M
3.10 alpine (musl) - - 0.47s 22.5M
3.10 slim (glibc) wheel 2.1s 0.40s 23M
3.10 slim (glibc) - - 0.37s 23M
3.11 alpine (musl) wheel - 0.66s 25.2M
3.11 alpine (musl) - - 0.67s 25.2M
3.11 slim (glibc) wheel 2.3s 0.58s 26M
3.11 slim (glibc) - - 0.50s 26M
3.12 alpine (musl) wheel - 0.60s 16.9M
3.12 alpine (musl) - - 0.56s 16.9M
3.12 slim (glibc) wheel 2.0s 0.59s 17M
3.12 slim (glibc) - - 0.53s 17M
3.13 alpine (musl) wheel - 0.58s 16.6M
3.13 alpine (musl) - - 0.52s 16.5M
3.13 slim (glibc) wheel 2.0s 0.58s 17M
3.13 slim (glibc) - - 0.52s 17M
3.9 alpine (musl) wheel - 0.41s 22.3M
3.9 alpine (musl) - - 0.43s 22.3M
3.9 slim (glibc) wheel 2.6s 0.39s 23M
3.9 slim (glibc) - - 0.37s 23M
Imports
- LoginManager
from flask_login import LoginManager - UserMixin wrong
from flask.ext.login import UserMixincorrectfrom flask_login import UserMixin - login_user
from flask_login import login_user - logout_user
from flask_login import logout_user - login_required
from flask_login import login_required - current_user
from flask_login import current_user
Quickstart last tested: 2026-04-24
import os
from flask import Flask, redirect, url_for, request, render_template_string
from flask_login import LoginManager, UserMixin, login_user, logout_user, login_required, current_user
app = Flask(__name__)
app.config['SECRET_KEY'] = os.environ.get('FLASK_SECRET_KEY', 'a_secret_key_for_dev_only') # IMPORTANT: Change in production!
login_manager = LoginManager()
login_manager.init_app(app)
login_manager.login_view = 'login'
# A simple 'database' for demonstration
USERS = {
"testuser": {"password": "testpass", "id": "1"},
"another": {"password": "anotherpass", "id": "2"}
}
class User(UserMixin):
def __init__(self, id, password, username):
self.id = id
self.password = password
self.username = username
def get_id(self):
return str(self.id)
@login_manager.user_loader
def load_user(user_id):
# This callback is used to reload the user object from the user ID stored in the session.
# It should take the unicode ID of a user, and return the corresponding user object.
for username, data in USERS.items():
if data['id'] == user_id:
return User(data['id'], data['password'], username)
return None
@app.route('/')
def index():
if current_user.is_authenticated:
return f'Hello, {current_user.username}! <a href="{url_for("protected")}">Protected Page</a> | <a href="{url_for("logout")}">Logout</a>'
return '<a href="/login">Login</a>'
@app.route('/login', methods=['GET', 'POST'])
def login():
if current_user.is_authenticated:
return redirect(url_for('index'))
if request.method == 'POST':
username = request.form['username']
password = request.form['password']
if username in USERS and USERS[username]['password'] == password:
user_data = USERS[username]
user = User(user_data['id'], user_data['password'], username)
login_user(user)
next_page = request.args.get('next')
return redirect(next_page or url_for('index'))
return 'Invalid credentials'
return render_template_string('''
<form method="post">
<p><input type=text name=username placeholder="Username"></p>
<p><input type=password name=password placeholder="Password"></p>
<p><input type=submit value=Login></p>
</form>
''')
@app.route('/logout')
@login_required
def logout():
logout_user()
return redirect(url_for('index'))
@app.route('/protected')
@login_required
def protected():
return f'This is a protected page. Only {current_user.username} can see this! <a href="{url_for("index")}">Home</a>'
if __name__ == '__main__':
app.run(debug=True)