Skip to content

Feature request: --subject flag (or env var) for service-account impersonation on user-context APIs (Keep, Admin Directory, etc.) #776

@nandomaki

Description

@nandomaki

Problem

Some Google Workspace APIs only work with a service account that impersonates a user via domain-wide delegation. Notable examples:

  • Keep API (keep.googleapis.com) — explicitly states user OAuth is unsupported; only service accounts work, and they need to act on behalf of a real user (since notes belong to users).
  • Admin Directory write operations on certain entities.
  • Cloud Identity device APIs.

gws today supports two auth modes:

  • User OAuth via gws auth login
  • Service account via GOOGLE_APPLICATION_CREDENTIALS=/path/to/sa.json

In the SA path, there's no way to set the JWT subject (the user to impersonate). Without it, gws keep notes list returns:

{
  "error": {
    "code": 403,
    "message": "Request had insufficient authentication scopes.",
    "reason": "unknown"
  }
}

even when the SA has DWD authorized for https://www.googleapis.com/auth/keep on the target domain.

Reproduction

  1. Create an SA in GCP, enable DWD via Workspace Admin (admin.google.com → Security → API controls → Domain-wide Delegation) with scope https://www.googleapis.com/auth/keep.
  2. Export GOOGLE_APPLICATION_CREDENTIALS=/path/to/sa.json.
  3. Run gws keep notes list.
  4. 403 — there is no user context attached to the request.

Proposed solution

Add one of:

  • CLI flag --subject <user@example.com> on commands (or globally on gws auth login).
  • Env var GOOGLE_WORKSPACE_CLI_SUBJECT or GOOGLE_WORKSPACE_CLI_IMPERSONATE.

Internally this maps to subject=... when constructing google.oauth2.service_account.Credentials (or the equivalent Go/Rust binding depending on the implementation language).

Workaround used today

Standalone Python script using google-auth directly:

creds = service_account.Credentials.from_service_account_file(
    "sa.json",
    scopes=["https://www.googleapis.com/auth/keep"],
    subject="user@example.com",
)

Works, but means losing all the schema-discovery / formatting / pagination ergonomics that gws ships with.

Why it matters

Without subject impersonation, gws-keep, parts of gws-admin-directory, and other DWD-only flows are effectively dead code in the skill set — the auth path can't be completed.

Happy to PR this if there's interest in the direction.

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