Install & Compatibility
Where this runs
tested against v0.4.0 · 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.920 runs
installs and imports cleanly · install 0.0s · import 0.486s · 23.8MB
glibcpy 3.10–3.920 runs
installs and imports cleanly · install 3.1s · import 0.452s · 24MB
22MB installed
● package 22MB
Code
Verified usage
Verified import paths — ran on the pinned version, not inferred.
Principal
✓ from flask_principal import Principal
✗ from flaskext.principal import Principal
The 'flaskext' namespace was used in older Flask extensions but is deprecated.
Identity
✓ from flask_principal import Identity
AnonymousIdentity
✓ from flask_principal import AnonymousIdentity
Permission
✓ from flask_principal import Permission
Need
✓ from flask_principal import Need
RoleNeed
✓ from flask_principal import RoleNeed
UserNeed
✓ from flask_principal import UserNeed
current_identity
✓ from flask_principal import current_identity
identity_changed
✓ from flask_principal import identity_changed
identity_loaded
✓ from flask_principal import identity_loaded
This quickstart demonstrates how to set up Flask-Principal with an example Flask application. It includes routes for logging in/out, a profile page, and an admin dashboard protected by a 'RoleNeed'. The `identity_changed` signal is used to update the current identity, and `identity_loaded` is used to assign `Needs` (like 'admin' role) to the identity. Ensure `FLASK_SECRET_KEY` is set for production.
import os
from flask import Flask, session, g, redirect, url_for
from flask_principal import Principal, Identity, AnonymousIdentity, \
identity_changed, identity_loaded, \
current_identity, \
Need, UserNeed, RoleNeed, Permission
# Initialize Flask app
app = Flask(__name__)
app.secret_key = os.environ.get('FLASK_SECRET_KEY', 'default_secret_key')
# Initialize Flask-Principal
principals = Principal(app)
# Define a permission for 'admin' role
admin_permission = Permission(RoleNeed('admin'))
@app.before_request
def load_identity_from_session():
# Load identity from session or create an anonymous one
if 'identity_id' in session:
g.identity = Identity(session['identity_id'])
# Signal that the identity has loaded
identity_changed.send(app, identity=g.identity)
else:
g.identity = AnonymousIdentity()
@identity_loaded.connect_via(app)
def on_identity_loaded(sender, identity):
# This signal is fired when an identity is loaded (or changed)
# Use this to add Needs to the identity based on your application logic
if identity.id:
# Example: user_id 1 is an admin, others are regular users
if identity.id == 1:
identity.provides.add(RoleNeed('admin'))
identity.provides.add(UserNeed(identity.id)) # All logged-in users get their UserNeed
# For anonymous users, no specific Needs are added here by default
@app.route('/login/<int:user_id>')
def login(user_id):
# Simulate a login: set identity in session and emit signal
session['identity_id'] = user_id
identity_changed.send(app, identity=Identity(user_id))
return f'Logged in as User {user_id}. <a href="{url_for("profile")}">Go to profile</a> <a href="{url_for("admin_dashboard")}">Go to admin dashboard</a>'
@app.route('/logout')
def logout():
# Simulate a logout: clear session and emit anonymous identity signal
if 'identity_id' in session:
del session['identity_id']
identity_changed.send(app, identity=AnonymousIdentity())
return 'Logged out. <a href="{url_for("profile")}">Go to profile</a>'
@app.route('/')
@app.route('/profile')
def profile():
if current_identity.is_authenticated():
return f'Hello, User {current_identity.id}! You are authenticated.' \
f' Role: {"Admin" if admin_permission.can() else "User"}.'
return 'Hello, Anonymous User! You are not logged in.'
@app.route('/admin')
@admin_permission.require() # This decorator enforces the 'admin' permission
def admin_dashboard():
return f'Welcome to the Admin Dashboard, User {current_identity.id}!'
if __name__ == '__main__':
# For local testing, ensure a secret key is set
if 'FLASK_SECRET_KEY' not in os.environ:
print("WARNING: FLASK_SECRET_KEY environment variable not set. Using a default for demonstration.")
app.run(debug=True)
Debug
Known issues
deprecatedFlask-Principal is largely unmaintained, with its last release (0.4.0) dating back to 2012. It may not be compatible with newer Flask versions or Python features, and there are no active security updates. Modern Flask applications are advised to use more actively maintained authentication and authorization libraries like Flask-Login (for authentication) combined with custom decorators for role-based access control, or comprehensive solutions for JWT/session management.fixConsider migrating to Flask-Login for authentication and implementing authorization logic with custom decorators or a more modern library.
affects: <=0.4.0
gotchaFlask-Principal was originally developed for Python 2. While basic functionality might work on Python 3, subtle compatibility issues or unhandled edge cases may arise due to differences in string handling, standard library changes, or other Python 2/3 specific behaviors. The library's `requires_python` metadata is `None`, indicating no explicit Python 3 support was declared.fixThoroughly test on your target Python 3 version if you must use this library. Prefer alternatives for new projects.
affects: All versions on Python 3.x
gotchaFlask-Principal handles *authorization* (what a user can do), not *authentication* (who the user is). You must integrate it with a separate authentication mechanism (e.g., Flask-Login, a custom session manager, or OAuth) to establish the user's identity before Principal can assign and check permissions.fixImplement a robust authentication system to manage user sessions and login states, then use Flask-Principal's `Identity` object to represent the authenticated user.
affects: All versions
gotchaThe signal-based nature of `identity_changed` and `identity_loaded` means they must be sent within an active Flask application context (`app.app_context()` or during a request). Calling `identity_changed.send()` outside of an app context will raise a `RuntimeError`.fixEnsure that `identity_changed.send()` is always called within a request context or an explicitly pushed application context (e.g., `with app.app_context():`).
affects: All versions
Errors
Common errors & fixes
RuntimeError: Working outside of application context.
The `identity_changed.send()` method or other Principal operations were called outside of an active Flask application context (e.g., in a background thread or a script without a context pushed).
fixEnsure all Flask-Principal operations, especially signal emissions, are performed within an active request context or by explicitly pushing an application context: `with app.app_context(): identity_changed.send(...)`.
AssertionError: This Principal extension is not registered to the current application.
Flask-Principal was either not initialized with your Flask application (`principals = Principal(app)` was omitted or called incorrectly), or you are attempting to use Principal features (like `current_identity`) outside of a request where the application context might not be properly set up or associated with the Principal extension.
fixVerify that `principals = Principal(app)` is correctly called during your application setup. If using an app factory, ensure `Principal()` is initialized with `app` *within* the factory or with `Principal().init_app(app)`.
Access denied / Permission not granted / @requires_role decorator not working.
The `current_identity` does not possess the required `Need` for the permission being checked. This often happens if the `identity_loaded` signal handler doesn't correctly add `Needs` to the identity's `provides` set, or if the `Need` definition in the permission does not match what's provided.
fixInspect your `identity_loaded` signal handler to ensure it correctly populates `identity.provides` with the appropriate `Need` objects for the current user. Double-check that the `Need` defined in your `Permission` (e.g., `Permission(RoleNeed('admin'))`) exactly matches the `Need` added to the identity. Upgrade
Version history
0.4.0latest on PyPI · released Jun 14, 2013
Audit
Dependencies
FlaskrequiredCore web framework dependency.