Google connectors — Gmail and Google Drive (one OAuth dance)

Single Google OAuth flow covers both Gmail and Drive. Delta-cursor sync per surface, encrypted token storage, refresh-token rotation handled.

Updated 2026-04-30

Two connector kinds — Gmail and Google Drive — share one OAuth flow. One consent screen, one token, two sync surfaces.

## Setup

In Google Cloud Console (console.cloud.google.com):

1. Pick the same OAuth client you use for Auth.js sign-in (or create a new one — Application type "Web application") 2. Add to **Authorized redirect URIs:** ``` <your-kodori-domain>/api/oauth/callback/google ``` 3. In **APIs & Services → Library**, enable **Gmail API** and **Google Drive API** for the project the OAuth client lives in 4. The OAuth consent screen needs the requested scopes added (gmail.readonly, drive.readonly, userinfo.email, userinfo.profile) — click **Add or Remove Scopes** if they aren't listed

Set environment variables on your Kodori deployment:

- `GOOGLE_OAUTH_CLIENT_ID` — Client ID (or reuse `AUTH_GOOGLE_ID` if you're already running Google sign-in; the connector falls back to the sign-in env automatically) - `GOOGLE_OAUTH_CLIENT_SECRET` — Client secret (or reuse `AUTH_GOOGLE_SECRET`)

Then click **Connect** next to Gmail or Google Drive on `/integrations`. Both kinds connect through the same OAuth route; the `?kind=` query param tells the callback which connector row to create.

## What gets synced

- **Gmail** → messages from your Inbox via `users.history.list` (incremental delta) + `users.messages.get` (per-message body extraction). HTML bodies are stripped to plain text for indexing. First-time sync bootstraps a recent batch via `messages.list?labelIds=INBOX`. - **Google Drive** → files from your personal Drive via `changes.list` (incremental delta). First-time sync captures a future-anchored cursor via `changes.getStartPageToken` and backfills via `files.list?orderBy=modifiedTime desc`. Folders and trashed items are skipped.

Both use the standard delta-cursor pattern so subsequent runs return only changes since the last cursor advance.

## What does NOT get synced (by design)

- File **bytes** are not mirrored. The connector stores text + URL pointer + metadata; the vendor stays the source of truth. Delete a file in Drive → it disappears from Kodori on the next sync. - Gmail attachments (next-batch upgrade — message body indexes today) - Drive shared drives / Team Drives (currently personal Drive only — add corpora=drive support when a customer asks) - Google Workspace admin-mode (would require domain-wide delegation + a service account, separate flow)

## Recurring sync

The cron at `*/30 * * * *` keeps every connected connector fresh — no operator action needed. /integrations/[id] still has a Sync now button for immediate refresh.