boardgames_tracker / README.md
RandomCatLover's picture
Wire app.py/secrets/requirements to the new cookie-based token store
2f83d3e
|
Raw
History Blame Contribute Delete
4.43 kB
metadata
title: Boardgames Tracker
emoji: ๐Ÿš€
colorFrom: red
colorTo: red
sdk: docker
app_port: 8501
tags:
  - streamlit
pinned: false
short_description: Streamlit template space
license: mit

Welcome to Streamlit!

Edit /src/streamlit_app.py to customize this app to your heart's desire. :heart:

If you have any questions, checkout our documentation and community forums.

Persistence setup (Google login + Sheets)

The app works fully anonymously with no setup. Logging in with Google (button in the sidebar) additionally persists your players and round history to a Google Sheet -- created directly in your own Drive, owned by you.

There's no service account and no shared storage involved: Drive access uses the drive.file scope, so the app can only create/edit the one spreadsheet it creates for you (BoardGameTracker - {your email}), nothing else in your Drive. Because it's created by your own account, it uses your own storage -- there's no "service account has 0 storage quota" issue to work around.

Trade-off: since the app stays in Google's "Testing" publishing status (no Google security review needed for a small friend group), each person who connects Drive must first be added as a test user in the OAuth consent screen, and will see an "unverified app" warning the first time they click through.

Login vs. Drive access are two separate steps. st.login() (sidebar button) only ever handles identity -- it deliberately never exposes a refresh_token to app code, no matter what scope you ask for. So once logged in, a separate one-time Connect Google Drive button appears in the sidebar; clicking it runs its own OAuth exchange (access_type=offline + prompt=consent) to force Google to hand back a refresh_token, which gets encrypted and stored in a long-lived browser cookie (via extra-streamlit-components' CookieManager, since Streamlit page code has no direct access to set real HttpOnly cookies). From then on, on that same browser, the app silently mints a fresh access token from the stored refresh_token -- no repeat consent screen -- until it's revoked (e.g. you remove the app's access in your Google account) or the browser's cookies are cleared. This is per-browser/device, not per-account centrally: a new device or browser needs to click Connect Drive once, same as the very first time. The cookie only ever holds the encrypted blob; the decryption key lives purely in server-side secrets and never reaches the browser.

One-time Google Cloud setup

  1. Create an OAuth 2.0 Web application Client ID; add your app's URL(s) with the /oauth2callback path as authorized redirect URIs (e.g. http://localhost:8501/oauth2callback for local dev, and https://<your-space>.hf.space/oauth2callback for a deployed Space). Streamlit only processes the OAuth code exchange at that exact path -- a bare app URL without /oauth2callback will silently fail to complete login (no error, but st.user.is_logged_in never becomes True).
  2. On the OAuth consent screen, add every player's Google account email under Test users (keep publishing status as "Testing" -- this avoids Google's verification review entirely for drive.file, a scope that only grants access to files the app itself creates).
  3. No service account, no Sheets/Drive API project-level enablement, no index spreadsheet -- the app's own OAuth client handles everything.

Local dev โ€” .streamlit/secrets.toml (gitignored, never commit this)

[auth]
redirect_uri = "http://localhost:8501/oauth2callback"
cookie_secret = "<random string>"
client_id = "<oauth client id>"
client_secret = "<oauth client secret>"
server_metadata_url = "https://accounts.google.com/.well-known/openid-configuration"

[cookies]
encryption_key = "<Fernet.generate_key() output>"

Hugging Face Space deploy

st.login() only reads st.secrets, so src/bootstrap_secrets.py assembles .streamlit/secrets.toml at startup from individual Space secrets (Settings โ†’ Variables and secrets), so nothing sensitive needs to live in the repo or the image: AUTH_CLIENT_ID, AUTH_CLIENT_SECRET, AUTH_COOKIE_SECRET, AUTH_REDIRECT_URI (your Space's public URL with /oauth2callback appended, e.g. https://randomcatlover-boardgames-tracker.hf.space/oauth2callback), COOKIE_ENCRYPTION_KEY.