#741·cli

`auth login -s` / `--services` silently drops unknown service names and injects `cloud-platform` scope without documentation

Author: hoyt-harnessCreated Apr 21, 2026Updated Sep 8, 2026

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:

  1. Unknown names in the list are silently dropped. No warning, no error. The surviving names are granted; the dropped names are not.
  2. 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

  1. 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.
  2. 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.
  3. Potential policy violation. cloud-platform injection can silently grant a scope the user deliberately excluded, which conflicts with least-privilege security practices.

Requested behavior

  1. 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.
  2. 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.
  3. 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.