Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/ElthonJohan/Sistema-MRP/llms.txt

Use this file to discover all available pages before exploring further.

Sistema MRP uses bcrypt password hashing, server-side sessions stored in the user_sessions database table, and URL-based token passing via st.query_params["token"]. Two roles — superadmin and cliente — are enforced by guard functions called at the top of every page.

Login

Passwords are hashed with bcrypt at 12 rounds. On a successful login, the server creates a record in the user_sessions table and places the opaque session token in the URL as st.query_params["token"]. The token is the only credential the browser carries forward.
# Simplified login flow
token = create_session(user_id=user.id, role=user.role)
st.query_params["token"] = token
st.switch_page("pages/dashboard.py")  # or admin.py for superadmin
The original implementation attempted to persist the token via document.cookie from JavaScript. This was abandoned because browsers block document.cookie when a page is loaded from a data: URL iframe (as Streamlit Cloud does). The current implementation relies solely on st.query_params["token"].

Session persistence

init_session() in utils/session_manager.py is called at the top of every page (via the guard functions). It reads the token from the URL, looks up the matching row in user_sessions, and populates st.session_state with user_id, username, and role.
1

Read token from URL

init_session() reads st.query_params.get("token"). If absent, the session is considered invalid.
2

Validate against database

The token is looked up in user_sessions. A valid session must exist and must not be expired.
3

Check inactivity expiry

get_valid_session() enforces a 30-minute inactivity window. If last_activity is more than 30 minutes ago the session is invalidated server-side and the user is redirected to login.
4

Populate session state

On success, st.session_state is populated with user_id, username, and role. st.switch_page() preserves this state across page navigations without a full reload.

Account lockout

To limit brute-force attacks, failed login attempts are counted per account:
  • 5 consecutive failed attempts locks the account for 15 minutes.
  • The lockout is tracked server-side in the database; clearing cookies does not bypass it.
  • After 15 minutes the counter resets automatically and the account unlocks.

Roles

Two roles exist, stored in User.role, propagated to UserSession.role, and available at runtime in st.session_state.role.
RoleDefault landing pageData scope
superadminpages/admin.pyGlobal — all clients visible
clientepages/dashboard.pyScoped to own warehouses (Warehouse.owner_id == user_id)
The superadmin account is seeded by init_db.py with username superadmin and initial password Admin123!. It cannot be deleted.

Protecting a page

Call one of the guard functions as the very first statement in any page file. Do not add logic before the guard.
# pages/requirements.py
from utils.auth import require_cliente, get_current_user_id
from database import SessionLocal
import services.requirement_service as req_svc

require_cliente()  # redirects if not authenticated or if superadmin (not impersonating)

db = SessionLocal()
try:
    owner_id = get_current_user_id()
    warehouse_ids = warehouse_service.get_owner_warehouse_ids(db, owner_id)
    requirements, total = req_svc.get_requirements(db, skip=0, limit=50, warehouse_ids=warehouse_ids)
finally:
    db.close()
GuardUse on
require_login()Pages accessible to any authenticated user
require_cliente()All operational pages (requirements, dispatch, inventory, …)
require_superadmin()Admin-only pages such as pages/access_logs.py

Impersonation

A superadmin can click Visualizar on pages/admin.py to view the application as a specific client. This sets st.session_state["impersonating"] = True and st.session_state["impersonating_user_id"] to the target client’s ID. get_current_user_id() automatically returns impersonating_user_id while impersonation is active, so every warehouse query is scoped to the impersonated client without any additional changes in the service layer. require_cliente() also allows the superadmin through when impersonating is True.

Build docs developers (and LLMs) love