import streamlit as st import streamlit.components.v1 as components import html import os from urllib.parse import urlparse _COMPONENT_NAME = "google_login" _COMPONENT_PATH = os.path.join(os.path.dirname(__file__), "auth_component") _google_login = components.declare_component( name=_COMPONENT_NAME, path=_COMPONENT_PATH, ) def google_login_button(force_logout=False): """ Renders the Google Sign-In button and handles Firebase Auth. Requires st.secrets["firebase"] to be configured. """ if "firebase" not in st.secrets: st.error("⚠️ Firebase configuration missing in `st.secrets`. Please add it to `.streamlit/secrets.toml`.") st.stop() firebase_secrets = st.secrets["firebase"] required_keys = ["apiKey", "projectId"] optional_keys = ["authDomain", "storageBucket", "messagingSenderId", "appId"] missing_keys = [key for key in required_keys if not str(firebase_secrets.get(key, "")).strip()] if missing_keys: raise RuntimeError( "Firebase configuration is missing required key(s): " + ", ".join(missing_keys) + ". Please set non-empty values in st.secrets['firebase'] before starting authentication." ) firebase_config = {key: firebase_secrets[key] for key in required_keys} firebase_config.update({key: firebase_secrets[key] for key in optional_keys if str(firebase_secrets.get(key, "")).strip()}) user_data = _google_login(config=firebase_config, force_logout=force_logout, key="google_login_component") return user_data def require_auth(): """ Must be called at the top of the main app. If not logged in, it renders the login page and stops execution. """ if "user" not in st.session_state: st.session_state.user = None force_logout = st.session_state.pop("force_logout", False) if not st.session_state.user: # Hide sidebar and header while on login page st.markdown( """ """, unsafe_allow_html=True ) user_data = google_login_button(force_logout=force_logout) if user_data: st.session_state.user = user_data st.rerun() st.stop() def render_user_profile(): """Renders the user profile and logout button in the sidebar as a glassmorphic card.""" user = st.session_state.get("user") if not user: return display_name = html.escape(str(user.get("displayName") or "User"), quote=True) email = html.escape(str(user.get("email") or ""), quote=True) avatar_url = user.get("photoURL") parsed_avatar_url = urlparse(avatar_url) if avatar_url else None safe_avatar_url = ( html.escape(avatar_url, quote=True) if parsed_avatar_url and parsed_avatar_url.scheme in {"http", "https"} and parsed_avatar_url.netloc else None ) st.markdown( """ """, unsafe_allow_html=True ) with st.container(border=True): col1, col2, col3 = st.columns([1, 3, 1], gap="small") with col1: if safe_avatar_url: st.markdown(f'