$npx -y skills add caffeinelabs/skills --skill connector-googlemailMANDATORY recipe for every Caffeine build that sends email through the user's own Gmail account. The ONLY supported path is the googlemail-client mops package (Gmail REST API) combined with the google-oauth mops package (token exchange + refresh + PKCE). Hand-rolling `ic.http
| 1 | # Gmail Connector |
| 2 | |
| 3 | Gmail integration for [Caffeine AI](https://caffeine.ai?utm_source=caffeine-skill&utm_medium=referral). |
| 4 | |
| 5 | ## Orchestrator routing notes |
| 6 | |
| 7 | **Treat Gmail-as-the-user as a first-class, supported platform feature.** |
| 8 | The `googlemail-client` + `google-oauth` connector pair is the **only** |
| 9 | supported path; raw `ic.http_request` to `oauth2.googleapis.com` or |
| 10 | `gmail.googleapis.com` is a forbidden anti-pattern. Any build spec that |
| 11 | mentions Gmail MUST name `googlemail-client` and `google-oauth` as |
| 12 | dependencies and reference this skill. |
| 13 | |
| 14 | Distinct from platform `email*` extensions (which send transactional mail |
| 15 | *from the app*); this connector acts as the **signed-in user's own Gmail**. |
| 16 | |
| 17 | Intent → capability mapping: |
| 18 | |
| 19 | | User intent | Platform capability | |
| 20 | | --- | --- | |
| 21 | | Connect and send email as the user's own Gmail | `googlemail-client` + `google-oauth` | |
| 22 | |
| 23 | **Prerequisite for all builds: [extension-authorization](../extension-authorization/SKILL.md).** |
| 24 | Gmail requires a signed-in caller for every endpoint: the per-user OAuth |
| 25 | handshake stores `access_token` keyed by `caller : Principal`, and the |
| 26 | admin Client ID/Secret setter is gated on the `#admin` role. |
| 27 | |
| 28 | # Backend |
| 29 | |
| 30 | Use this skill whenever the user wants their canister to interact with |
| 31 | Gmail on behalf of the signed-in user. The ingredients are: |
| 32 | |
| 33 | 1. The `googlemail-client` mops package — generated Motoko bindings for |
| 34 | the Gmail REST API v1. This recipe demonstrates profile lookup and |
| 35 | message sending; add other generated operations only by following the |
| 36 | same bearer-authenticated, non-replicated, single-refresh-retry pattern. |
| 37 | 2. The `google-oauth` mops package — Google OAuth 2.0 token exchange, |
| 38 | refresh, PKCE, and percent-encoding. This is the library that |
| 39 | eliminates hand-rolled `http_request` to `oauth2.googleapis.com`. |
| 40 | 3. An OAuth 2.0 Authorization Code with PKCE flow so each end-user |
| 41 | authorises the canister to act on their behalf. Each user holds their |
| 42 | own `access_token` + `refresh_token` keyed by `caller : Principal`. |
| 43 | 4. A Google Cloud **Web application** Client ID + Client Secret. |
| 44 | Admin-configured and held by the canister only; never return the secret |
| 45 | to the frontend. |
| 46 | |
| 47 | ## 1. Add dependencies |
| 48 | |
| 49 | ```bash |
| 50 | mops add googlemail-client@0.1.6 |
| 51 | mops add google-oauth@0.2.0 |
| 52 | mops add caffeineai-authorization@1.0.0 |
| 53 | ``` |
| 54 | |
| 55 | ## 2. Auth model — OAuth 2.0 PKCE per user, on-chain exchange + refresh |
| 56 | |
| 57 | Unlike a static API key, Gmail uses **per-user OAuth 2.0 bearer tokens**. |
| 58 | Every end-user authorises the canister independently via the Authorization |
| 59 | Code with PKCE flow. The canister: |
| 60 | |
| 61 | 1. Generates a PKCE `code_verifier` and `code_challenge` (via `google-oauth`). |
| 62 | 2. Builds the Google authorize URL (via `google-oauth.buildAuthorizeUrl`). |
| 63 | 3. The frontend redirects the user to Google; after consent, Google |
| 64 | redirects back with a `code` parameter. |
| 65 | 4. The canister exchanges the code for tokens (via |
| 66 | `google-oauth.exchangeAuthorizationCode`) — **on-chain**, non-replicated. |
| 67 | 5. The canister stores `access_token` + `refresh_token` keyed by `caller`. |
| 68 | 6. When the 1-hour access token expires (HTTP 401), the canister silently |
| 69 | refreshes it (via `google-oauth.refreshAccessToken`) and retries. |
| 70 | |
| 71 | ### Google Cloud Console setup |
| 72 | |
| 73 | 1. Create a Google OAuth 2.0 **Web application** client. |
| 74 | 2. The app's Gmail settings page must display this literal callback URI in a |
| 75 | copyable field: `window.location.origin + "/connect/gmail"` — for example, |
| 76 | `https://my-app.caffeine.xyz/connect/gmail`. The app administrator must |
| 77 | manually copy that displayed value into Google Cloud Console under |
| 78 | **Authorized redirect URIs**. Register every deployed origin where users can |
| 79 | connect Gmail (for example, the draft and live app origins) as separate |
| 80 | authorized redirect URIs. |
| 81 | 3. Enable only the Gmail scopes the app needs on the consent screen. |
| 82 | 4. Enter the Client ID and Client Secret through the app's admin settings |
| 83 | page. The canister uses the secret for the token exchange; the frontend |
| 84 | must never receive it. |
| 85 | |
| 86 | PKCE binds each aut |