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
- 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.
- Export
GOOGLE_APPLICATION_CREDENTIALS=/path/to/sa.json.
- Run
gws keep notes list.
- 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.
Problem
Some Google Workspace APIs only work with a service account that impersonates a user via domain-wide delegation. Notable examples:
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).gwstoday supports two auth modes:gws auth loginGOOGLE_APPLICATION_CREDENTIALS=/path/to/sa.jsonIn the SA path, there's no way to set the JWT
subject(the user to impersonate). Without it,gws keep notes listreturns:even when the SA has DWD authorized for
https://www.googleapis.com/auth/keepon the target domain.Reproduction
admin.google.com → Security → API controls → Domain-wide Delegation) with scopehttps://www.googleapis.com/auth/keep.GOOGLE_APPLICATION_CREDENTIALS=/path/to/sa.json.gws keep notes list.Proposed solution
Add one of:
--subject <user@example.com>on commands (or globally ongws auth login).GOOGLE_WORKSPACE_CLI_SUBJECTorGOOGLE_WORKSPACE_CLI_IMPERSONATE.Internally this maps to
subject=...when constructinggoogle.oauth2.service_account.Credentials(or the equivalent Go/Rust binding depending on the implementation language).Workaround used today
Standalone Python script using
google-authdirectly:Works, but means losing all the schema-discovery / formatting / pagination ergonomics that
gwsships with.Why it matters
Without subject impersonation,
gws-keep, parts ofgws-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.