Body
Version: gws v0.22.5 (Windows x86_64 binary from GitHub Releases)
Environment:
- OS: Windows 10/11
- Auth flow: OAuth 2.0 desktop client, consent screen in External / Testing mode
Summary
gws auth login -s <list> is a service-picker filter that accepts a comma-separated list of service names and resolves each to a default scope. In practice, the flag has two undocumented behaviors:
- Unknown names in the list are silently dropped. No warning, no error. The surviving names are granted; the dropped names are not.
- An undocumented
cloud-platform scope is injected whenever any of the surviving services have a sanitization layer dependency. This happens even when the user explicitly omitted cloud-platform from their intent.
The combined effect: a user who runs a single auth login -s command gets a token containing a scope set materially different from what they typed, with no diagnostic indicating the mismatch.
Reproduction
Case A: Silent-drop of unknown names
gws auth login -s gmail,drive,calendar,calendar.events,documents,spreadsheets,tasks,contacts,contacts.other.readonly
Expected: Grant all nine named scopes. The help text example (-s drive,gmail,sheets) suggests the flag takes service names, and several items in the list (calendar.events, contacts.other.readonly) are valid scope URI suffixes, so a user could reasonably interpret the flag as accepting either form.
Actual: After browser consent, gws auth status shows the valid service aliases each resolved to one default scope — plus cloud-platform which was not in the list. The names contacts and contacts.other.readonly were silently dropped. No stderr warning. Exit code 0.
Cross-check: Running gws contacts --help after the re-auth fails with a Known services: ... error — so the CLI has the data to validate service names at parse time but does not use it during auth login -s.
Case B: Undocumented cloud-platform injection
Same command as Case A. gws auth status shows cloud-platform in the granted-scope list even though it was not requested. No step of the auth login -s flow surfaced this addition.
Working path (no injection): Deleting token_cache.json and re-running with --scopes (plural) using explicit scope URIs:
gws auth login --scopes https://www.googleapis.com/auth/gmail.modify,https://www.googleapis.com/auth/drive,...
This produces a clean grant of exactly the intended scopes with no cloud-platform added.
Hypothesis
The -s code path maps each input token through a lookup table, silently skips tokens that miss the lookup, and post-processes the resulting scope set to add cloud-platform when any resolved service has a sanitization dependency. The --scopes code path bypasses both the lookup and the post-processing.
Impact
- Scope drift. Users get a different scope set than they requested. The only way to catch it is to run
gws auth status afterward and manually diff against the intended list.
- Re-auth friction. Fixing scope drift requires deleting
token_cache.json and repeating the browser consent flow. For accounts with test-user restrictions or organizational OAuth policies, that friction is non-trivial.
- Potential policy violation.
cloud-platform injection can silently grant a scope the user deliberately excluded, which conflicts with least-privilege security practices.
Requested behavior
- Reject unknown service names at parse time. The validation data already exists in the binary (
gws contacts --help produces a Known services: ... error). Wire that same validation into auth login -s. Print an error listing valid service names and exit non-zero before opening the browser consent flow.
- Document the
cloud-platform injection in auth login --help. At minimum: a note that certain services pull in cloud-platform as an internal dependency, with a list of which services trigger it.
- Gate the injection behind an explicit opt-in (e.g., an
--allow-gcp flag). A user who runs -s without that flag should receive only the user-facing service scopes.
Body
Version:
gwsv0.22.5 (Windows x86_64 binary from GitHub Releases)Environment:
Summary
gws auth login -s <list>is a service-picker filter that accepts a comma-separated list of service names and resolves each to a default scope. In practice, the flag has two undocumented behaviors:cloud-platformscope is injected whenever any of the surviving services have a sanitization layer dependency. This happens even when the user explicitly omittedcloud-platformfrom their intent.The combined effect: a user who runs a single
auth login -scommand gets a token containing a scope set materially different from what they typed, with no diagnostic indicating the mismatch.Reproduction
Case A: Silent-drop of unknown names
Expected: Grant all nine named scopes. The help text example (
-s drive,gmail,sheets) suggests the flag takes service names, and several items in the list (calendar.events,contacts.other.readonly) are valid scope URI suffixes, so a user could reasonably interpret the flag as accepting either form.Actual: After browser consent,
gws auth statusshows the valid service aliases each resolved to one default scope — pluscloud-platformwhich was not in the list. The namescontactsandcontacts.other.readonlywere silently dropped. No stderr warning. Exit code 0.Cross-check: Running
gws contacts --helpafter the re-auth fails with aKnown services: ...error — so the CLI has the data to validate service names at parse time but does not use it duringauth login -s.Case B: Undocumented
cloud-platforminjectionSame command as Case A.
gws auth statusshowscloud-platformin the granted-scope list even though it was not requested. No step of theauth login -sflow surfaced this addition.Working path (no injection): Deleting
token_cache.jsonand re-running with--scopes(plural) using explicit scope URIs:This produces a clean grant of exactly the intended scopes with no
cloud-platformadded.Hypothesis
The
-scode path maps each input token through a lookup table, silently skips tokens that miss the lookup, and post-processes the resulting scope set to addcloud-platformwhen any resolved service has a sanitization dependency. The--scopescode path bypasses both the lookup and the post-processing.Impact
gws auth statusafterward and manually diff against the intended list.token_cache.jsonand repeating the browser consent flow. For accounts with test-user restrictions or organizational OAuth policies, that friction is non-trivial.cloud-platforminjection can silently grant a scope the user deliberately excluded, which conflicts with least-privilege security practices.Requested behavior
gws contacts --helpproduces aKnown services: ...error). Wire that same validation intoauth login -s. Print an error listing valid service names and exit non-zero before opening the browser consent flow.cloud-platforminjection inauth login --help. At minimum: a note that certain services pull incloud-platformas an internal dependency, with a list of which services trigger it.--allow-gcpflag). A user who runs-swithout that flag should receive only the user-facing service scopes.