feat(oauth): switch Google OAuth from PKCE to authorization code flow with client secret

Previously, the Google OAuth integration used a PKCE-only flow (no client
secret). This switches to a standard authorization code flow where the user
provides both a Client ID and Client Secret from a "Web application" type
OAuth client in Google Cloud Console. PKCE is retained alongside the secret
for defense in depth.

Key changes:

- oauth-client.ts: discoverConfiguration() and createStaticConfiguration()
  now accept an optional clientSecret param. When provided, uses
  ClientSecretPost instead of None() for client authentication.

- oauth-handler.ts: connectProvider() takes a credentials object
  ({clientId, clientSecret}) instead of a bare clientId. Removed eager
  persistence of clientId before flow completion — credentials are now
  only saved after successful token exchange. Renamed resolveClientId to
  resolveClientCredentials to return both values from a single repo read.

- google-client-factory.ts: same resolveClientId → resolveCredentials
  rename. Passes clientSecret to OAuth2Client constructor and
  discoverConfiguration for token refresh.

- repo.ts: added clientSecret to ProviderConnectionSchema. Not exposed
  to renderer via ClientFacingConfigSchema (stays main-process only).

- IPC: added clientSecret to oauth:connect request schema. Handler builds
  a credentials object and passes it through.

- UI: GoogleClientIdModal now collects both Client ID and Client Secret
  (password field). Always shown on connect — no in-memory credential
  caching. Renamed google-client-id-store to google-credentials-store
  with a unified {clientId, clientSecret} object.

- google-setup.md: updated to instruct users to create a "Web application"
  type OAuth client (instead of UWP), add the localhost redirect URI, and
  copy both Client ID and Client Secret. Added credentials modal screenshot.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Ramnique Singh 2026-04-10 00:42:59 +05:30
parent 924e136505
commit 50bce6c1d6
15 changed files with 155 additions and 138 deletions

View file

@ -1,6 +1,6 @@
# Connecting Google to Rowboat
Rowboat requires a Google OAuth Client ID to connect to Gmail, Calendar, and Drive. Follow the steps below to generate your Client ID correctly.
Rowboat requires Google OAuth credentials (Client ID and Client Secret) to connect to Gmail, Calendar, and Drive. Follow the steps below to generate them.
---
@ -114,34 +114,32 @@ Click **Create Credentials → OAuth Client ID**
Select:
**Universal Windows Platform (UWP)**
**Web application**
- Name it anything (e.g. `Rowboat Desktop`)
- Store ID can be anything (e.g. `test` )
- Click **Create**
![Create OAuth Client ID (UWP)](https://raw.githubusercontent.com/rowboatlabs/rowboat/main/apps/docs/docs/img/google-setup/05-create-oauth-client-uwp.png)
### Authorized redirect URIs
### Authorized redirect URIs (if shown)
If your OAuth client configuration shows **Authorized redirect URIs**, add:
Add the following redirect URI:
- `http://localhost:8080/oauth/callback`
Use this exactly: no trailing slash, port **8080**. This must match what the app uses for the OAuth callback. (Some client types, e.g. UWP, may not expose redirect URIs; that is fine.)
Use this exactly: no trailing slash, port **8080**. This must match what the app uses for the OAuth callback.
Click **Create**.
---
## 7⃣ Copy the Client ID
## 7⃣ Copy the Client ID and Client Secret
After creation, Google will show:
- **Client ID**
- **Client Secret**
Copy the **Client ID** and paste it into Rowboat where prompted.
Copy **both values** and paste them into Rowboat when prompted.
![Copy Client ID](https://raw.githubusercontent.com/rowboatlabs/rowboat/main/apps/docs/docs/img/google-setup/06-copy-client-id.png)
![Enter credentials in Rowboat](https://raw.githubusercontent.com/rowboatlabs/rowboat/main/apps/docs/docs/img/google-setup/07-enter-credentials.png)
---
@ -152,7 +150,7 @@ Copy the **Client ID** and paste it into Rowboat where prompted.
If the browser shows "Authorization Successful" but the app then shows an error (e.g. "invalid response encountered" or "response parameter \"iss\" (issuer) missing"):
1. **Check the app logs** (e.g. terminal or dev tools) for the full error. The message there will often indicate the cause (e.g. redirect URI mismatch, missing parameter).
2. **Verify redirect URI in Google Cloud Console**: Open [Credentials → your OAuth 2.0 Client ID](https://console.cloud.google.com/auth/clients). If the client type allows **Authorized redirect URIs**, ensure `http://localhost:8080/oauth/callback` is listed exactly.
3. **Client type**: Use **Desktop** or **UWP** as the application type. A "Web application" client may require the redirect URI to be set and can behave differently with localhost.
2. **Verify redirect URI in Google Cloud Console**: Open [Credentials → your OAuth 2.0 Client ID](https://console.cloud.google.com/auth/clients). Ensure `http://localhost:8080/oauth/callback` is listed under **Authorized redirect URIs**.
3. **Client type**: Make sure you selected **Web application** as the application type. Other types (Desktop, UWP) may not provide a client secret or may handle redirect URIs differently.
---