nexus/.planning/phases/31-puter.js-zero-config-cloud/31-02-SUMMARY.md

5 KiB

phase plan subsystem tags dependency_graph tech_stack key_files decisions metrics requirements
31-puter.js-zero-config-cloud 02 server/google-oauth
oauth
pkce
google
gemini
api-keys
secrets
requires provides affects
googleOAuthService
googleOAuthRoutes
google-oauth-routes
server/src/app.ts
added patterns
PKCE-S256
pendingTokens-claim-pattern
secretService-upsert
created modified
server/src/services/google-oauth.ts
server/src/routes/google-oauth.ts
server/src/__tests__/31-google-oauth.test.ts
server/src/app.ts
pendingPkce stores only verifier (no companyId) — company does not exist at authorize time
pendingTokens uses stateId as key — claim endpoint links tokens to companyId post-company-creation
Google token exchange uses fetch (Node built-in) not axios
secretService upsert pattern
getByName -> rotate if exists, create if not
duration completed tasks_completed files_created files_modified
3 minutes 22 seconds 2026-04-03T00:36:57Z 3 3 1
CLOUD-03
CLOUD-05

Phase 31 Plan 02: Google OAuth PKCE Service and Routes Summary

One-liner: Google OAuth PKCE flow for Gemini free tier access — generateAuthUrl/exchangeCode/storeTokens service plus authorize/callback/claim/api-keys routes with in-memory pendingTokens pattern separating callback from company creation.

Tasks Completed

Task Name Commit Files
1 googleOAuthService — PKCE generation, code exchange, token storage 72045513 server/src/services/google-oauth.ts
2 googleOAuthRoutes (pendingTokens pattern) + API key route + mount in app.ts c41ec162 server/src/routes/google-oauth.ts, server/src/app.ts
3 Unit tests for Google OAuth service and routes d750d15f server/src/tests/31-google-oauth.test.ts

What Was Built

googleOAuthService (server/src/services/google-oauth.ts)

  • generatePkce() — crypto.randomBytes(32).toString("base64url") verifier, SHA256 base64url challenge
  • generateAuthUrl(redirectUri, state) — builds Google OAuth URL with PKCE S256, Gemini scopes (openid + cloud-platform + generative-language.retriever), access_type=offline, prompt=consent
  • exchangeCode(code, redirectUri, verifier) — POSTs to https://oauth2.googleapis.com/token with authorization_code grant and code_verifier
  • storeTokens(companyId, tokens) — upserts JSON-stringified tokens under name "google_gemini_oauth_token" via secretService
  • resolveTokens(companyId) — retrieves and JSON.parses stored tokens

googleOAuthRoutes (server/src/routes/google-oauth.ts)

  • POST /oauth/google/authorize — assertBoard, generates UUID state, stores PKCE verifier in pendingPkce (NO companyId), returns { url, stateId }
  • GET /oauth/google/callback — exchanges code via pendingPkce verifier, parks tokens in pendingTokens by stateId, redirects to /?google_oauth=success&state=...
  • POST /oauth/google/claim — assertBoard + assertCompanyAccess, moves tokens from pendingTokens to secretService under real companyId, returns { ok: true }
  • POST /api-keys/store — assertBoard + assertCompanyAccess, upserts ${provider}_api_key secret (openai/anthropic/groq), returns { ok: true }
  • Cleanup of entries older than 10 minutes on each request

Test Coverage (server/src/__tests__/31-google-oauth.test.ts)

All 11 tests pass:

  • Tests 1-2: PKCE generation format and auth URL contents
  • Test 3: token exchange HTTP call
  • Tests 4-5: storeTokens create and rotate paths
  • Test 6: authorize returns { url, stateId } with no companyId
  • Test 7: callback exchanges code and redirects with success
  • Test 8: callback with invalid state returns 400
  • Test 9: full authorize→callback→claim flow
  • Test 10: claim with missing stateId returns 404
  • Test 11: api-keys/store upsert

Decisions Made

  1. pendingPkce stores only { verifier, createdAt } — no companyId stored at authorize time because the company has not been created yet during onboarding step 3. This was the key design requirement.

  2. pendingTokens pattern — callback stores tokens in memory by stateId; claim endpoint later links them to a real companyId. This separates the OAuth callback timing from company creation.

  3. Client ID is public812546505895-ag9nvbqvf8cpqk3mfem1glig0jtl5i31.apps.googleusercontent.com is the publicly documented Gemini CLI installed-app client_id (per RESEARCH.md). PKCE flow is appropriate for public clients.

Deviations from Plan

None — plan executed exactly as written.

Known Stubs

None — all functionality is fully wired.

Self-Check: PASSED

Files created:

  • server/src/services/google-oauth.ts: EXISTS
  • server/src/routes/google-oauth.ts: EXISTS
  • server/src/tests/31-google-oauth.test.ts: EXISTS

Commits:

  • 72045513: feat(31-02): add googleOAuthService with PKCE generation and token management
  • c41ec162: feat(31-02): add googleOAuthRoutes with pendingTokens pattern and mount in app.ts
  • d750d15f: test(31-02): add 11 unit tests for Google OAuth service and routes