docs(31-02): complete Google OAuth PKCE plan summary and state updates
This commit is contained in:
parent
4068d3de08
commit
fe65166ecd
3 changed files with 113 additions and 8 deletions
|
|
@ -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-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
|
- [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-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
|
### Voice
|
||||||
|
|
||||||
|
|
@ -75,9 +75,9 @@
|
||||||
| ONBD-07 | Phase 30 | Complete |
|
| ONBD-07 | Phase 30 | Complete |
|
||||||
| CLOUD-01 | Phase 31 | Complete |
|
| CLOUD-01 | Phase 31 | Complete |
|
||||||
| CLOUD-02 | Phase 31 | Complete |
|
| CLOUD-02 | Phase 31 | Complete |
|
||||||
| CLOUD-03 | Phase 31 | Pending |
|
| CLOUD-03 | Phase 31 | Complete |
|
||||||
| CLOUD-04 | Phase 31 | Pending |
|
| CLOUD-04 | Phase 31 | Pending |
|
||||||
| CLOUD-05 | Phase 31 | Pending |
|
| CLOUD-05 | Phase 31 | Complete |
|
||||||
| ONBD-04 | Phase 32 | Pending |
|
| ONBD-04 | Phase 32 | Pending |
|
||||||
| ONBD-05 | Phase 32 | Pending |
|
| ONBD-05 | Phase 32 | Pending |
|
||||||
| ONBD-06 | Phase 32 | Pending |
|
| ONBD-06 | Phase 32 | Pending |
|
||||||
|
|
|
||||||
|
|
@ -3,8 +3,8 @@ gsd_state_version: 1.0
|
||||||
milestone: v1.5
|
milestone: v1.5
|
||||||
milestone_name: Smart Onboarding + Personal AI Assistant
|
milestone_name: Smart Onboarding + Personal AI Assistant
|
||||||
status: executing
|
status: executing
|
||||||
stopped_at: Completed 31-puter.js-zero-config-cloud/31-01
|
stopped_at: Completed 31-puter.js-zero-config-cloud/31-02 (all 3 tasks, 11 tests pass)
|
||||||
last_updated: "2026-04-03T00:37:39.937Z"
|
last_updated: "2026-04-03T00:37:50.371Z"
|
||||||
last_activity: 2026-04-03
|
last_activity: 2026-04-03
|
||||||
progress:
|
progress:
|
||||||
total_phases: 6
|
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]: 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 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]: 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
|
### Pending Todos
|
||||||
|
|
||||||
|
|
@ -90,6 +92,6 @@ None yet.
|
||||||
|
|
||||||
## Session Continuity
|
## Session Continuity
|
||||||
|
|
||||||
Last session: 2026-04-03T00:37:30.288Z
|
Last session: 2026-04-03T00:37:50.368Z
|
||||||
Stopped at: Completed 31-puter.js-zero-config-cloud/31-01
|
Stopped at: Completed 31-puter.js-zero-config-cloud/31-02 (all 3 tasks, 11 tests pass)
|
||||||
Resume file: None
|
Resume file: None
|
||||||
|
|
|
||||||
103
.planning/phases/31-puter.js-zero-config-cloud/31-02-SUMMARY.md
Normal file
103
.planning/phases/31-puter.js-zero-config-cloud/31-02-SUMMARY.md
Normal file
|
|
@ -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
|
||||||
Loading…
Add table
Reference in a new issue