""" Google OAuth token verification and lightweight user store. Uses google-auth to verify JWT credentials issued by Google Identity Services. User profiles are stored in a JSON file (/tmp) — ephemeral on HF Spaces, which is acceptable for a demo. Client-side localStorage persists the session. """ import json import os from pathlib import Path from datetime import datetime, timezone from google.oauth2 import id_token from google.auth.transport import requests as google_requests GOOGLE_CLIENT_ID = os.getenv("GOOGLE_CLIENT_ID", "") USER_STORE_PATH = Path(os.getenv("USER_STORE_PATH", "/tmp/urbanflow_users.json")) def _load_users() -> dict: if USER_STORE_PATH.exists(): return json.loads(USER_STORE_PATH.read_text()) return {} def _save_users(users: dict): USER_STORE_PATH.write_text(json.dumps(users, indent=2)) def verify_google_token(credential: str) -> dict: """ Verify a Google ID token (JWT) and return the decoded payload. Raises ValueError on invalid/expired tokens. """ idinfo = id_token.verify_oauth2_token( credential, google_requests.Request(), GOOGLE_CLIENT_ID, ) return { "email": idinfo["email"], "name": idinfo.get("name", ""), "picture": idinfo.get("picture", ""), } def get_or_create_user(email: str, name: str, picture: str) -> dict: """ Look up a user by email. If they don't exist, create a stub record. Returns the user record with a `new_user` flag. """ users = _load_users() is_new = email not in users if is_new: users[email] = { "username": "", "name": name, "picture": picture, "created_at": datetime.now(timezone.utc).isoformat(), } _save_users(users) user = users[email] return { "email": email, "username": user.get("username", ""), "name": user.get("name", name), "picture": user.get("picture", picture), "new_user": is_new or not user.get("username"), } def set_username(email: str, username: str) -> bool: """ Save a display username for a first-time user. Returns True on success, False if the user record doesn't exist. """ users = _load_users() if email not in users: return False users[email]["username"] = username.strip() _save_users(users) return True