Skip to content

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

@hoyt-harness

Description

@hoyt-harness

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions