Estructurar permisos en un SaaS – Implementando roles en Flask - Episodio 25

Estucturar permisos en un SaaS es importante.
De hecho, es clave!
Por eso, hoy vengo a hablar sobre cómo implenté roles en Flask.
🚀 ¿Por qué necesitamos un sistema de roles en nuestra API? 📅 Diario de un SaaS
A medida que mi proyecto avanzaba, me encontré con un problema evidente: no todos los usuarios deben tener los mismos permisos.
La plataforma necesitaba un sistema de acceso estructurado, porque sin él:
🔴 Cualquiera podría modificar datos sensibles.
🔴 No habría control sobre las acciones dentro del sistema.
🔴 La seguridad y la escalabilidad se verían comprometidas.
Era obvio que necesitaba implementar roles y permisos en Flask.
Así que me puse manos a la obra.
En este post te cuento cómo lo hice paso a paso, los errores que encontré y cómo los solucioné. 🔥
🔍 Definiendo los roles del sistema
Antes de meterme con el código, decidí que lo primero era definir claramente los roles.
Porque claro, un sistema sin una estructura bien pensada es un desastre en potencia.
Así que tracé estos tres perfiles principales:
1️⃣ Admin → Tiene control total. Puede gestionar usuarios, modificar registros y acceder a todas las funciones del sistema.
2️⃣ Editor → Puede gestionar su propio contenido, modificar registros que le pertenezcan y ver estadísticas.
3️⃣ Usuario estándar → Solo puede consultar información, sin permisos para realizar cambios.
Con esto claro, pasé a la parte técnica.
Es decir, me bajé al barro a ensuciarme lo máximo!
Así que aposté por Flask-JWT-Extended sería mi herramienta clave para la autenticación...
Y no solo eso, sino que necesitaría una forma de validar los permisos en cada endpoint.
🛠️ Implementando roles en Flask con JWT y middleware
Para que cada usuario tuviera un rol asignado, primero agregué un campo role en la base de datos.
✅ 1. Agregar un campo role a la base de datos
Ejecuté esta migración en PostgreSQL:
ALTER TABLE users ADD COLUMN role VARCHAR(20) DEFAULT 'usuario';
De esta manera, cualquier nuevo usuario registrado tendría el rol de usuario por defecto, salvo que se le asignara otro rol manualmente.
✅ 2. Modificar el registro de usuarios para asignar roles
Luego, modifiqué el endpoint de registro para permitir la asignación de roles de manera segura.
@app.route('/register', methods=['POST'])
def register():
data = request.get_json()
username = data.get('username')
password = generate_password_hash(data.get('password'))
role = data.get('role', 'usuario') # Por defecto, asigna el rol de usuario
cursor.execute("INSERT INTO users (username, password, role) VALUES (%s, %s, %s)", (username, password, role))
db.commit()
return jsonify({'message': 'Usuario registrado correctamente'}), 201
Con esto, ya podía registrar usuarios con distintos roles, pero faltaba lo más importante: controlar los accesos a las rutas de la API según el rol asignado.
✅ 3. Middleware para restringir accesos por rol
Aquí es donde entra en juego Flask-JWT-Extended. Creé un decorador para validar roles antes de acceder a los endpoints:
from flask_jwt_extended import get_jwt_identity, verify_jwt_in_request
from functools import wraps
# Middleware para verificar roles
def role_required(required_role):
def decorator(f):
@wraps(f)
def wrapper(*args, **kwargs):
verify_jwt_in_request()
current_user = get_jwt_identity()
cursor.execute("SELECT role FROM users WHERE username = %s", (current_user,))
user_role = cursor.fetchone()[0]
if user_role != required_role:
return jsonify({'error': 'No tienes permiso para acceder a este recurso'}), 403
return f(*args, **kwargs)
return wrapper
return decorator
Este decorador se usa en las rutas que requieren permisos específicos.
Por lo que si un usuario intenta acceder a una ruta sin el rol adecuado, recibe un error 403 y se le deniega el acceso.
✅ 4. Proteger rutas según el rol
Ahora, con el middleware en su lugar, proteger las rutas fue tan simple como aplicar el decorador @role_required():
@app.route('/admin/dashboard', methods=['GET'])
@role_required('admin')
def admin_dashboard():
return jsonify({'message': 'Bienvenido al panel de administración'}), 200
@app.route('/editor/publicaciones', methods=['POST'])
@role_required('editor')
def crear_publicacion():
return jsonify({'message': 'Publicación creada exitosamente'}), 201
Con esto:
✅ Los administradores tienen control total.
✅ Los editores pueden gestionar sus propios contenidos.
✅ Los usuarios estándar tienen acceso limitado.
❌ Errores encontrados y cómo los solucioné
🔴 AttributeError: 'NoneType' object is not subscriptable en get_jwt_identity()
Solución:
- Asegurar que el token JWT se envía en la cabecera de la solicitud.
- Usar
verify_jwt_in_request()antes deget_jwt_identity()para evitar errores.
🔴 403 Forbidden en rutas protegidas para usuarios con permisos correctos
Solución:
- Verificar que el campo
roleen la base de datos está correctamente asignado. - Revisar que el decorador
@role_requiredestá llamando correctamente la función protegida.

💡 Aprendizajes del proceso
🔹 Definir roles desde el inicio evita problemas en el futuro
Si dejas esto para después, tendrás que reestructurar toda tu lógica de negocio cuando crezca la aplicación.
🔹 Un middleware bien diseñado simplifica la gestión de permisos
Con un decorador como @role_required, controlar accesos es más limpio y escalable.
🔹 JWT + roles es una combinación poderosa para la seguridad
El uso de tokens JWT combinados con roles en la base de datos proporciona una autenticación robusta y segura.
🚀 Próximos pasos
Con los roles funcionando, el siguiente paso es optimizar el rendimiento del backend y prepararlo para escalar sin problemas. 🔥
➡️ ¿Cómo gestionas roles y permisos en tu SaaS?
¿Has tenido problemas con autenticación en Flask?
Cuéntamelo en los comentarios.
Si quieres conocer otros artículos parecidos a Estructurar permisos en un SaaS – Implementando roles en Flask - Episodio 25 puedes visitar la categoría Proyecto-IA.

Deja una respuesta