Simple security for Flask apps
Flask-Security provides mixin classes that define the standard interface for user and role models. These mixins can be used with any ORM or database system to create models compatible with Flask-Security's authentication and authorization system.
The UserMixin class extends Flask-Login's BaseUserMixin and provides additional methods required by Flask-Security.
class UserMixin:
"""Mixin for User model definitions"""
@property
def is_active(self):
"""
Returns True if the user is active.
Returns:
bool: User's active status
"""
def get_auth_token(self):
"""
Returns the user's authentication token.
Returns:
str: Authentication token for API access
"""
def has_role(self, role):
"""
Returns True if the user identifies with the specified role.
Args:
role (str or Role): A role name or Role instance
Returns:
bool: True if user has the role
"""
def get_security_payload(self):
"""
Serialize user object as response payload.
Returns:
dict: User data for API responses
"""The RoleMixin class provides standard comparison methods for role objects.
class RoleMixin:
"""Mixin for Role model definitions"""
def __eq__(self, other):
"""
Compare roles for equality.
Args:
other (str or Role): Role name or Role instance
Returns:
bool: True if roles are equal
"""
def __ne__(self, other):
"""
Compare roles for inequality.
Args:
other (str or Role): Role name or Role instance
Returns:
bool: True if roles are not equal
"""
def __hash__(self):
"""
Return hash of role name.
Returns:
int: Hash value for role
"""The AnonymousUser class represents users who are not logged in.
class AnonymousUser:
"""AnonymousUser definition"""
def __init__(self):
"""Initialize anonymous user with empty roles"""
def has_role(self, *args):
"""
Always returns False for anonymous users.
Returns:
bool: False
"""Flask-Security provides a proxy to access the current logged-in user.
# Current user proxy object
current_user: Union[User, AnonymousUser]from flask_sqlalchemy import SQLAlchemy
from flask_security import UserMixin, RoleMixin
db = SQLAlchemy()
roles_users = db.Table('roles_users',
db.Column('user_id', db.Integer(), db.ForeignKey('user.id')),
db.Column('role_id', db.Integer(), db.ForeignKey('role.id'))
)
class Role(db.Model, RoleMixin):
id = db.Column(db.Integer(), primary_key=True)
name = db.Column(db.String(80), unique=True)
description = db.Column(db.String(255))
class User(db.Model, UserMixin):
id = db.Column(db.Integer, primary_key=True)
email = db.Column(db.String(255), unique=True)
password = db.Column(db.String(255))
active = db.Column(db.Boolean(), default=True)
confirmed_at = db.Column(db.DateTime())
roles = db.relationship('Role', secondary=roles_users, backref='users')
# Optional tracking fields
last_login_at = db.Column(db.DateTime())
current_login_at = db.Column(db.DateTime())
last_login_ip = db.Column(db.String(100))
current_login_ip = db.Column(db.String(100))
login_count = db.Column(db.Integer)from flask_mongoengine import MongoEngine
from flask_security import UserMixin, RoleMixin
db = MongoEngine()
class Role(db.Document, RoleMixin):
name = db.StringField(max_length=80, unique=True)
description = db.StringField(max_length=255)
class User(db.Document, UserMixin):
email = db.StringField(max_length=255, unique=True)
password = db.StringField(max_length=255)
active = db.BooleanField(default=True)
confirmed_at = db.DateTimeField()
roles = db.ListField(db.ReferenceField(Role), default=[])from flask_security import current_user
@app.route('/profile')
def profile():
if current_user.is_authenticated:
return f"Hello {current_user.email}!"
else:
return "Please log in"
@app.route('/admin')
def admin():
if current_user.has_role('admin'):
return "Admin panel"
else:
return "Access denied"You can extend the User model with additional methods:
class User(db.Model, UserMixin):
# ... standard fields ...
def get_full_name(self):
return f"{self.first_name} {self.last_name}"
def is_admin(self):
return self.has_role('admin')
def can_edit_post(self, post):
return self.has_role('admin') or post.author_id == self.idFlask-Security expects your User model to have these attributes:
class User:
"""Required interface for User models"""
id: Any # Primary key
email: str # User's email address
password: str # Hashed password
active: bool # Whether user account is active
roles: List[Role] # User's roles
# Optional fields for additional features
confirmed_at: Optional[datetime] # Email confirmation timestamp
last_login_at: Optional[datetime] # Last login time
current_login_at: Optional[datetime] # Current login time
last_login_ip: Optional[str] # Last login IP address
current_login_ip: Optional[str] # Current login IP address
login_count: Optional[int] # Number of loginsFlask-Security expects your Role model to have these attributes:
class Role:
"""Required interface for Role models"""
name: str # Role name (e.g., 'admin', 'user')
description: Optional[str] # Role descriptiontessl i tessl/pypi-flask-security@3.0.0