diff options
| author | Peter Stone <thepeterstone@gmail.com> | 2026-08-16 00:03:51 +0000 |
|---|---|---|
| committer | Peter Stone <thepeterstone@gmail.com> | 2026-08-16 00:03:51 +0000 |
| commit | 3660486153a16760d2b980e546bbbd29408fb8d4 (patch) | |
| tree | e29199d88b7365eca0438e41ac23fa0bd533f7c2 /web/templates | |
| parent | b55cfbbd433bed6035dfa228ee700e2cca060ca4 (diff) | |
Replace Google Tasks service-account auth with real OAuth
Service-account auth structurally cannot see a regular user's personal
task lists (no equivalent of Calendar's per-item sharing model) --
confirmed via GetTaskLists returning exactly the service account's own
empty "My Tasks" list, never the real user's three lists. Zero rows
were ever cached in production as a result.
Adds a standard 3-legged OAuth flow: /settings/google-tasks/connect
redirects to Google's consent screen (AccessTypeOffline+ApprovalForce
so a refresh_token is always issued), /callback exchanges the code and
persists the token (new oauth_tokens table), /disconnect clears it.
GoogleTasksClient now takes an option.ClientOption instead of a
credentials file path; NewGoogleTasksOAuthClient wraps it with a
dbTokenSource that reloads/refreshes from the DB on each access-token
expiry and re-persists -- carefully preserving the original
refresh_token when Google's refresh response omits one (it usually
does), which would otherwise silently and permanently break future
refreshes.
Settings page shows connection status and a Connect/Disconnect
button. Calendar keeps using service-account auth (that one actually
works). Requires a one-time manual step: create an OAuth 2.0 Client ID
in Google Cloud Console and set GOOGLE_OAUTH_CLIENT_ID/SECRET in .env
-- documented in .env.example.
Diffstat (limited to 'web/templates')
| -rw-r--r-- | web/templates/settings.html | 33 |
1 files changed, 33 insertions, 0 deletions
diff --git a/web/templates/settings.html b/web/templates/settings.html index acee1ee..8a72287 100644 --- a/web/templates/settings.html +++ b/web/templates/settings.html @@ -161,6 +161,39 @@ </div> </section> + <!-- Google Tasks Connection Section --> + <section class="mb-12"> + <div class="flex flex-wrap items-center justify-between gap-4 mb-6 pb-2 border-b border-white/10"> + <h2 class="text-xl font-medium text-white">Google Tasks</h2> + </div> + {{if .GoogleTasksError}} + <div class="card border-red-500/30 text-red-400 text-sm mb-4"> + Connection failed: {{.GoogleTasksError}} + </div> + {{end}} + {{if not .GoogleTasksOAuthReady}} + <div class="card text-slate-500 text-sm italic"> + Not configured — set GOOGLE_OAUTH_CLIENT_ID and GOOGLE_OAUTH_CLIENT_SECRET in .env and restart. + </div> + {{else if .GoogleTasksConnected}} + <div class="card flex items-center justify-between gap-4"> + <span class="text-white text-sm">✓ Connected — real task lists syncing via OAuth.</span> + <button class="text-sm border border-red-500/30 text-red-400 hover:bg-red-500/10 px-4 py-2 rounded-lg transition-colors" + hx-post="/settings/google-tasks/disconnect" + hx-confirm="Disconnect Google Tasks? Task sync will stop until you reconnect."> + Disconnect + </button> + </div> + {{else}} + <div class="card flex items-center justify-between gap-4"> + <span class="text-slate-400 text-sm">Not connected — service-account auth can't see your real task lists, this needs your own Google sign-in.</span> + <a href="/settings/google-tasks/connect" class="text-sm bg-slate-800 hover:bg-slate-700 text-white px-4 py-2 rounded-lg transition-colors"> + Connect Google Tasks + </a> + </div> + {{end}} + </section> + <!-- Data Sources Section --> <section class="mb-12"> <div class="flex flex-wrap items-center justify-between gap-4 mb-6 pb-2 border-b border-white/10"> |
