Skip to content

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/login calls your verify_credentials(); on success, the user's id is stored in the signed session cookie.
  • Every subsequent /admin/* request calls your load_user() with that id, and the result is attached to request.state.admin_user -- this is what RBACModelView's permission checks read.
  • POST /admin/logout clears the session.