From fe65166ecddaa82ecde35e72732cb499e30f02a7 Mon Sep 17 00:00:00 2001 From: Nexus Dev Date: Fri, 3 Apr 2026 00:38:00 +0000 Subject: [PATCH] docs(31-02): complete Google OAuth PKCE plan summary and state updates --- .planning/REQUIREMENTS.md | 8 +- .planning/STATE.md | 10 +- .../31-02-SUMMARY.md | 103 ++++++++++++++++++ 3 files changed, 113 insertions(+), 8 deletions(-) create mode 100644 .planning/phases/31-puter.js-zero-config-cloud/31-02-SUMMARY.md diff --git a/.planning/REQUIREMENTS.md b/.planning/REQUIREMENTS.md index 87d4d05d..495d5f4f 100644 --- a/.planning/REQUIREMENTS.md +++ b/.planning/REQUIREMENTS.md @@ -19,9 +19,9 @@ - [x] **CLOUD-01**: User gets working AI via Puter.js with zero API keys and no sign-up required - [x] **CLOUD-02**: Puter.js integrated as server-proxied adapter (not browser-direct) with full cost tracking -- [ ] **CLOUD-03**: User can sign in via Google OAuth to access Gemini free tier +- [x] **CLOUD-03**: User can sign in via Google OAuth to access Gemini free tier - [ ] **CLOUD-04**: System auto-detects installed tools (Hermes, Claude Code, OpenClaw) and pre-fills configuration -- [ ] **CLOUD-05**: User can enter API keys for subscription providers during onboarding +- [x] **CLOUD-05**: User can enter API keys for subscription providers during onboarding ### Voice @@ -75,9 +75,9 @@ | ONBD-07 | Phase 30 | Complete | | CLOUD-01 | Phase 31 | Complete | | CLOUD-02 | Phase 31 | Complete | -| CLOUD-03 | Phase 31 | Pending | +| CLOUD-03 | Phase 31 | Complete | | CLOUD-04 | Phase 31 | Pending | -| CLOUD-05 | Phase 31 | Pending | +| CLOUD-05 | Phase 31 | Complete | | ONBD-04 | Phase 32 | Pending | | ONBD-05 | Phase 32 | Pending | | ONBD-06 | Phase 32 | Pending | diff --git a/.planning/STATE.md b/.planning/STATE.md index 97630301..71cb2782 100644 --- a/.planning/STATE.md +++ b/.planning/STATE.md @@ -3,8 +3,8 @@ gsd_state_version: 1.0 milestone: v1.5 milestone_name: Smart Onboarding + Personal AI Assistant status: executing -stopped_at: Completed 31-puter.js-zero-config-cloud/31-01 -last_updated: "2026-04-03T00:37:39.937Z" +stopped_at: Completed 31-puter.js-zero-config-cloud/31-02 (all 3 tasks, 11 tests pass) +last_updated: "2026-04-03T00:37:50.371Z" last_activity: 2026-04-03 progress: total_phases: 6 @@ -76,6 +76,8 @@ Key constraints for v1.5 (established at roadmap): - [Phase 30-hardware-detection-mode-selection]: Hardware probe is non-blocking — wizard step 1 always has an enabled Continue button regardless of probe outcome - [Phase 30-hardware-detection-mode-selection]: Mode save on wizard completion is non-blocking — wrapped in try/catch, defaults to 'both' on failure - [Phase 31-puter.js-zero-config-cloud]: agentId is optional in puterProxyService.chatStream — cost recording skipped when null/undefined to avoid FK violation in cost_events +- [Phase 31-puter.js-zero-config-cloud]: pendingPkce stores only verifier (no companyId) — company does not exist at authorize time during onboarding +- [Phase 31-puter.js-zero-config-cloud]: pendingTokens pattern: callback parks tokens by stateId, claim endpoint links to real companyId post-company-creation ### Pending Todos @@ -90,6 +92,6 @@ None yet. ## Session Continuity -Last session: 2026-04-03T00:37:30.288Z -Stopped at: Completed 31-puter.js-zero-config-cloud/31-01 +Last session: 2026-04-03T00:37:50.368Z +Stopped at: Completed 31-puter.js-zero-config-cloud/31-02 (all 3 tasks, 11 tests pass) Resume file: None diff --git a/.planning/phases/31-puter.js-zero-config-cloud/31-02-SUMMARY.md b/.planning/phases/31-puter.js-zero-config-cloud/31-02-SUMMARY.md new file mode 100644 index 00000000..7a57f185 --- /dev/null +++ b/.planning/phases/31-puter.js-zero-config-cloud/31-02-SUMMARY.md @@ -0,0 +1,103 @@ +--- +phase: 31-puter.js-zero-config-cloud +plan: "02" +subsystem: server/google-oauth +tags: [oauth, pkce, google, gemini, api-keys, secrets] +dependency_graph: + requires: [] + provides: [googleOAuthService, googleOAuthRoutes, google-oauth-routes] + affects: [server/src/app.ts] +tech_stack: + added: [] + patterns: [PKCE-S256, pendingTokens-claim-pattern, secretService-upsert] +key_files: + created: + - server/src/services/google-oauth.ts + - server/src/routes/google-oauth.ts + - server/src/__tests__/31-google-oauth.test.ts + modified: + - server/src/app.ts +decisions: + - 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 +metrics: + duration: "3 minutes 22 seconds" + completed: "2026-04-03T00:36:57Z" + tasks_completed: 3 + files_created: 3 + files_modified: 1 +requirements: [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 public** — `812546505895-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