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 /cmd/dashboard/main.go | |
| 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 'cmd/dashboard/main.go')
| -rw-r--r-- | cmd/dashboard/main.go | 12 |
1 files changed, 9 insertions, 3 deletions
diff --git a/cmd/dashboard/main.go b/cmd/dashboard/main.go index 24fb8a6..a84c32d 100644 --- a/cmd/dashboard/main.go +++ b/cmd/dashboard/main.go @@ -16,6 +16,7 @@ import ( "github.com/go-chi/chi/v5" "github.com/go-chi/chi/v5/middleware" "github.com/joho/godotenv" + "golang.org/x/oauth2" "task-dashboard/internal/api" "task-dashboard/internal/auth" @@ -133,15 +134,17 @@ func main() { } var googleTasksClient api.GoogleTasksAPI + var googleTasksOAuthConfig *oauth2.Config if cfg.HasGoogleTasks() { + googleTasksOAuthConfig = api.GoogleTasksOAuthConfig(cfg.GoogleOAuthClientID, cfg.GoogleOAuthClientSecret, cfg.GoogleOAuthRedirectURL) initCtx, cancel := context.WithTimeout(context.Background(), config.GoogleCalendarInitTimeout) var err error - googleTasksClient, err = api.NewGoogleTasksClient(initCtx, cfg.GoogleCredentialsFile, cfg.GoogleTasksListID, cfg.Timezone) + googleTasksClient, err = api.NewGoogleTasksOAuthClient(initCtx, googleTasksOAuthConfig, db, cfg.GoogleTasksListID, cfg.Timezone) cancel() if err != nil { log.Printf("Warning: failed to initialize Google Tasks client: %v", err) } else { - log.Printf("Google Tasks client initialized for list: %s", cfg.GoogleTasksListID) + log.Printf("Google Tasks client initialized for list: %s (OAuth; connect at /settings if not already)", cfg.GoogleTasksListID) } } @@ -151,7 +154,7 @@ func main() { } // Initialize handlers - h := handlers.New(db, trelloClient, planToEatClient, googleCalendarClient, googleTasksClient, claudomatorClient, cfg, buildCommit, wa != nil) + h := handlers.New(db, trelloClient, planToEatClient, googleCalendarClient, googleTasksClient, googleTasksOAuthConfig, claudomatorClient, cfg, sessionManager, buildCommit, wa != nil) // Set up router r := chi.NewRouter() @@ -384,6 +387,9 @@ func main() { r.Post("/settings/clear-cache", h.HandleClearCache) r.Post("/settings/toggle", h.HandleToggleSourceConfig) r.Delete("/settings/agents/{id}", h.HandleDeleteAgent) + r.Get("/settings/google-tasks/connect", h.HandleGoogleTasksOAuthStart) + r.Get("/settings/google-tasks/callback", h.HandleGoogleTasksOAuthCallback) + r.Post("/settings/google-tasks/disconnect", h.HandleGoogleTasksOAuthDisconnect) // Maintenance bucket management (Settings page) r.Post("/settings/buckets", h.HandleBucketsCreate) |
