RandomCatLover Claude Sonnet 5 commited on
Commit
c5a7532
·
1 Parent(s): 76b596f

Document the Connect Drive / Supabase refresh-token flow

Browse files

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

Files changed (1) hide show
  1. README.md +25 -10
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: Streamlit's login
29
- requests the `drive.file` scope alongside identity, so the app gets a
30
- short-lived OAuth token that lets it create/edit only the one spreadsheet it
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
- wants to log in must first be added as a **test user** in the OAuth consent
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
- [auth.client_kwargs]
67
- scope = "openid email profile https://www.googleapis.com/auth/drive.file"
68
- prompt = "consent select_account"
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`.