Add latchkey prepare command and PKCE for Google OAuth#92
Conversation
Add a top-level `latchkey prepare <service> <json>` command that stores a
service's credentials from a validated JSON payload. Services opt in via an
optional `prepareFromJson` hook backed by a Zod schema (the base default is
"not supported"); Google services accept `{clientId, clientSecret}` and store
token-less OAuth credentials. This lets users register the official Imbue OAuth
client directly instead of minting their own via the Cloud Console. The older
`auth browser-prepare` flow remains as a fallback.
Wire PKCE (S256) into the Google OAuth login flow: the authorization request
now sends code_challenge/code_challenge_method and the token exchange sends the
matching code_verifier, while still sending the client secret (confidential
desktop client + PKCE, defense-in-depth).
- Forward `prepare` through gateway mode like `browser-prepare`
- Update Google service info strings + README to recommend `prepare`
- Tests for prepareService, the CLI command, the gateway endpoint, and PKCE
Co-authored-by: Sculptor <sculptor@imbue.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Wrap the long `.description()` line in the new `prepare` command to satisfy prettier. Co-authored-by: Sculptor <sculptor@imbue.com> Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
4eabf45 to
367bf2b
Compare
Revert the per-service `info` strings to recommend `auth browser-prepare` (the default OAuth-client setup) rather than `latchkey prepare`. Reword the README so `prepare` is documented as an alternative without demoting `browser-prepare`. Co-authored-by: Sculptor <sculptor@imbue.com> Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
| /** | ||
| * Google services accept an official OAuth client's id/secret via | ||
| * `latchkey auth prepare`, stored as token-less OAuth credentials until login. | ||
| */ |
There was a problem hiding this comment.
🔴 Vet Issue commit_message_mismatch severity: 3/5, confidence: 0.80
The request explicitly states 'Updated every Google service's info string (surfaced via services info) to recommend prepare first, with browser-prepare as the fallback' and 'Updated the README command overview.' Neither the README nor any service info strings are modified in the diff, leaving the documentation portion of the request unimplemented.
|
@pseay-imbue Thanks, Preston! I finalized this by doing the following:
|
| /** | ||
| * Google services accept an OAuth client's id/secret prepared | ||
| * in advance via `latchkey auth prepare`, stored as token-less OAuth credentials until login. | ||
| */ |
There was a problem hiding this comment.
🔴 Vet Issue documentation_implementation_mismatch severity: 3/5, confidence: 0.80
The user request states 'Updated every Google service's info string (surfaced via services info) to recommend prepare first, with browser-prepare as the fallback' and 'Updated the README command overview.' However, the diff contains no changes to any service's info string nor to the README. The documentation updates described in the request are missing.
Summary
Two related changes, both in order to add a more seemless OAuth experience with an official Imbue OAuth Client:
1.
latchkey prepare <service> <json>A new top-level command that stores a service's credentials directly from a validated JSON payload — the manual equivalent of what
auth browser-prepareproduces, without driving the Cloud Console.prepareFromJsonhook onService, backed by a Zod schema. The base default is "not supported", so every service stays closed until it declares a schema.GoogleServiceimplements it once:{ "clientId": "...", "clientSecret": "..." }→ token-lessOAuthCredentials. Runauth browser <service>afterward to complete login (status ismissinguntil then, same asbrowser-prepare).browser-prepare.auth browser-prepare(Console automation) remains as a fallback.2. PKCE for the Google OAuth login flow
code_challenge+code_challenge_method=S256, and the token exchange sends the matchingcode_verifier.client_secretis still sent (confidential desktop client + PKCE — defense-in-depth). The refresh-token path is unchanged.oauthUtils.ts(already used bynotion-mcp); the Google flow simply wasn't calling them.Docs
infostring (surfaced viaservices info) to recommendpreparefirst, withbrowser-prepareas the fallback.Testing
npm run lint,npm run typecheck,npm run build, andnpm testall pass.prepareService(happy path, overwrite, unknown/unsupported service, malformed JSON, missing/unknown fields, empty fields), the CLI command (success, error exits, gateway forwarding), the gateway endpoint (schema validation + dispatch), and strengthened the PKCEgenerateCodeChallengetest with the RFC 7636 known-answer vector.Notes
Servicemock compiling and leaves all current services' behavior unchanged.🤖 Generated with Claude Code