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
---
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](https://docs.streamlit.io) and [community
forums](https://discuss.streamlit.io).
## 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)
```toml
[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`.