How to Implement Role Based Access in Flask Easily
To implement
role based access in Flask, use extensions like Flask-Login for user sessions and Flask-Principal to manage roles and permissions. Define roles for users and protect routes by checking these roles before allowing access.Syntax
Here is the basic pattern to implement role based access in Flask:
- Use
Flask-Loginto manage user login sessions. - Use
Flask-Principalto define roles and permissions. - Create a
Usermodel with aroleattribute. - Protect routes by checking if the logged-in user has the required role.
python
from flask import Flask, redirect, url_for from flask_login import LoginManager, UserMixin, login_user, login_required, current_user from flask_principal import Principal, Permission, RoleNeed, identity_loaded, Identity, identity_changed app = Flask(__name__) app.secret_key = 'secret' login_manager = LoginManager(app) principals = Principal(app) # Define roles as needs admin_permission = Permission(RoleNeed('admin')) user_permission = Permission(RoleNeed('user')) class User(UserMixin): def __init__(self, id, role): self.id = id self.role = role @login_manager.user_loader def load_user(user_id): # For demo, return a user with role 'admin' or 'user' if user_id == '1': return User('1', 'admin') else: return User(user_id, 'user') @identity_loaded.connect_via(app) def on_identity_loaded(sender, identity): identity.user = current_user if hasattr(current_user, 'role'): identity.provides.add(RoleNeed(current_user.role))
Example
This example shows a Flask app where users have roles and routes are protected based on those roles. Only admins can access the /admin page, while all logged-in users can access /dashboard.
python
from flask import Flask, redirect, url_for, render_template_string from flask_login import LoginManager, UserMixin, login_user, login_required, current_user, logout_user from flask_principal import Principal, Permission, RoleNeed, identity_loaded, Identity, identity_changed app = Flask(__name__) app.secret_key = 'secret' login_manager = LoginManager(app) principals = Principal(app) admin_permission = Permission(RoleNeed('admin')) user_permission = Permission(RoleNeed('user')) class User(UserMixin): def __init__(self, id, role): self.id = id self.role = role @login_manager.user_loader def load_user(user_id): if user_id == '1': return User('1', 'admin') elif user_id == '2': return User('2', 'user') return None @identity_loaded.connect_via(app) def on_identity_loaded(sender, identity): identity.user = current_user if hasattr(current_user, 'role'): identity.provides.add(RoleNeed(current_user.role)) @app.route('/login/<user_id>') def login(user_id): user = load_user(user_id) if user: login_user(user) identity_changed.send(app, identity=Identity(user.id)) return redirect(url_for('dashboard')) return 'User not found', 404 @app.route('/logout') @login_required def logout(): logout_user() return 'Logged out' @app.route('/dashboard') @login_required def dashboard(): return f'Hello {current_user.role}, welcome to your dashboard!' @app.route('/admin') @login_required @admin_permission.require(http_exception=403) def admin(): return 'Welcome Admin! This is the admin panel.' @app.errorhandler(403) def access_forbidden(e): return 'Access denied: You do not have permission to view this page.', 403 if __name__ == '__main__': app.run(debug=True)
Output
Running Flask app. Access /login/1 to login as admin, /login/2 as user. Admin can access /admin, user cannot.
Common Pitfalls
- Not calling
identity_changedafter login, so roles are not set properly. - Forgetting to protect routes with
@login_requiredand role permissions. - Assigning roles incorrectly or not checking roles before allowing access.
- Not handling unauthorized access with proper error messages or redirects.
python
from flask import abort # Wrong way: No role check @app.route('/admin') @login_required def admin_wrong(): return 'Admin panel visible to all logged in users!' # Right way: Check role @app.route('/admin_correct') @login_required @admin_permission.require(http_exception=403) def admin_correct(): return 'Admin panel visible only to admins.'
Quick Reference
- Flask-Login: Manages user sessions and login state.
- Flask-Principal: Manages roles and permissions using
NeedandPermission. - User Model: Must have a
roleattribute to assign roles. - Protect Routes: Use
@login_requiredand@permission.require()decorators. - Identity Signal: Use
identity_loadedto add roles to the current identity.
Key Takeaways
Use Flask-Login to handle user sessions and Flask-Principal to manage roles and permissions.
Assign roles to users and check these roles before allowing access to protected routes.
Always call identity_changed after login to update the user's identity with roles.
Protect routes with both @login_required and role-based permission decorators.
Handle unauthorized access gracefully with error handlers or redirects.