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 /internal/api/google_tasks.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 'internal/api/google_tasks.go')
| -rw-r--r-- | internal/api/google_tasks.go | 14 |
1 files changed, 11 insertions, 3 deletions
diff --git a/internal/api/google_tasks.go b/internal/api/google_tasks.go index 644f124..9188bc7 100644 --- a/internal/api/google_tasks.go +++ b/internal/api/google_tasks.go @@ -20,11 +20,19 @@ type GoogleTasksClient struct { displayTZ *time.Location } -// NewGoogleTasksClient creates a client for Google Tasks. +// NewGoogleTasksClient creates a client for Google Tasks authenticated via +// the given option (e.g. option.WithHTTPClient for OAuth -- see +// NewGoogleTasksOAuthClient in google_tasks_oauth.go). Service-account auth +// (option.WithCredentialsFile) does NOT work for Tasks: a service account +// has no path to a regular user's personal task lists, unlike Calendar, +// which supports sharing a calendar with any email including a service +// account's. Confirmed 2026-07-14 (see project_doot_google_tasks_oauth_limitation +// memory) -- always returns exactly one list ("My Tasks") with zero items, +// the service account's own empty default list, never the real user's. // tasklistID can be "@default" for the primary list, or a specific list ID. // Multiple lists can be comma-separated. -func NewGoogleTasksClient(ctx context.Context, credentialsFile, tasklistID, timezone string) (*GoogleTasksClient, error) { - srv, err := tasks.NewService(ctx, option.WithCredentialsFile(credentialsFile)) +func NewGoogleTasksClient(ctx context.Context, clientOpt option.ClientOption, tasklistID, timezone string) (*GoogleTasksClient, error) { + srv, err := tasks.NewService(ctx, clientOpt) if err != nil { return nil, fmt.Errorf("unable to create Tasks client: %v", err) } |
