Flask-Security-Too
Flask-Security-Too quickly adds common security features like user registration, login, roles, and password management to your Flask application. Currently at version 5.8.0, it's the actively maintained successor to the original Flask-Security, frequently releasing updates with fixes and improvements. Despite the 'too' suffix in its PyPI name, it is now the official Flask-Security project under Pallets-Eco.
Common errors
-
ModuleNotFoundError: No module named 'flask_security'
cause You likely installed the package `flask-security-too` but are trying to import from `flask_security_too`, or `flask-security-too` was not installed correctly.fixEnsure `flask-security-too` is installed (`pip install flask-security-too`) and use `from flask_security import ...` for all imports. -
werkzeug.routing.BuildError: Could not build url for endpoint 'security.login'. Did you mean 'static' instead?
cause This usually means that the Flask-Security blueprint (which contains the 'security.login' endpoint) was not properly registered with your Flask application, or the `Security` object was not correctly initialized.fixVerify that `security = Security(app, user_datastore)` is called correctly after your Flask app and user datastore are initialized. Ensure your `SECURITY_REGISTERABLE`, `SECURITY_CONFIRMABLE` settings are consistent with the features you expect. -
Password scheme '<scheme_name>' is not supported.
cause This error comes from Passlib, indicating that the password hashing scheme specified in `SECURITY_PASSWORD_SCHEMES` is either misspelled, not installed, or not supported by your Passlib version.fixCheck `app.config['SECURITY_PASSWORD_SCHEMES']` for correct scheme names (e.g., 'pbkdf2_sha512', 'bcrypt'). Ensure `passlib` is installed and up to date. -
TypeError: 'Security' object is not callable
cause You are trying to call the `Security` object as if it were a function (e.g., `security()`) after it has been initialized, instead of accessing its methods or attributes.fixOnce `security = Security(app, user_datastore)` is called, `security` is an object. Access its functionality via `security.login_manager` or similar attributes, or use decorators like `@security.login_required`.
Warnings
- gotcha The PyPI package name is `flask-security-too`, but the Python module to import from is `flask_security`. Attempting to import from `flask_security_too` will result in a `ModuleNotFoundError`.
- breaking Upgrading from very old `Flask-Security` versions (pre-5.x) may require reviewing your password hashing configuration due to `Passlib` integration changes. Specifically, `SECURITY_PASSWORD_SCHEMES` might need to be adjusted.
- gotcha When using Flask-SQLAlchemy version 3.x or higher, it is recommended to use `SQLAlchemySessionUserDatastore` instead of `SQLAlchemyUserDatastore`. The former uses `db.session` directly, aligning better with modern Flask-SQLAlchemy practices.
- gotcha Ensure `app.config['SECRET_KEY']` and `app.config['SECURITY_PASSWORD_SALT']` are set to long, random, and distinct strings, especially in production environments. Using simple or default values compromises security.
Install
-
pip install flask-security-too Flask-SQLAlchemy -
pip install flask-security-too[email]
Imports
- Security
from flask_security_too import Security
from flask_security import Security
- SQLAlchemySessionUserDatastore
from flask_security import SQLAlchemySessionUserDatastore
- UserMixin
from flask_security import UserMixin
- RoleMixin
from flask_security import RoleMixin
Quickstart
import os
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from flask_security import Security, SQLAlchemySessionUserDatastore, UserMixin, RoleMixin
# Configure Flask app
app = Flask(__name__)
app.config['DEBUG'] = True
app.config['SECRET_KEY'] = os.environ.get('FLASK_SECRET_KEY', 'super-secret-dev-key')
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///test.db'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
app.config['SECURITY_PASSWORD_SALT'] = os.environ.get('SECURITY_PASSWORD_SALT', 'some-random-salt')
# Initialize SQLAlchemy
db = SQLAlchemy(app)
# Define User and Role models
roles_users = db.Table(
'roles_users',
db.Column('user_id', db.Integer, db.ForeignKey('user.id')),
db.Column('role_id', db.Integer, db.ForeignKey('role.id'))
)
class Role(db.Model, RoleMixin):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(80), unique=True)
description = db.Column(db.String(255))
class User(db.Model, UserMixin):
id = db.Column(db.Integer, primary_key=True)
email = db.Column(db.String(255), unique=True)
password = db.Column(db.String(255))
active = db.Column(db.Boolean())
fs_uniquifier = db.Column(db.String(64), unique=True, nullable=False)
roles = db.relationship('Role', secondary=roles_users, backref=db.backref('users', lazy='dynamic'))
# Setup Flask-Security
user_datastore = SQLAlchemySessionUserDatastore(db.session, User, Role)
security = Security(app, user_datastore)
@app.before_first_request
def create_user():
db.create_all()
if not user_datastore.find_user(email='test@example.com'):
user_datastore.create_user(email='test@example.com', password='password')
db.session.commit()
@app.route('/')
def home():
return 'Hello, Flask-Security-Too! Go to /login or /register.'
if __name__ == '__main__':
app.run()