""" 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.""" # Safely get password from session state entered_password = st.session_state.get("password", "") if not entered_password: st.session_state["authenticated"] = False st.session_state["password_incorrect"] = True return # Get password(s) from secrets - try multiple locations stored_password = None # Try auth.password first try: if "auth" in st.secrets and "password" in st.secrets["auth"]: stored_password = str(st.secrets["auth"]["password"]).strip() except Exception: pass # Fallback: try root level password if not stored_password: try: if "password" in st.secrets: stored_password = str(st.secrets["password"]).strip() except Exception: pass # Check single password if stored_password: # Use simple comparison (strip whitespace from both) if entered_password.strip() == stored_password: st.session_state["authenticated"] = True st.session_state["username"] = "user" if "password" in st.session_state: del st.session_state["password"] return # Check multiple users (auth.passwords) try: if "auth" in st.secrets and "passwords" in st.secrets["auth"]: username = st.session_state.get("username_input", "") passwords = st.secrets["auth"]["passwords"] if username in passwords: stored_user_pass = str(passwords[username]).strip() if entered_password.strip() == stored_user_pass: st.session_state["authenticated"] = True st.session_state["username"] = username if "password" in st.session_state: del st.session_state["password"] return except Exception: pass 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('