From 3660486153a16760d2b980e546bbbd29408fb8d4 Mon Sep 17 00:00:00 2001 From: Peter Stone Date: Sun, 16 Aug 2026 00:03:51 +0000 Subject: 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. --- internal/handlers/handlers.go | 48 ++++++++++++++++++++++++------------------- 1 file changed, 27 insertions(+), 21 deletions(-) (limited to 'internal/handlers/handlers.go') diff --git a/internal/handlers/handlers.go b/internal/handlers/handlers.go index aedbd11..fc66f84 100644 --- a/internal/handlers/handlers.go +++ b/internal/handlers/handlers.go @@ -15,7 +15,9 @@ import ( "sync" "time" + "github.com/alexedwards/scs/v2" "github.com/go-chi/chi/v5" + "golang.org/x/oauth2" "task-dashboard/internal/api" "task-dashboard/internal/auth" @@ -33,20 +35,22 @@ func newID() string { // Handler holds dependencies for HTTP handlers type Handler struct { - store *store.Store - trelloClient api.TrelloAPI - planToEatClient api.PlanToEatAPI - googleCalendarClient api.GoogleCalendarAPI - googleTasksClient api.GoogleTasksAPI - claudomatorClient api.ClaudomatorClient - config *config.Config - renderer Renderer - BuildVersion string - WebAuthnEnabled bool + store *store.Store + trelloClient api.TrelloAPI + planToEatClient api.PlanToEatAPI + googleCalendarClient api.GoogleCalendarAPI + googleTasksClient api.GoogleTasksAPI + googleTasksOAuthConfig *oauth2.Config + claudomatorClient api.ClaudomatorClient + config *config.Config + renderer Renderer + sessions *scs.SessionManager + BuildVersion string + WebAuthnEnabled bool } // New creates a new Handler instance -func New(s *store.Store, trello api.TrelloAPI, planToEat api.PlanToEatAPI, googleCalendar api.GoogleCalendarAPI, googleTasks api.GoogleTasksAPI, claudomator api.ClaudomatorClient, cfg *config.Config, buildVersion string, webAuthnEnabled bool) *Handler { +func New(s *store.Store, trello api.TrelloAPI, planToEat api.PlanToEatAPI, googleCalendar api.GoogleCalendarAPI, googleTasks api.GoogleTasksAPI, googleTasksOAuthConfig *oauth2.Config, claudomator api.ClaudomatorClient, cfg *config.Config, sessions *scs.SessionManager, buildVersion string, webAuthnEnabled bool) *Handler { // Template functions funcMap := template.FuncMap{ "subtract": func(a, b int) int { return a - b }, @@ -125,16 +129,18 @@ func New(s *store.Store, trello api.TrelloAPI, planToEat api.PlanToEatAPI, googl } return &Handler{ - store: s, - trelloClient: trello, - planToEatClient: planToEat, - googleCalendarClient: googleCalendar, - googleTasksClient: googleTasks, - claudomatorClient: claudomator, - config: cfg, - renderer: NewTemplateRenderer(tmpl), - BuildVersion: buildVersion, - WebAuthnEnabled: webAuthnEnabled, + store: s, + trelloClient: trello, + planToEatClient: planToEat, + googleCalendarClient: googleCalendar, + googleTasksClient: googleTasks, + googleTasksOAuthConfig: googleTasksOAuthConfig, + claudomatorClient: claudomator, + config: cfg, + renderer: NewTemplateRenderer(tmpl), + sessions: sessions, + BuildVersion: buildVersion, + WebAuthnEnabled: webAuthnEnabled, } } -- cgit v1.2.3