Spaces:
Sleeping
Sleeping
Commit ·
c5a7532
1
Parent(s): 76b596f
Document the Connect Drive / Supabase refresh-token flow
Browse filesCo-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
README.md
CHANGED
|
@@ -25,19 +25,30 @@ The app works fully anonymously with no setup. Logging in with Google (button
|
|
| 25 |
in the sidebar) additionally persists your players and round history to a
|
| 26 |
Google Sheet -- created directly in **your own Drive**, owned by you.
|
| 27 |
|
| 28 |
-
There's no service account and no shared storage involved:
|
| 29 |
-
|
| 30 |
-
|
| 31 |
-
creates for you (`BoardGameTracker - {your email}`), nothing else in your
|
| 32 |
Drive. Because it's created by your own account, it uses your own storage --
|
| 33 |
there's no "service account has 0 storage quota" issue to work around.
|
| 34 |
|
| 35 |
Trade-off: since the app stays in Google's "Testing" publishing status
|
| 36 |
(no Google security review needed for a small friend group), each person who
|
| 37 |
-
|
| 38 |
screen, and will see an "unverified app" warning the first time they
|
| 39 |
click through.
|
| 40 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 41 |
### One-time Google Cloud setup
|
| 42 |
1. Create an OAuth 2.0 **Web application** Client ID; add your app's URL(s)
|
| 43 |
**with the `/oauth2callback` path** as authorized redirect URIs (e.g.
|
|
@@ -61,12 +72,15 @@ cookie_secret = "<random string>"
|
|
| 61 |
client_id = "<oauth client id>"
|
| 62 |
client_secret = "<oauth client secret>"
|
| 63 |
server_metadata_url = "https://accounts.google.com/.well-known/openid-configuration"
|
| 64 |
-
expose_tokens = ["access"]
|
| 65 |
|
| 66 |
-
[
|
| 67 |
-
|
| 68 |
-
|
| 69 |
```
|
|
|
|
|
|
|
|
|
|
|
|
|
| 70 |
|
| 71 |
### Hugging Face Space deploy
|
| 72 |
`st.login()` only reads `st.secrets`, so `src/bootstrap_secrets.py` assembles
|
|
@@ -74,4 +88,5 @@ prompt = "consent select_account"
|
|
| 74 |
Variables and secrets), so nothing sensitive needs to live in the repo or the
|
| 75 |
image: `AUTH_CLIENT_ID`, `AUTH_CLIENT_SECRET`, `AUTH_COOKIE_SECRET`,
|
| 76 |
`AUTH_REDIRECT_URI` (your Space's public URL **with `/oauth2callback`
|
| 77 |
-
appended**, e.g. `https://randomcatlover-boardgames-tracker.hf.space/oauth2callback`)
|
|
|
|
|
|
| 25 |
in the sidebar) additionally persists your players and round history to a
|
| 26 |
Google Sheet -- created directly in **your own Drive**, owned by you.
|
| 27 |
|
| 28 |
+
There's no service account and no shared storage involved: Drive access uses
|
| 29 |
+
the `drive.file` scope, so the app can only create/edit the one spreadsheet
|
| 30 |
+
it creates for you (`BoardGameTracker - {your email}`), nothing else in your
|
|
|
|
| 31 |
Drive. Because it's created by your own account, it uses your own storage --
|
| 32 |
there's no "service account has 0 storage quota" issue to work around.
|
| 33 |
|
| 34 |
Trade-off: since the app stays in Google's "Testing" publishing status
|
| 35 |
(no Google security review needed for a small friend group), each person who
|
| 36 |
+
connects Drive must first be added as a **test user** in the OAuth consent
|
| 37 |
screen, and will see an "unverified app" warning the first time they
|
| 38 |
click through.
|
| 39 |
|
| 40 |
+
**Login vs. Drive access are two separate steps.** `st.login()` (sidebar
|
| 41 |
+
button) only ever handles identity -- it deliberately never exposes a
|
| 42 |
+
`refresh_token` to app code, no matter what scope you ask for. So once
|
| 43 |
+
logged in, a separate one-time **Connect Google Drive** button appears in the
|
| 44 |
+
sidebar; clicking it runs its own OAuth exchange (`access_type=offline` +
|
| 45 |
+
`prompt=consent`) to force Google to hand back a `refresh_token`, which gets
|
| 46 |
+
encrypted and stored in a Supabase Postgres table keyed by email. From then
|
| 47 |
+
on -- even after closing the browser, or logging in from a different device
|
| 48 |
+
-- the app silently mints a fresh access token from that stored refresh_token
|
| 49 |
+
on every login; the Connect Drive button only reappears if that stored token
|
| 50 |
+
is ever revoked (e.g. you remove the app's access in your Google account).
|
| 51 |
+
|
| 52 |
### One-time Google Cloud setup
|
| 53 |
1. Create an OAuth 2.0 **Web application** Client ID; add your app's URL(s)
|
| 54 |
**with the `/oauth2callback` path** as authorized redirect URIs (e.g.
|
|
|
|
| 72 |
client_id = "<oauth client id>"
|
| 73 |
client_secret = "<oauth client secret>"
|
| 74 |
server_metadata_url = "https://accounts.google.com/.well-known/openid-configuration"
|
|
|
|
| 75 |
|
| 76 |
+
[supabase]
|
| 77 |
+
db_url = "postgresql://..." # Supabase connection string
|
| 78 |
+
encryption_key = "<Fernet.generate_key() output>"
|
| 79 |
```
|
| 80 |
+
The `[supabase]` table (`user_refresh_tokens`) is created automatically on
|
| 81 |
+
first use -- no manual migration needed. Rotate the DB password in Supabase's
|
| 82 |
+
dashboard if it's ever been pasted somewhere outside a secrets file (e.g.
|
| 83 |
+
chat, a ticket, a terminal history you don't control).
|
| 84 |
|
| 85 |
### Hugging Face Space deploy
|
| 86 |
`st.login()` only reads `st.secrets`, so `src/bootstrap_secrets.py` assembles
|
|
|
|
| 88 |
Variables and secrets), so nothing sensitive needs to live in the repo or the
|
| 89 |
image: `AUTH_CLIENT_ID`, `AUTH_CLIENT_SECRET`, `AUTH_COOKIE_SECRET`,
|
| 90 |
`AUTH_REDIRECT_URI` (your Space's public URL **with `/oauth2callback`
|
| 91 |
+
appended**, e.g. `https://randomcatlover-boardgames-tracker.hf.space/oauth2callback`),
|
| 92 |
+
`SUPABASE_DB_URL`, `SUPABASE_ENCRYPTION_KEY`.
|