Install any skill in seconds. Free to start, no credit card required.
Get Started Free →OverKill Hill P³ client-only Google Identity Services (GIS) auth workflow. Use when designing, implementing, or troubleshooting client-only Google Identity Services (GIS) OAuth token flows for React or TypeScript SPAs on static hosts. Use for Google Sign-In, Calendar or Tasks read access, popup token acquisition, session-scoped token storage, expiry handling, silent re-authentication, GCP OAuth setup, or porting this pattern without a backend, Client Secret, redirect URI, or refresh token. Keep
| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-04 | ✗→✓ | ▲ Improved | 52% | 0% |
| case-06 | ✗→✓ | ▲ Improved | 85% | 0% |
| case-15 | ✗→✓ | ▲ Improved | 22% | 0% |
| case-18 | ✗→✓ | ▲ Improved | 37% | 0% |
| case-19 | ✗→✓ | ▲ Improved | 20% | 0% |
OverKill Hill P³ · overkillhill.com · github.com/OKHP3
Intent: Give a static-site agent a reliable GIS token lifecycle for Google API read flows without inventing a backend or leaking a Client Secret.
| In scope | Out of scope | |---|---| | GIS popup token model and GCP Web Application setup | Authorization code flow, refresh tokens, or offline access | | React/TypeScript useGoogleAuth lifecycle | Service accounts, non-Google identity providers, or multi-tenant servers | | Calendar and Tasks read requests with Bearer tokens | Calendar/Tasks create, update, or delete operations | | Expiry buffer, silent re-auth, consent fallback, and reconnect UI | Provider-specific write logic or product data persistence |
LifeTrkr boundary: This skill covers authentication and read flows. Keep repo-specific write operations in src/lib/google.ts when that integration boundary exists; LifeTrkr currently splits the same boundary across src/lib/googleCalendar.ts and src/lib/googleTasks.ts. Do not move writes into the auth hook or invent a new backend.
Before editing, identify the host origins, existing GIS script, client-ID source, requested read scopes, auth hook location, and existing Google API modules. Record the smallest scope set that satisfies the request. Treat the following as the repo defaults, not universal names:
| Option | LifeTrkr default | Porting rule | |---|---|---| | token storage | browser sessionStorage | Allow an injected Storage implementation only when required | | token key | gal_token | Configure one key and use it for read, write, validity, and revoke paths | | expiry key | gal_expiry | Configure one key and store an absolute epoch-millisecond expiry | | expiry skew | 120_000 ms | Keep a safety buffer; make it configurable for a different latency budget | | client ID | VITE_GOOGLE_CLIENT_ID | Keep it public, environment-backed, and never replace it with a secret |
When porting, pass storage, tokenKey, expiryKey, and expirySkewMs through one options object. Do not copy hard-coded gal_* names into an unrelated app.
Check the target before implementing:
index.html loads https://accounts.google.com/gsi/client.Secret, .env file, or token is being added to source control.
origins under Authorized JavaScript Origins. Leave Authorized redirect URIs empty for this token model.
calendar.readonly and/or tasks.readonly only when those reads are needed; add openid profile email only when identity/profile data is needed.
lifecycle change:
bash node .agents/skills/okhp3-google-gis-client-auth/scripts/check-gis-setup.cjs
Read assets/gcp-setup-checklist.md for Console setup or origin_mismatch/invalid_client diagnosis. The checker is advisory: it cannot prove that OAuth consent, CORS, quotas, or live credentials work.
Implement the smallest change that satisfies the plan:
window.google.accounts.oauth2.initTokenClient with the publicclient ID, minimum scopes, prompt: '' for an explicit connect, and a callback that rejects response.error.
Date.now() + response.expires_in * 1000, store the tokenand absolute expiry using the configured storage/key options, and update UI state. Default examples for this repository are gal_token and gal_expiry.
isTokenValid() require a token andDate.now() < expiry - expirySkewMs; use the same check before every read API call.
getToken() return a valid token, otherwise request withprompt: 'none', then retry with the consent popup only if silent re-auth fails. Surface a user-actionable reconnect state when both attempts fail or a read request returns 401.
disconnect() revoke the current token when GIS is available, removeboth configured session keys, and clear local auth state. Do not persist the access token in localStorage, a database, a URL, or logs.
writes through the repository’s existing Google integration boundary.
Use references/useGoogleAuth.ts for a configurable hook baseline. Its useGoogleAuth(options) contract returns isConnected, accessToken, minutesUntilExpiry, connect(), getToken(), and disconnect(); adapt the return shape only when the host application already has a stable contract.
The flow is: popup opens → user consents → GIS returns an access token directly to the JavaScript callback → the browser calls Google APIs with Authorization: Bearer <token>. There is no redirect callback, server exchange, Client Secret, or refresh token.
Use the Calendar read endpoint https://www.googleapis.com/calendar/v3/calendars/primary/events and the Tasks read endpoints under https://tasks.googleapis.com/tasks/v1/. Check res.ok and handle 401 as an expired or revoked token; do not silently treat an API error as an empty result.
If the product needs the signed-in profile, fetch it separately with https://www.googleapis.com/oauth2/v3/userinfo using the bearer token. Keep that profile read out of the auth hook unless the host application already treats it as part of connect(); LifeTrkr keeps it in its Google API integration layer.
The minimum API calls and scopes are:
| Read | Scope | |---|---| | Calendar events | https://www.googleapis.com/auth/calendar.readonly | | Task lists/tasks | https://www.googleapis.com/auth/tasks.readonly | | Stable profile identity | openid profile email |
put secrets, access tokens, or .env files in the bundle or repository.
sessionStorage limits token lifetime to the browser tab. Never silently switchto localStorage just to survive a reload.
gal_token and gal_expiry for this repository. For a port, configure thekey names rather than mixing repo defaults with copied examples.
expires_in seconds. Apply the sameskew in render-time status and request-time validity checks.
prompt: 'none' can fail because consent was revoked, the browser blocks thepopup/session, or GIS is not loaded. Treat failure as a branch to explicit user consent, not as permission to bypass auth.
scheme://host[:port]); do notadd paths, hash routes, or redirect URIs.
friction and may trigger verification requirements.
code and instruction input, not proof that a live OAuth configuration is safe.
At handoff, report:
was added; and
availability).
references/useGoogleAuth.ts — configurable hook baseline; read when implementingor debugging token lifecycle behavior.
assets/gcp-setup-checklist.md — detailed GCP Console checklist.scripts/check-gis-setup.cjs — deterministic local setup scan; run from thetarget project root and treat warnings as review items.
Built by Jamie Hill · OverKill Hill P³ Published at github.com/OKHP3 Part of the OKHP3/skillz Agent Skill library. MIT License -- free to use, fork, and adapt. A nod to the source is appreciated.
Other measured skills in the registry, with their headline benchmark lift.