Skip to content

gog auth list under-reports scopes when stored token has subset of currently-granted scopes #649

@rancur

Description

@rancur

Summary

gog auth list reports the scope set stored at token-add time in the keyring entry, but a stored token will often successfully execute API calls against scopes it does not list. This caused a 30+ minute false-alarm investigation in one of our agent runs: auth list reported an account as calendar-only, yet gmail send on that same account succeeded.

Reproduction (locally observed)

$ gog --version
v0.12.0 (c18c58c 2026-03-09T05:53:14Z)

$ gog auth list
barry@willcurran.com    default    calendar           2026-02-26T18:45:40Z    oauth
flash@willcurran.com    default    calendar,gmail     2026-04-15T22:01:53Z    oauth

$ gog auth list --json
{
  "accounts": [
    {
      "email": "barry@willcurran.com",
      "services": ["calendar"],
      "scopes": [
        "email",
        "https://www.googleapis.com/auth/calendar",
        "https://www.googleapis.com/auth/userinfo.email",
        "openid"
      ],
      ...
    }
  ]
}

$ gog gmail --account barry@willcurran.com search "newer_than:1d" --results-only
ID                DATE              FROM             SUBJECT  LABELS  THREAD
19e6a781e1d2bfff  2026-05-27 10:25  barry@curran.co  Re: ...  SENT    [2 msgs]

$ gog --verbose gmail --account barry@willcurran.com search "newer_than:1d" --results-only 2>&1 | head -3
time=... level=DEBUG msg=\"creating client options with custom scopes\" serviceLabel=gmail email=barry@willcurran.com
time=... level=DEBUG msg=\"client options with custom scopes created successfully\" serviceLabel=gmail email=barry@willcurran.com

The keyring file's stored scope set does not include gmail, yet gmail send / gmail search both succeed against this account. (We confirmed actual send via msg-id 19e6a77240c18d79 — message landed in Sent.)

Root cause (hypothesis)

Google's OAuth server honors incremental authorization — if the user has previously consented to a wider scope set under the SAME OAuth client (across any account), Google will issue access tokens with the requested scopes off the refresh token even when that specific refresh token's original consent only covered a subset. gog's request-time path \"creating client options with custom scopes\" is asking for gmail scope and Google grants it; meanwhile the keyring entry's stored scope set is stale because it was never re-issued to reflect the broader consent.

Why this matters

Operators (and agents) trust gog auth list as the source of truth for "can this account do X?" When the displayed scope set is a strict subset of what actually works, they either:
(a) waste time re-authing accounts that don't need it, or
(b) raise false-alarm "auth blocker" notifications when there is no actual blocker.

Both happened in our system.

Suggested fix (one of)

  1. Detect + persist scope upgrades. When gog successfully obtains an access token off a refresh token with a scope set that exceeds the stored consent set, write the new scope union back to the keyring entry so subsequent auth list calls reflect reality.
  2. gog auth refresh <email> command. Trigger a refresh+token-exchange that updates the stored scope metadata without a full browser re-consent flow.
  3. auth list warning glyph. When a stored token's scope set has been observed to be insufficient for a recent successful call, mark the row with a * and explain in auth list --help.

Option 1 is the most user-friendly (auto-heals). Option 2 is the smallest change. Option 3 is the cheapest but does not auto-heal.

Workaround

gog auth add <email> --services gmail,calendar --force-consent re-authorizes through the browser flow with the explicit superset, which refreshes the keyring entry's stored scope list. (Verified locally that this is the path that updates the metadata; running it for this account is a separate Will-side step requiring interactive browser action.)

Environment

  • Version: v0.12.0 (c18c58c)
  • Backend: file keyring (passwords managed in 1Password via gog-safe wrapper)
  • macOS 25.2.0 (Darwin), arm64
  • Latest release is v0.19.0; release notes since v0.12.0 do not appear to address this specifically ("auth list" changes since v0.12.0 are about graceful unreadable-entry handling and per-client filtering, not scope-upgrade reconciliation).

Happy to test a fix locally; the affected account is barry@willcurran.com (Workspace) on this machine.

Metadata

Metadata

Assignees

No one assigned

    Labels

    P2Normal priority bug or improvement with limited blast radius.clawsweeper:source-reproClawSweeper found a high-confidence source-level issue reproduction.impact:auth-providerThis issue is about auth, provider routing, model choice, or SecretRef resolution.issue-rating: 🦞 diamond lobsterVery strong issue quality with high-confidence source-level or clear reproduction.

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions