Role-Based Access Control & Custom Decorators0%
Modular Routing with Flask Blueprints

Role-Based Access Control & Custom Decorators

Beginner12 min read•Updated: Jul 11, 2026
Study MaterialsCheatsheet

Role-Based Access Control & Custom Decorators

While authentication answers "Who are you?", authorization answers "What are you allowed to do?". A student should not be able to access the admin portal, and an instructor should not be able to delete another instructor's course.

In Flask, Role-Based Access Control (RBAC) is cleanly implemented using Python Decorators combined with functools.wraps.


1. Defining User Roles on the Model

First, attach role attributes or an Enum to your User model:

Python
from flask_sqlalchemy import SQLAlchemy
from flask_login import UserMixin
 
db = SQLAlchemy()
 
class User(UserMixin, db.Model):
__tablename__ = "users"
 
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(50), unique=True, nullable=False)
role = db.Column(db.String(20), default="student", nullable=False) # 'student', 'instructor', 'admin'
 
def is_admin(self):
return self.role == "admin"
 
def is_instructor(self):
return self.role in ("instructor", "admin")

2. Crafting Custom Authorization Decorators

A custom decorator wraps a view function and checks permission conditions before invoking the original handler. Use functools.wraps to preserve function signatures and docstrings (essential for Flask routing!):

Python
from functools import wraps
from flask import abort
from flask_login import current_user
 
def admin_required(f):
@wraps(f)
def decorated_function(*args, **kwargs):
# 1. Check if user is logged in
if not current_user.is_authenticated:
abort(401) # Unauthorized
# 2. Check if user holds the admin role
if not current_user.is_admin():
abort(403) # Forbidden: authenticated, but insufficient privileges!
return f(*args, **kwargs)
return decorated_function

3. Parameterized Role Guard Decorator

For enterprise applications with multiple roles, create a reusable decorator that accepts permissible role names:

Python
def roles_required(*allowed_roles):
def decorator(f):
@wraps(f)
def decorated_function(*args, **kwargs):
if not current_user.is_authenticated:
abort(401)
if current_user.role not in allowed_roles:
abort(403, description=f"Access requires one of these roles: {allowed_roles}")
return f(*args, **kwargs)
return decorated_function
return decorator

4. Applying Decorators to Protected Routes

[!IMPORTANT] Decorator Order Matters: The @app.route() decorator must always be on the outside (top), followed by authentication/authorization decorators:
Python
from flask import Flask, render_template
 
app = Flask(__name__)
 
# Only accessible by system administrators
@app.route("/admin/dashboard")
@admin_required
def admin_panel():
return render_template("admin/dashboard.html")
 
# Accessible by instructors and admins
@app.route("/courses/create", methods=["GET", "POST"])
@roles_required("instructor", "admin")
def create_course():
return render_template("instructor/create_course.html")

Interactive Quiz

Practice Quiz

Question 1
What HTTP status code should be returned when an authenticated user attempts to access a resource they do not have permissions for?
Question 2
Why is @wraps(f) from functools required when writing custom Flask view decorators?
Question 3
What is the correct ordering when applying @app.route and a custom permission decorator?
Question 4
What does calling abort(403) do inside a decorator?
Question 5
How can UI navigation links be conditionally hidden from non-admin users in Jinja2 templates?
Next Lesson

Modular Routing with Flask Blueprints

Continue learning with hands-on practice, examples, and exercises in the upcoming topic.

Practice Quiz

Test your understanding of this lesson with 5 questions. Each question has one correct answer.

PrevNext