Flask-Login
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.
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.
- 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.
- 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.
- 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.
- deprecated Older tutorials or examples might use `from flask.ext.login import ...`. This import path is deprecated and will fail in modern Flask applications.
- 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.
Install
-
pip install flask-login
Imports
- LoginManager
from flask_login import LoginManager
- UserMixin
from 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
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)