User Models & Authentication
Flask-More-Smorest includes a built-in user system with authentication, roles, and token management.
Overview
The user system provides:
Pre-built
Usermodel with email/password authenticationRole-based access control with
UserRoleandBaseRoleEnumJWT token management with
Tokenobjects (refresh/API tokens)Profile and timestamp mixins for extended user data
Easy extension for custom user models
Basic Usage
Import and use the default user models:
from flask_more_smorest.perms import User, UserRole, BaseRoleEnum
# Create a user
user = User(email="admin@example.com")
user.set_password("secret123")
user.save()
# Assign admin role
UserRole(user=user, role=BaseRoleEnum.ADMIN).save()
# Verify password
if user.is_password_correct("secret123"):
print("Password correct!")
# Check if user has a role
if user.has_role(BaseRoleEnum.ADMIN):
print("User is an admin!")
User Model
The User model provides:
Fields
email: str - Unique email address (required)password: bytes | None - Hashed password storage (set viaset_password)is_enabled: bool - Whether user account is enabled (default: True)id: UUID - Inherited from BasePermsModelcreated_at,updated_at: datetime - Inherited from BasePermsModel
Methods
Password Management:
user = User(email="user@example.com")
# Set password (automatically hashed with bcrypt)
user.set_password("my-secure-password")
# Check password
if user.is_password_correct("my-secure-password"):
print("Correct!")
Role Management:
# Check if user has a specific role
if user.has_role(BaseRoleEnum.ADMIN):
print("Admin user")
# Check if user has any role
if user.has_role():
print("User has at least one role")
# Get all user roles
roles = user.roles # List of UserRole objects
Token Management:
# Get user's tokens
tokens = user.tokens # List of Token objects
Default Roles
BaseRoleEnum enum provides common roles:
from flask_more_smorest.perms import BaseRoleEnum
# Available roles
BaseRoleEnum.SUPERADMIN # Super administrator
BaseRoleEnum.ADMIN # Administrator
BaseRoleEnum.USER # Regular user
Assigning Roles:
from flask_more_smorest.perms import User, UserRole, BaseRoleEnum
user = User.query.filter_by(email="user@example.com").first()
# Add admin role
UserRole(user=user, role=BaseRoleEnum.ADMIN).save()
# Add another role
UserRole(user=user, role=BaseRoleEnum.SUPERADMIN).save()
Removing Roles:
# Find and delete specific role
role = UserRole.query.filter_by(
user_id=user.id,
role=BaseRoleEnum.MODERATOR
).first()
if role:
role.delete()
User Authentication Blueprints
The easiest way to add authentication is with UserBlueprint:
UserBlueprint features:
Auto-generated CRUD endpoints for the configured user model (list, detail, create, update, delete)
Built-in authentication endpoints:
POST /login/andGET /me/Respects all
CRUDBlueprintoptions (url_prefix,methods,skip_methods, etc.)Uses the provided
modelandschemaclasses (defaults toUserandUser.Schema)Automatically marks
POSTas public whenmodel.PUBLIC_REGISTRATIONisTrueSupports multiple instances (e.g., admin vs public endpoints)
from flask_more_smorest import UserBlueprint
from flask_more_smorest.perms import init_fms
# Register all default models - no imports needed!
init_fms()
# Instant authentication endpoints
user_bp = UserBlueprint(register=False)
api.register_blueprint(user_bp)
This automatically provides:
POST /api/users/login/- JWT authenticationGET /api/users/me/- Current user profileFull CRUD endpoints for user management
Enable Public Registration:
from flask_more_smorest import UserBlueprint
from flask_more_smorest.perms.models.defaults import DefaultUser
class PublicUser(DefaultUser):
PUBLIC_REGISTRATION = True # Allow unauthenticated user creation
public_bp = UserBlueprint(model=PublicUser, register=False)
api.register_blueprint(public_bp)
Customize UserBlueprint:
You can either configure methods/skip_methods dictionaries or subclass UserBlueprint.
from flask_more_smorest.crud.crud_blueprint import CRUDMethod
# Custom configuration
custom_bp = UserBlueprint(
model=Employee,
schema=Employee.Schema,
name="auth",
url_prefix="/api/auth/",
skip_methods=[CRUDMethod.DELETE], # Disable user deletion
)
# Override schema/args for CRUD create endpoint (e.g., invite-only signup)
from myapp.schemas import InviteSignupSchema
invite_bp = UserBlueprint(
methods={
CRUDMethod.POST: {
"arg_schema": InviteSignupSchema,
"schema": PublicUser.Schema,
}
}
)
# Subclass for custom login response or validation
class AdminUserBlueprint(UserBlueprint):
def _register_login_endpoint(self) -> None:
super()._register_login_endpoint()
# Additional admin-specific setup here
admin_bp = AdminUserBlueprint(model=AdminUser, schema=AdminUser.Schema)
For deeper customization you can override _register_login_endpoint or _register_current_user_endpoint in a subclass and call super() to retain default behavior.
JWT Authentication (Manual Implementation)
For custom authentication flows, you can manually implement JWT authentication.
Configuration:
from flask import Flask
from flask_more_smorest.perms import Api
app = Flask(__name__)
app.config.update(
SECRET_KEY="your-secret-key",
JWT_SECRET_KEY="your-jwt-secret",
JWT_ACCESS_TOKEN_EXPIRES=3600, # 1 hour
JWT_REFRESH_TOKEN_EXPIRES=2592000, # 30 days
)
api = Api(app) # Automatically initializes JWT
Manual Login Endpoint:
from flask import Blueprint
from flask_jwt_extended import create_access_token, create_refresh_token
from flask_more_smorest.perms import User
auth = Blueprint("auth", __name__)
@auth.route("/login", methods=["POST"])
def login():
data = request.get_json()
user = User.query.filter_by(email=data["email"]).first()
if not user or not user.is_password_correct(data["password"]):
return {"error": "Invalid credentials"}, 401
if not user.is_enabled:
return {"error": "Account disabled"}, 403
access_token = create_access_token(identity=user.id)
refresh_token = create_refresh_token(identity=user.id)
return {
"access_token": access_token,
"refresh_token": refresh_token,
}
Protected Endpoints:
from flask_jwt_extended import jwt_required, get_jwt_identity
@auth.route("/profile")
@jwt_required()
def profile():
user_id = get_jwt_identity()
user = User.get_by_or_404(id=user_id)
return UserSchema().dump(user)
Token Management
Token Model
The Token model stores refresh/API tokens and delegates permissions to the owning user. Tokens inherit UserOwnershipMixin with __delegate_to_user__ = True so permissions mirror the owning user’s permissions.
Fields:
token: str - Token value (refresh token, API key, etc.)description: str | None - Optional description for UI displayexpires_at: datetime | None - Expiration timestamprevoked/revoked_at: Track revocation state
from flask_more_smorest.perms import Token
from flask_jwt_extended import create_refresh_token
# Create and store token
refresh_token = create_refresh_token(identity=user.id)
token = Token(
user_id=user.id,
token=refresh_token,
description="CLI token",
expires_at=datetime.utcnow() + timedelta(days=30)
)
token.save()
# Revoke token
token.update(revoked=True, revoked_at=datetime.utcnow())
# Clean up expired tokens
Token.query.filter(
Token.expires_at < datetime.utcnow()
).delete()
Extending User Model
Create custom user models by inheriting from User:
Basic Extension:
from flask_more_smorest.perms import User
from flask_more_smorest.sqla import db
from sqlalchemy.orm import Mapped, mapped_column
class Employee(DefaultUser):
employee_id: Mapped[str] = mapped_column(
db.String(32), unique=True, nullable=False
)
department: Mapped[str] = mapped_column(db.String(100))
hire_date: Mapped[datetime] = mapped_column(db.DateTime)
With Profile Mixin:
from flask_more_smorest.perms import User, ProfileMixin
class Customer(ProfileMixin, User):
loyalty_points: Mapped[int] = mapped_column(db.Integer, default=0)
# ProfileMixin adds: first_name, last_name, phone, address, etc.
With Timestamps Mixin:
from flask_more_smorest.perms import User, TimestampMixin
class Member(TimestampMixin, User):
# Table name automatically set to "member"
membership_level: Mapped[str] = mapped_column(db.String(20))
# TimestampMixin adds: created_at, updated_at
Multi-Tenant Users:
from flask_more_smorest.perms import User, HasUserMixin
import uuid
class TenantUser(HasUserMixin, User):
# Table name automatically set to "tenant_user"
# HasUserMixin adds user_id and user relationship
# Customize to support multi-tenant patterns as needed
Profile Mixin
ProfileMixin adds common profile fields:
from flask_more_smorest.perms import ProfileMixin, User
class UserProfile(ProfileMixin, User):
# Table name automatically set to "user_profile"
pass
Fields added by ProfileMixin:
first_name: strlast_name: strphone: str (optional)address: str (optional)city: str (optional)state: str (optional)postal_code: str (optional)country: str (optional)
Domain/Multi-Tenancy
Multi-tenant patterns are typically implemented using Domain and UserRole (roles scoped to domains) or your own mixins.
See Custom User Context Integration for integrating existing multi-tenant models.
Custom Roles
Define custom roles for your application:
import enum
from flask_more_smorest.perms import UserRole, User
class CustomRole(enum.StrEnum):
SUPER_ADMIN = "super_admin"
EDITOR = "editor"
VIEWER = "viewer"
CONTRIBUTOR = "contributor"
# Assign custom role
user = User.get_by(email="editor@example.com")
UserRole(user=user, role=CustomRole.EDITOR).save()
# Check custom role
if user.has_role(CustomRole.EDITOR):
print("User is an editor")
Permission Integration
User models integrate with the permission system:
from flask_more_smorest.perms import (
BasePermsModel,
HasUserMixin,
UserOwnershipMixin,
)
class UserDocument(HasUserMixin, UserOwnershipMixin, BasePermsModel):
# Table name automatically set to "user_document"
# Uses default: __delegate_to_user__ = False (simple ownership)
title: Mapped[str] = mapped_column(db.String(200))
content: Mapped[str] = mapped_column(db.Text)
# Permissions automatically enforced:
# - Users can only access their own documents
# - Admins can access all documents
Example: Complete Auth System
Here’s a complete authentication system:
from flask import Flask, Blueprint, request
from flask_jwt_extended import (
create_access_token,
create_refresh_token,
jwt_required,
get_jwt_identity,
)
from flask_more_smorest import init_db
from flask_more_smorest.perms import (
Api,
User,
UserRole,
BaseRoleEnum,
CRUDBlueprint,
)
app = Flask(__name__)
app.config.update(
SQLALCHEMY_DATABASE_URI="sqlite:///app.db",
SECRET_KEY="secret",
JWT_SECRET_KEY="jwt-secret",
)
init_db(app)
api = Api(app)
# Auth blueprint
auth = Blueprint("auth", __name__, url_prefix="/auth")
@auth.route("/register", methods=["POST"])
def register():
data = request.get_json()
if User.query.filter_by(email=data["email"]).first():
return {"error": "Email already exists"}, 400
user = User(email=data["email"])
user.set_password(data["password"])
user.save()
# Assign default user role
UserRole(user=user, role=BaseRoleEnum.USER).save()
return {"message": "User created"}, 201
@auth.route("/login", methods=["POST"])
def login():
data = request.get_json()
user = User.query.filter_by(email=data["email"]).first()
if not user or not user.is_password_correct(data["password"]):
return {"error": "Invalid credentials"}, 401
access_token = create_access_token(identity=str(user.id))
refresh_token = create_refresh_token(identity=str(user.id))
return {
"access_token": access_token,
"refresh_token": refresh_token,
}
@auth.route("/profile")
@jwt_required()
def profile():
user_id = get_jwt_identity()
user = User.get_by_or_404(id=user_id)
return {
"id": str(user.id),
"email": user.email,
"is_enabled": user.is_enabled,
"roles": [role.role for role in user.roles],
}
app.register_blueprint(auth)
# Users management (admin only)
users_bp = CRUDBlueprint(
"users",
__name__,
model=User,
schema=User.Schema,
url_prefix="/api/users/",
methods={
CRUDMethod.DELETE: {"admin_only": True},
CRUDMethod.PATCH: {"admin_only": True},
},
)
api.register_blueprint(users_bp)
Next Steps
Learn about Permissions System for access control
See CRUD Blueprints for creating user management endpoints
Check the API Reference reference for detailed model documentation