""" 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(""" """, unsafe_allow_html=True) col1, col2, col3 = st.columns([1, 2, 1]) with col2: st.markdown('
🔥 SPARKNET
', unsafe_allow_html=True) st.markdown('
Document Intelligence Platform
', 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()