Install & Compatibility
Where this runs
tested against v0.6.3 · pip install
no network on importno background threads
Install × environment matrix
Each cell = how many times install + import succeeded across repeated harness runs. Partial = flaky.
glibc = Debian/Ubuntu slim · musl = Alpine Linux
muslpy 3.10–3.95 runs
installs and imports cleanly · install 0.0s · import 0.590s · 22.6MB
glibcpy 3.10–3.95 runs
installs and imports cleanly · install 2.2s · import 0.528s · 23MB
21MB installed
● package 21MB
Code
Verified usage
Verified import paths — ran on the pinned version, not inferred.
LoginManager
✓ from flask_login import LoginManager
UserMixin
✓ from flask_login import UserMixin
✗ from flask.ext.login import UserMixin
The `flask.ext.` prefix for extensions was deprecated and removed. Use direct import from `flask_login`.
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
This quickstart demonstrates a minimal Flask application with Flask-Login. It initializes the LoginManager, defines a simple `User` class inheriting `UserMixin`, implements a `user_loader` callback to load users by ID, and provides basic login, logout, and a protected route. Remember to set a strong `SECRET_KEY` in production. User authentication logic (e.g., password hashing) and user storage are handled by the application, not Flask-Login directly.
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)
Debug
Known issues
breakingVersion 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.fixEnsure 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.
affects: >=0.6.0
gotchaA `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.fixAlways 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.
affects: All
gotchaFlask-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.fixImplement 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.
affects: All
gotchaThe `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.fixModify your `@login_manager.user_loader` function to explicitly return `None` when a user cannot be found for the given `user_id`.
affects: All
deprecatedOlder tutorials or examples might use `from flask.ext.login import ...`. This import path is deprecated and will fail in modern Flask applications.fixAlways use `from flask_login import ...` for importing Flask-Login components.
affects: Flask 0.11+ / Flask-Login 0.2.x onwards
gotchaIf 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.fixEnsure 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.
affects: All (especially with Gunicorn/Azure deployment)
breakingFlask-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.fixWhen 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.
affects: All (especially with pre-release or newly released major Python versions)
Errors
Common errors & fixes
ModuleNotFoundError: No module named 'flask_login'
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).
fixEnsure `flask-login` is installed in your active Python environment. If using a virtual environment, activate it first. Then run: `pip install Flask-Login`
Exception: Missing user_loader or request_loader. Refer to http://flask-login.readthedocs.io/#how-it-works for more info.
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.
fixDefine 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.
AttributeError: 'AnonymousUserMixin' object has no attribute 'id'
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.
fixBefore 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`
AttributeError: 'Flask' object has no attribute 'login_manager'
This error typically means that the `LoginManager` has not been correctly initialized with your Flask application instance using `login_manager.init_app(app)`.
fixEnsure 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.
TypeError: The view function did not return a valid response. The function either returned None or ended without a return statement.
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.
fixEnsure 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). Upgrade
Version history
0.6.3latest on PyPI · released Oct 30, 2023
Audit
Dependencies
FlaskrequiredCore web framework dependency. Version 0.6.0 onwards requires Flask >= 1.0.4, with 0.6.3 fixing compatibility with Flask 3.
WerkzeugrequiredA dependency of Flask. Version 0.6.0 onwards requires Werkzeug >= 1.0.1, with 0.6.3 fixing compatibility with Werkzeug 3.