Lightweight Role-Based Access Control (RBAC) library for Streamlit applications.
Inspired by Spring Security's authorization API,
streamlit-rbac provides a thin, declarative layer for role-based access control
— without pulling in authentication, user management, or any heavyweight framework.
- Pure function core —
has_role(),has_any_role(),has_all_roles()are stateless and side-effect-free, making them trivial to unit test. - Declarative decorator —
@require_roles()guards functions with a single line. - Streamlit integration —
authorize_page()stops unauthorized page rendering viast.stop(). - Bring your own auth — inject any
role_loaderfunction to resolve roles from IdP tokens, session state, databases, or anything else. - Zero required dependencies — core functions use only the Python standard library. Streamlit is an optional dependency.
+---------------------------------------------+
| Streamlit Integration | authorize_page()
| (optional dependency) |
+---------------------------------------------+
| Decorator Layer | @require_roles()
+---------------------------------------------+
| Core Functions | has_role()
| (pure, zero dependencies) | has_any_role()
| | has_all_roles()
+---------------------------------------------+
Each layer depends only on the one below it. Core functions work anywhere — no Streamlit required.
pip install streamlit-rbac
# With Streamlit integration
pip install streamlit-rbac[streamlit]Requires Python 3.11+
from streamlit_rbac import has_role, has_any_role, has_all_roles
# Single role check
has_role("Admin", user_roles=["Admin", "User"]) # True
has_role("Admin", user_roles=["User"]) # False
# Any of these roles (OR)
has_any_role("Admin", "Manager", user_roles=["Manager"]) # True
# All of these roles (AND)
has_all_roles("Admin", "Auditor", user_roles=["Admin", "Auditor"]) # True
has_all_roles("Admin", "Auditor", user_roles=["Admin"]) # FalseInstead of passing roles directly, supply a callable that returns them:
from streamlit_rbac import has_role
def get_user_roles() -> list[str]:
# Fetch from IdP, database, session, etc.
return ["Admin", "User"]
has_role("Admin", role_loader=get_user_roles) # True
user_rolesandrole_loaderare mutually exclusive — pass one or the other, never both.
from streamlit_rbac import require_roles
@require_roles("Admin", role_loader=get_user_roles)
def delete_user(user_id: str) -> None:
...Raises PermissionError if the user lacks the required role.
An optional on_denied callback runs before the error:
@require_roles("Admin", role_loader=get_user_roles, on_denied=log_violation)
def delete_user(user_id: str) -> None:
...import streamlit as st
from streamlit_rbac import authorize_page
def get_user_roles() -> list[str]:
claims = st.session_state.get("token_claims", {})
return claims.get("roles", [])
# Place at the top of each page script
authorize_page("Admin", role_loader=get_user_roles)
st.title("Admin Page") # Only rendered if authorizedIf the user lacks the required role, authorize_page displays an error message
and calls st.stop() — nothing below it executes.
Mix page guards with inline checks for fine-grained control:
from streamlit_rbac import authorize_page, has_role
authorize_page("User", "Admin", role_loader=get_user_roles)
st.title("Dashboard")
# Only SuperAdmins see this section
if has_role("SuperAdmin", role_loader=get_user_roles):
with st.expander("System Settings"):
st.write("Dangerous operations...")| Function | Description |
|---|---|
has_role(required, *, user_roles, role_loader) |
Single role check |
has_any_role(*required, *, user_roles, role_loader) |
Any of the given roles (OR) |
has_all_roles(*required, *, user_roles, role_loader) |
All of the given roles (AND) |
All return bool.
All raise ValueError if both or neither of user_roles / role_loader are provided.
Edge cases:
has_any_role() with no required roles returns False.
has_all_roles() with no required roles returns True (vacuous truth).
| Function | Description |
|---|---|
@require_roles(*roles, *, user_roles, role_loader, on_denied) |
Guard a function (OR logic). Raises PermissionError on denial. |
| Function | Description |
|---|---|
authorize_page(*roles, *, role_loader, login_url, denied_message) |
Page-level guard. Calls st.stop() on denial. |
| Type | Definition |
|---|---|
RoleLoader |
Callable[[], Iterable[str]] |
OnDeniedHandler |
Callable[[], None] |
| Principle | Detail |
|---|---|
| Authorization only | No authentication, no user management, no database schemas. This library answers one question: does this user have the required role? |
| Developer stays in control | Role resolution is entirely your responsibility via role_loader. The library never dictates where roles come from. |
| Pure core, optional integration | Core functions have zero dependencies. Streamlit is only imported when you use authorize_page(). |
MIT