Spaces:
Sleeping
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
- Create an OAuth 2.0 Web application Client ID; add your app's URL(s)
with the
/oauth2callbackpath as authorized redirect URIs (e.g.http://localhost:8501/oauth2callbackfor local dev, andhttps://<your-space>.hf.space/oauth2callbackfor a deployed Space). Streamlit only processes the OAuth code exchange at that exact path -- a bare app URL without/oauth2callbackwill silently fail to complete login (no error, butst.user.is_logged_innever becomesTrue). - 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). - 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.