From 80ce8d9aafc43c40b96e46b3c55bc6575c530adb Mon Sep 17 00:00:00 2001 From: William Valentin Date: Sun, 22 Feb 2026 23:42:40 -0800 Subject: [PATCH] fix(gmail-auth): request explicit filter settings scope --- README.md | 2 +- config/default.yaml | 2 +- docs/plans/state.json | 16 +++++++++++++++- src/cli/gmail-auth.test.ts | 3 ++- src/cli/gmail-auth.ts | 6 ++++-- 5 files changed, 23 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 5641dcc..780bc87 100644 --- a/README.md +++ b/README.md @@ -1154,7 +1154,7 @@ Supported delivery modes: 1. Create a Google Cloud project with the Gmail API enabled 2. Create OAuth2 credentials (Desktop application type) and download the JSON file 3. Run `flynn gmail-auth` to complete the OAuth2 flow and store the refresh token - - Requests full Gmail access scope (`https://mail.google.com/`) so all filter operations are allowed + - Requests Gmail scopes for settings + read access (`gmail.settings.basic` + `gmail.readonly`) For Pub/Sub delivery (push/pull), also enable the Pub/Sub API and create: - A topic (e.g. `projects/your-project/topics/gmail-push`) diff --git a/config/default.yaml b/config/default.yaml index 788cf2d..5148f1d 100644 --- a/config/default.yaml +++ b/config/default.yaml @@ -452,7 +452,7 @@ memory: # credentials_file: ~/.config/flynn/gmail-credentials.json # token_file: ~/.config/flynn/gmail-token.json # # Authenticate with: flynn gmail-auth -# # (requests full Gmail scope https://mail.google.com/ for complete Gmail filter permissions) +# # (requests gmail.settings.basic + gmail.readonly for filter create + inbox tools) # # # Optional Pub/Sub delivery # # Push mode: configure a topic and a push subscription that POSTs to /gmail/push diff --git a/docs/plans/state.json b/docs/plans/state.json index 751b726..00d4d4f 100644 --- a/docs/plans/state.json +++ b/docs/plans/state.json @@ -3,6 +3,20 @@ "updated_at": "2026-02-23", "description": "Tracks the status of all Flynn plans and implementation phases", "plans": { + "gmail-filter-scope-correction": { + "status": "completed", + "date": "2026-02-23", + "updated": "2026-02-23", + "summary": "Corrected Gmail OAuth scopes for filter creation after validating that `mail.google.com` alone still produced `ACCESS_TOKEN_SCOPE_INSUFFICIENT` for `users.settings.filters.create` in live checks. `flynn gmail-auth` now explicitly requests `gmail.settings.basic` (plus `gmail.readonly`). Updated auth tests and operator docs/comments.", + "files_modified": [ + "src/cli/gmail-auth.ts", + "src/cli/gmail-auth.test.ts", + "README.md", + "config/default.yaml", + "docs/plans/state.json" + ], + "test_status": "pnpm test:run src/cli/gmail-auth.test.ts + pnpm typecheck passing" + }, "minimal-tui-multiline-paste-mode": { "status": "completed", "date": "2026-02-23", @@ -6345,7 +6359,7 @@ "operator_dx_milestone": "Phase 3 (Live Ops Dashboard): 2/2 plans complete — milestone done", "dashboard_observability": "completed — service health graphs + core service log viewer added to web UI via observability RPCs and bounded backend sampling", "gmail_auth_cli": "flynn gmail-auth command implemented with OAuth2 flow, doctor check, config routed to Telegram", - "gmail_filter_creation": "completed — gmail.filter.create tool added with criteria/action validation; gmail-auth now requests full Gmail scope (https://mail.google.com/) for complete filter permissions", + "gmail_filter_creation": "completed — gmail.filter.create tool added with criteria/action validation; gmail-auth requests explicit gmail.settings.basic + gmail.readonly scopes for filter creation and inbox reads", "toolloop_action_intent_recovery": "completed — when a model claims it will execute a tool but emits no tool call, NativeAgent now issues one internal nudge and continues the same turn to execute tools or produce a concrete blocker", "toolloop_execution_claim_recovery": "completed — when a model claims a known tool already succeeded/failed without emitting a tool call, NativeAgent now nudges once and retries the same turn before returning text", "minimal_tui_multiline_paste_mode": "completed — minimal TUI now supports `/paste`/`/multiline` multiline compose mode ending with single '.' line, preventing newline truncation for pasted prompts", diff --git a/src/cli/gmail-auth.test.ts b/src/cli/gmail-auth.test.ts index 752c247..c611440 100644 --- a/src/cli/gmail-auth.test.ts +++ b/src/cli/gmail-auth.test.ts @@ -71,7 +71,8 @@ describe('gmail-auth', () => { expect(url).toContain('https://accounts.google.com/o/oauth2/v2/auth'); expect(url).toContain('client_id=my-client-id'); expect(url).toContain('redirect_uri=http%3A%2F%2Flocalhost%3A3000'); - expect(url).toContain('scope=https%3A%2F%2Fmail.google.com%2F'); + expect(url).toContain('scope=https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fgmail.settings.basic'); + expect(url).toContain('https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fgmail.readonly'); expect(url).toContain('access_type=offline'); expect(url).toContain('prompt=consent'); }); diff --git a/src/cli/gmail-auth.ts b/src/cli/gmail-auth.ts index 714ed3b..24a78a0 100644 --- a/src/cli/gmail-auth.ts +++ b/src/cli/gmail-auth.ts @@ -7,8 +7,10 @@ import { URL } from 'url'; import { loadConfigSafe } from './shared.js'; const SCOPES = [ - // Full Gmail access (includes all filter operations and settings APIs). - 'https://mail.google.com/', + // Explicitly request Gmail settings scope required by filters.create. + 'https://www.googleapis.com/auth/gmail.settings.basic', + // Keep readonly mailbox access for list/search/read tooling. + 'https://www.googleapis.com/auth/gmail.readonly', ]; const REDIRECT_PORT = 3000; const REDIRECT_URI = `http://localhost:${REDIRECT_PORT}`;