SPARKNET / demo /auth.py
MHamdan's picture
Enhance SPARKNET for TTO automation with new scenarios and security features
76c3b0a
raw
history blame
6.13 kB
"""
Simple Password Authentication for SPARKNET
Provides password-based access control for the Streamlit app.
SECURITY NOTES:
---------------
This module provides basic password authentication suitable for demos
and internal deployments. For production use, consider:
1. ENHANCED AUTHENTICATION:
- Integrate with OAuth/OIDC (Google, Azure AD, Okta)
- Use Streamlit's built-in OAuth support
- Implement multi-factor authentication (MFA)
2. SESSION MANAGEMENT:
- Configure session timeouts (default: browser session)
- Implement session invalidation on logout
- Consider IP-based session binding
3. PASSWORD SECURITY:
- Use strong password requirements
- Implement account lockout after failed attempts
- Store passwords hashed with bcrypt (not SHA-256) for production
4. AUDIT LOGGING:
- Log authentication attempts (success/failure)
- Track user sessions
- Monitor for suspicious activity
GDPR CONSIDERATIONS:
-------------------
- Authentication logs may contain personal data (usernames, IPs)
- Implement data retention policies for auth logs
- Support right-to-erasure for user accounts
- Document authentication processing in GDPR records
PRIVATE DEPLOYMENT:
------------------
For enterprise deployments:
- Integrate with existing identity providers
- Use LDAP/Active Directory for user management
- Implement role-based access control (RBAC)
- Enable single sign-on (SSO)
See SECURITY.md for comprehensive security documentation.
"""
import streamlit as st
import hashlib
import hmac
from typing import Optional
def hash_password(password: str) -> str:
"""Hash a password for secure storage."""
return hashlib.sha256(password.encode()).hexdigest()
def check_password() -> bool:
"""
Show login form and verify password.
Returns True if password is correct, False otherwise.
Configure password in Streamlit secrets:
[auth]
password = "your-secure-password"
Or set multiple users:
[auth]
passwords = { admin = "admin123", user1 = "pass123" }
"""
def password_entered():
"""Check if entered password is correct."""
# Get password(s) from secrets
if "auth" in st.secrets:
# Check single password
if "password" in st.secrets["auth"]:
if hmac.compare_digest(
st.session_state["password"],
st.secrets["auth"]["password"]
):
st.session_state["authenticated"] = True
st.session_state["username"] = "user"
del st.session_state["password"]
return
# Check multiple users
if "passwords" in st.secrets["auth"]:
username = st.session_state.get("username_input", "")
password = st.session_state.get("password", "")
passwords = st.secrets["auth"]["passwords"]
if username in passwords and hmac.compare_digest(
password, passwords[username]
):
st.session_state["authenticated"] = True
st.session_state["username"] = username
del st.session_state["password"]
return
st.session_state["authenticated"] = False
st.session_state["password_incorrect"] = True
# Check if already authenticated
if st.session_state.get("authenticated", False):
return True
# Show login form
st.markdown("""
<style>
.login-container {
max-width: 400px;
margin: 100px auto;
padding: 40px;
background: linear-gradient(135deg, #1a1a2e 0%, #16213e 100%);
border-radius: 15px;
box-shadow: 0 10px 40px rgba(0,0,0,0.3);
}
.login-title {
text-align: center;
color: #4ECDC4;
font-size: 2em;
margin-bottom: 10px;
}
.login-subtitle {
text-align: center;
color: #8b949e;
margin-bottom: 30px;
}
</style>
""", unsafe_allow_html=True)
col1, col2, col3 = st.columns([1, 2, 1])
with col2:
st.markdown('<div class="login-title">🔥 SPARKNET</div>', unsafe_allow_html=True)
st.markdown('<div class="login-subtitle">Document Intelligence Platform</div>', unsafe_allow_html=True)
st.markdown("---")
# Check if we have multi-user setup
has_multi_user = (
"auth" in st.secrets and
"passwords" in st.secrets["auth"]
)
if has_multi_user:
st.text_input(
"Username",
key="username_input",
placeholder="Enter username"
)
st.text_input(
"Password",
type="password",
key="password",
placeholder="Enter password",
on_change=password_entered
)
if st.button("🔐 Login", type="primary", use_container_width=True):
password_entered()
if st.session_state.get("password_incorrect", False):
st.error("😕 Incorrect password. Please try again.")
st.markdown("---")
st.caption("Contact administrator for access credentials.")
return False
def logout():
"""Log out the current user."""
st.session_state["authenticated"] = False
st.session_state["username"] = None
st.rerun()
def get_current_user() -> Optional[str]:
"""Get the current logged-in username."""
return st.session_state.get("username")
def require_auth(func):
"""Decorator to require authentication for a page."""
def wrapper(*args, **kwargs):
if check_password():
return func(*args, **kwargs)
return wrapper
def show_logout_button():
"""Show logout button in sidebar."""
if st.session_state.get("authenticated", False):
with st.sidebar:
st.markdown("---")
user = get_current_user()
st.caption(f"Logged in as: **{user}**")
if st.button("🚪 Logout", use_container_width=True):
logout()