0
0
FlaskHow-ToBeginner · 4 min read

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-Login to manage user login sessions.
  • Use Flask-Principal to define roles and permissions.
  • Create a User model with a role attribute.
  • 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_changed after login, so roles are not set properly.
  • Forgetting to protect routes with @login_required and 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 Need and Permission.
  • User Model: Must have a role attribute to assign roles.
  • Protect Routes: Use @login_required and @permission.require() decorators.
  • Identity Signal: Use identity_loaded to 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.