{"id":983,"library":"flask-login","title":"Flask-Login","description":"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.","status":"active","version":"0.6.3","language":"python","source_language":"en","source_url":"https://github.com/maxcountryman/flask-login","tags":["flask","authentication","session","login","web"],"install":[{"cmd":"pip install flask-login","lang":"bash","label":"Install Flask-Login"}],"dependencies":[{"reason":"Core web framework dependency. Version 0.6.0 onwards requires Flask >= 1.0.4, with 0.6.3 fixing compatibility with Flask 3.","package":"Flask","optional":false},{"reason":"A dependency of Flask. Version 0.6.0 onwards requires Werkzeug >= 1.0.1, with 0.6.3 fixing compatibility with Werkzeug 3.","package":"Werkzeug","optional":false}],"imports":[{"symbol":"LoginManager","correct":"from flask_login import LoginManager"},{"note":"The `flask.ext.` prefix for extensions was deprecated and removed. Use direct import from `flask_login`.","wrong":"from flask.ext.login import UserMixin","symbol":"UserMixin","correct":"from flask_login import UserMixin"},{"symbol":"login_user","correct":"from flask_login import login_user"},{"symbol":"logout_user","correct":"from flask_login import logout_user"},{"symbol":"login_required","correct":"from flask_login import login_required"},{"symbol":"current_user","correct":"from flask_login import current_user"}],"quickstart":{"code":"import os\nfrom flask import Flask, redirect, url_for, request, render_template_string\nfrom flask_login import LoginManager, UserMixin, login_user, logout_user, login_required, current_user\n\napp = Flask(__name__)\napp.config['SECRET_KEY'] = os.environ.get('FLASK_SECRET_KEY', 'a_secret_key_for_dev_only') # IMPORTANT: Change in production!\n\nlogin_manager = LoginManager()\nlogin_manager.init_app(app)\nlogin_manager.login_view = 'login'\n\n# A simple 'database' for demonstration\nUSERS = {\n    \"testuser\": {\"password\": \"testpass\", \"id\": \"1\"},\n    \"another\": {\"password\": \"anotherpass\", \"id\": \"2\"}\n}\n\nclass User(UserMixin):\n    def __init__(self, id, password, username):\n        self.id = id\n        self.password = password\n        self.username = username\n\n    def get_id(self):\n        return str(self.id)\n\n@login_manager.user_loader\ndef load_user(user_id):\n    # This callback is used to reload the user object from the user ID stored in the session.\n    # It should take the unicode ID of a user, and return the corresponding user object.\n    for username, data in USERS.items():\n        if data['id'] == user_id:\n            return User(data['id'], data['password'], username)\n    return None\n\n@app.route('/')\ndef index():\n    if current_user.is_authenticated:\n        return f'Hello, {current_user.username}! <a href=\"{url_for(\"protected\")}\">Protected Page</a> | <a href=\"{url_for(\"logout\")}\">Logout</a>'\n    return '<a href=\"/login\">Login</a>'\n\n@app.route('/login', methods=['GET', 'POST'])\ndef login():\n    if current_user.is_authenticated:\n        return redirect(url_for('index'))\n\n    if request.method == 'POST':\n        username = request.form['username']\n        password = request.form['password']\n        if username in USERS and USERS[username]['password'] == password:\n            user_data = USERS[username]\n            user = User(user_data['id'], user_data['password'], username)\n            login_user(user)\n            next_page = request.args.get('next')\n            return redirect(next_page or url_for('index'))\n        return 'Invalid credentials'\n    return render_template_string('''\n        <form method=\"post\">\n            <p><input type=text name=username placeholder=\"Username\"></p>\n            <p><input type=password name=password placeholder=\"Password\"></p>\n            <p><input type=submit value=Login></p>\n        </form>\n    ''')\n\n@app.route('/logout')\n@login_required\ndef logout():\n    logout_user()\n    return redirect(url_for('index'))\n\n@app.route('/protected')\n@login_required\ndef protected():\n    return f'This is a protected page. Only {current_user.username} can see this! <a href=\"{url_for(\"index\")}\">Home</a>'\n\nif __name__ == '__main__':\n    app.run(debug=True)","lang":"python","description":"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."},"warnings":[{"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.","message":"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.","severity":"breaking","affected_versions":">=0.6.0"},{"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.","message":"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.","severity":"gotcha","affected_versions":"All"},{"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.","message":"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.","severity":"gotcha","affected_versions":"All"},{"fix":"Modify your `@login_manager.user_loader` function to explicitly return `None` when a user cannot be found for the given `user_id`.","message":"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.","severity":"gotcha","affected_versions":"All"},{"fix":"Always use `from flask_login import ...` for importing Flask-Login components.","message":"Older tutorials or examples might use `from flask.ext.login import ...`. This import path is deprecated and will fail in modern Flask applications.","severity":"deprecated","affected_versions":"Flask 0.11+ / Flask-Login 0.2.x onwards"},{"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.","message":"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.","severity":"gotcha","affected_versions":"All (especially with Gunicorn/Azure deployment)"},{"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.","message":"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.","severity":"breaking","affected_versions":"All (especially with pre-release or newly released major Python versions)"}],"env_vars":null,"last_verified":"2026-05-12T22:11:01.102Z","next_check":"2026-06-27T00:00:00.000Z","problems":[{"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`","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).","error":"ModuleNotFoundError: No module named 'flask_login'"},{"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.","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.","error":"Exception: Missing user_loader or request_loader. Refer to http://flask-login.readthedocs.io/#how-it-works for more info."},{"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`","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.","error":"AttributeError: 'AnonymousUserMixin' object has no attribute 'id'"},{"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.","cause":"This error typically means that the `LoginManager` has not been correctly initialized with your Flask application instance using `login_manager.init_app(app)`.","error":"AttributeError: 'Flask' object has no attribute 'login_manager'"},{"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).","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.","error":"TypeError: The view function did not return a valid response. The function either returned None or ended without a return statement."}],"ecosystem":"pypi","meta_description":null,"install_score":100,"install_tag":"verified","quickstart_score":null,"quickstart_tag":null,"pypi_latest":"0.6.3","cli_name":"","install_checks":{"last_tested":"2026-05-12","tag":"verified","tag_description":"installs cleanly on critical runtimes, fast import, recently tested","results":[{"runtime":"python:3.10-alpine","python_version":"3.10","os_libc":"alpine (musl)","variant":"default","exit_code":0,"wheel_type":"wheel","failure_reason":null,"install_time_s":null,"import_time_s":0.55,"mem_mb":15.3,"disk_size":"22.5M"},{"runtime":"python:3.10-alpine","python_version":"3.10","os_libc":"alpine (musl)","variant":"default","exit_code":0,"wheel_type":null,"failure_reason":null,"install_time_s":null,"import_time_s":0.47,"mem_mb":11.9,"disk_size":"22.5M"},{"runtime":"python:3.10-slim","python_version":"3.10","os_libc":"slim (glibc)","variant":"default","exit_code":0,"wheel_type":"wheel","failure_reason":null,"install_time_s":2.1,"import_time_s":0.4,"mem_mb":15.3,"disk_size":"23M"},{"runtime":"python:3.10-slim","python_version":"3.10","os_libc":"slim (glibc)","variant":"default","exit_code":0,"wheel_type":null,"failure_reason":null,"install_time_s":null,"import_time_s":0.37,"mem_mb":11.9,"disk_size":"23M"},{"runtime":"python:3.11-alpine","python_version":"3.11","os_libc":"alpine (musl)","variant":"default","exit_code":0,"wheel_type":"wheel","failure_reason":null,"install_time_s":null,"import_time_s":0.66,"mem_mb":16.9,"disk_size":"25.2M"},{"runtime":"python:3.11-alpine","python_version":"3.11","os_libc":"alpine (musl)","variant":"default","exit_code":0,"wheel_type":null,"failure_reason":null,"install_time_s":null,"import_time_s":0.67,"mem_mb":13.4,"disk_size":"25.2M"},{"runtime":"python:3.11-slim","python_version":"3.11","os_libc":"slim (glibc)","variant":"default","exit_code":0,"wheel_type":"wheel","failure_reason":null,"install_time_s":2.3,"import_time_s":0.58,"mem_mb":16.9,"disk_size":"26M"},{"runtime":"python:3.11-slim","python_version":"3.11","os_libc":"slim (glibc)","variant":"default","exit_code":0,"wheel_type":null,"failure_reason":null,"install_time_s":null,"import_time_s":0.5,"mem_mb":13.4,"disk_size":"26M"},{"runtime":"python:3.12-alpine","python_version":"3.12","os_libc":"alpine (musl)","variant":"default","exit_code":0,"wheel_type":"wheel","failure_reason":null,"install_time_s":null,"import_time_s":0.6,"mem_mb":16.9,"disk_size":"16.9M"},{"runtime":"python:3.12-alpine","python_version":"3.12","os_libc":"alpine (musl)","variant":"default","exit_code":0,"wheel_type":null,"failure_reason":null,"install_time_s":null,"import_time_s":0.56,"mem_mb":13.1,"disk_size":"16.9M"},{"runtime":"python:3.12-slim","python_version":"3.12","os_libc":"slim (glibc)","variant":"default","exit_code":0,"wheel_type":"wheel","failure_reason":null,"install_time_s":2,"import_time_s":0.59,"mem_mb":16.9,"disk_size":"17M"},{"runtime":"python:3.12-slim","python_version":"3.12","os_libc":"slim (glibc)","variant":"default","exit_code":0,"wheel_type":null,"failure_reason":null,"install_time_s":null,"import_time_s":0.53,"mem_mb":13.1,"disk_size":"17M"},{"runtime":"python:3.13-alpine","python_version":"3.13","os_libc":"alpine (musl)","variant":"default","exit_code":0,"wheel_type":"wheel","failure_reason":null,"install_time_s":null,"import_time_s":0.58,"mem_mb":18.4,"disk_size":"16.6M"},{"runtime":"python:3.13-alpine","python_version":"3.13","os_libc":"alpine (musl)","variant":"default","exit_code":0,"wheel_type":null,"failure_reason":null,"install_time_s":null,"import_time_s":0.52,"mem_mb":13.8,"disk_size":"16.5M"},{"runtime":"python:3.13-slim","python_version":"3.13","os_libc":"slim (glibc)","variant":"default","exit_code":0,"wheel_type":"wheel","failure_reason":null,"install_time_s":2,"import_time_s":0.58,"mem_mb":18.4,"disk_size":"17M"},{"runtime":"python:3.13-slim","python_version":"3.13","os_libc":"slim (glibc)","variant":"default","exit_code":0,"wheel_type":null,"failure_reason":null,"install_time_s":null,"import_time_s":0.52,"mem_mb":13.8,"disk_size":"17M"},{"runtime":"python:3.9-alpine","python_version":"3.9","os_libc":"alpine (musl)","variant":"default","exit_code":0,"wheel_type":"wheel","failure_reason":null,"install_time_s":null,"import_time_s":0.41,"mem_mb":12.1,"disk_size":"22.3M"},{"runtime":"python:3.9-alpine","python_version":"3.9","os_libc":"alpine (musl)","variant":"default","exit_code":0,"wheel_type":null,"failure_reason":null,"install_time_s":null,"import_time_s":0.43,"mem_mb":12.1,"disk_size":"22.3M"},{"runtime":"python:3.9-slim","python_version":"3.9","os_libc":"slim (glibc)","variant":"default","exit_code":0,"wheel_type":"wheel","failure_reason":null,"install_time_s":2.6,"import_time_s":0.39,"mem_mb":12.1,"disk_size":"23M"},{"runtime":"python:3.9-slim","python_version":"3.9","os_libc":"slim (glibc)","variant":"default","exit_code":0,"wheel_type":null,"failure_reason":null,"install_time_s":null,"import_time_s":0.37,"mem_mb":12.1,"disk_size":"23M"}]},"quickstart_checks":{"last_tested":"2026-04-24","tag":null,"tag_description":null,"results":[{"runtime":"python:3.10-alpine","exit_code":0},{"runtime":"python:3.10-slim","exit_code":0},{"runtime":"python:3.11-alpine","exit_code":0},{"runtime":"python:3.11-slim","exit_code":0},{"runtime":"python:3.12-alpine","exit_code":0},{"runtime":"python:3.12-slim","exit_code":0},{"runtime":"python:3.13-alpine","exit_code":0},{"runtime":"python:3.13-slim","exit_code":0},{"runtime":"python:3.9-alpine","exit_code":2},{"runtime":"python:3.9-slim","exit_code":2}]}}