Relationships
#1231 @swamp/gcp/calendar: service-account & ADC auth mint cloud-platform-scoped tokens → 403 on Calendar API
Opened by kneel · 7/17/2026· Shipped 7/18/2026
Summary
@swamp/gcp/calendar's service-account (credentialsJson) and ADC auth paths mint OAuth2 access tokens with no scopes requested, so gcloud returns a default cloud-platform-scoped token. The Google Calendar API (calendar/v3) requires a Calendar scope (https://[HOST-1]/auth/calendar or .../calendar.readonly), which cloud-platform does not grant. Every calendar model method therefore fails with 403 Request had insufficient authentication scopes when authenticated via a service account or ADC.
The only auth path that works is supplying a pre-minted, externally-scoped token via the accessToken global arg / GCP_ACCESS_TOKEN env — i.e. the user must obtain the correctly-scoped token outside the extension.
Affected code
models/_lib/gcp.ts:
activateServiceAccountFromJson()—gcloud auth print-access-token <client_email>is invoked with no--scopes(~line 235).getApplicationDefaultCredentials()—gcloud auth application-default print-access-tokenwith no--scopes(~line 266).- There is no
scopesglobal argument and no scope env override anywhere in the extension.
This _lib is shared across the whole @swamp/gcp/* family, where cloud-platform scope is correct for genuine infrastructure APIs — but the Calendar Data API is a Workspace/consumer API that needs its own OAuth scope, so this family is the case the generic assumption breaks.
Reproduction
# vault holds a valid service-account JSON with Calendar API access
swamp model create @swamp/gcp/calendar/events gcal-events \
--global-arg 'credentialsJson=${{ vault.get(gcal, credentialsJson) }}' \
--global-arg project=<project> \
--global-arg calendarId=<calendar-id>
swamp model method run gcal-events list --input maxResults=5 --input singleEvents=true
# -> list failed (403): "Request had insufficient authentication scopes"Confirmed the same service account + calendar succeed when called with a Calendar-scoped token: minting via
gcloud auth print-access-token --impersonate-service-account=<sa> --scopes=https://[HOST-1]/auth/calendar.readonly
and hitting calendar/v3/calendars/<id>/events directly returns events (HTTP 200). Only the scope differs.
Expected vs actual
- Expected: with service-account JSON (README auth options 2 & 3, advertised as the vault-friendly path), calendar model methods authenticate and succeed.
- Actual: they always 403 — the documented SA and ADC auth paths are non-functional for the Calendar API.
Suggested fix
Add an optional scopes global argument (default, for the calendar family, to https://[HOST-1]/auth/calendar) and pass --scopes to both gcloud auth print-access-token and gcloud auth application-default print-access-token. More generally, the codegen could derive default OAuth scopes per API family rather than relying on the implicit cloud-platform default.
Workaround
Mint a Calendar-scoped access token externally (e.g. a service-account JWT bearer-token exchange with scope=.../auth/calendar.readonly, or gcloud ... --scopes=...) and pass it via the accessToken global arg / GCP_ACCESS_TOKEN.
Environment
- Extension:
@swamp/gcp/calendar@2026.07.17.1 - swamp CLI:
20260716.194319.0-sha.df9fc306 - gcloud: Google Cloud SDK 555.0.0
- Calendar accessed: a personal calendar shared to the service account with "See all event details"
Upstream repository: https://github.com/swamp-club/swamp-extensions
Environment
- Extension:
@swamp/gcp/calendar@2026.07.17.1 - swamp:
20260716.194319.0-sha.df9fc306 - OS:
linux(x86_64) - Deno:
2.8.3 - Shell:
/bin/zsh
Shipped
Click a lifecycle step above to view its details.