Authentication Setup¶
Kloudmint doesn't know how your users/passwords are stored -- you subclass
KloudmintAuth and implement two lookups. It never touches credentials
directly; it just calls into your code.
1. Subclass KloudmintAuth¶
# admin_auth.py
from sqlalchemy.orm import joinedload
from kloudmint import KloudmintAuth
from models import User, Role
class AdminAuth(KloudmintAuth):
def __init__(self, secret_key: str, session_factory):
super().__init__(secret_key=secret_key)
self.session_factory = session_factory
def verify_credentials(self, username: str, password: str):
"""Called on login. Return the user object on success, None on failure."""
with self.session_factory() as db:
user = db.query(User).filter_by(email=username).first()
if user and user.check_password(password):
db.expunge(user)
return user
return None
def load_user(self, user_id):
"""Called on every admin request to re-hydrate the logged-in user.
Eager-load role + permissions here to avoid a query per permission check."""
with self.session_factory() as db:
user = (
db.query(User)
.options(joinedload(User.role).joinedload(Role.permissions))
.get(user_id)
)
if user:
db.expunge(user)
return user
Important: always eager-load role and role.permissions in
load_user -- has_permission() reads user.role.permissions on every
request, and without eager loading that's an extra query (or a
DetachedInstanceError, since the session used to fetch the user is closed
by the time the request handler runs).
2. Your User model needs a password check¶
Kloudmint doesn't provide password hashing -- use whatever your project
already uses (passlib, werkzeug.security, or a simple hashlib.pbkdf2_hmac
as in the example apps). The only contract is that your model exposes
set_password() / check_password(), or equivalent, for AdminAuth to call.
3. Wire it into the engine¶
# main.py
from kloudmint import Kloudmint
from admin_auth import AdminAuth
admin = Kloudmint(
app,
engine,
auth_backend=AdminAuth(secret_key=settings.SECRET_KEY, session_factory=SessionLocal),
base_url="/admin",
title="My Admin",
)
secret_key signs the session cookie -- use a real secret from your app's
config/env, not a hardcoded value, outside of local development.
How login actually works under the hood¶
POST /admin/logincalls yourverify_credentials(); on success, the user's id is stored in the signed session cookie.- Every subsequent
/admin/*request calls yourload_user()with that id, and the result is attached torequest.state.admin_user-- this is whatRBACModelView's permission checks read. POST /admin/logoutclears the session.