Google Workspace — how to connect
The plugin ships one node type, Google/Account: a node whose content is your Google OAuth
connection and whose page shows your upcoming calendar, latest unread mail and recent Drive
files — read-only. Getting the plugin puts a My Google node in your own space; that copy is
yours to configure and nobody else can read it.
0. Who supplies the OAuth client
The OAuth client (id + secret) is the operator's developer credential, so it belongs to the
installation: a platform administrator sets Google:OAuth:ClientId, Google:OAuth:ClientSecret
and Google:OAuth:RedirectUri once, in Administration ▸ Integrations, and registers the
authorize page's URL (/api/content/Google/content/authorize.html on the instance) as the
client's redirect URI. You then only give your own consent (section 3) and never handle a
developer key; the page says so and offers no client fields.
A refresh token is bound to the client that minted it, so an account that already holds its own complete client keeps using it (a notice says an installation client now exists). Sections 1 and 2 below describe that own-client path, and are the administrator's checklist for creating the installation's client.
Connect with just your Gmail address (installations without their own client)
On an installation that has no Google client of its own (a local install such as a Mac mini, or any cloud instance whose administrator did not configure one), open My Google, type your Gmail address and follow Connect with Google. You sign in on the mother instance (the registry this installation already talks to) with the same email, approve this installation, finish Google's consent and come back. There is no client id, no client secret and no code to paste.
- The Google client and your refresh token live only on the mother instance. This installation holds a revocable binding and asks the mother for short-lived access tokens.
- Mail never passes through the mother — it is read from Google directly. Keeping a copy of your mail is a separate, per-installation choice that is off until you switch it on there.
- On the mother, Settings ▸ Google connections lists the installations you approved (last use, and what each says about keeping a mail copy) with Revoke and Disconnect Google.
- If the mother cannot be reached, a token already issued keeps working until it expires (about an hour); then the Google sections show a notice and retry. Consent withdrawn at Google shows Reconnect.
The design of record is Connect Gmail through the mother instance.
1. Create the OAuth client (console.cloud.google.com, ~5 minutes)
- Create or pick a project.
- APIs & Services ▸ Library — enable Google Calendar API, Gmail API and Google Drive API (each is one click; a section 403s later if its API is missing).
- APIs & Services ▸ OAuth consent screen — External, fill the two required fields, and add yourself as a test user. (In test mode the consent — and with it the refresh token — expires after 7 days; publish the app to make grants long-lived.)
- Credentials ▸ Create credentials ▸ OAuth client ID ▸ Web application. Under Authorized
redirect URIs add the authorize page's URL — open
/api/content/Google/content/authorize.htmlon your mesh and copy the URL it displays at the top.
2. Configure your node
Open your My Google node. Until it is connected, its page IS the connect form:
| Setting | Value | How it is entered |
|---|---|---|
clientId |
the client id (….apps.googleusercontent.com) |
typed into the form; saved as you leave the field |
clientSecret |
the client secret | 🔑 Enter secret or code opens a masked dialog; paste it and press Save |
The client id is bound straight to the node — there is no Save button for it, and nothing you did not touch is ever rewritten. The secrets are not: they are write-only, so they go through their own dialog, which has a field each for the client secret, the authorization code and the refresh token. Fill only what you have; a blank field keeps the stored value rather than wiping it.
On the node's Edit view: redirectUri (the authorize page's exact URL — the same value you
registered in step 1.4), and optionally days (calendar window, default 7, clamped 1–31) and
limit (rows per section, default 10, clamped 1–25).
3. Sign in once
The node's page now shows Sign in with Google — the link opens the shipped authorize page,
which sends you to Google's consent screen (the three read-only scopes) and comes back with a
one-time code. Paste it into the form's secrets dialog (🔑 Enter secret or code →
Authorization code); the next render exchanges it and shows your refresh token. The form is
gone at that point — the page has moved on to the token hand-over — so store the token the way the
hand-over card says: in the node's refreshToken setting on its Edit view, clearing authCode
there too (codes are single-use and expire in minutes). From then on every Refresh signs in
silently. (The dialog's Refresh token field is for the other route: pasting a token you already
hold, to skip the consent, while the form is still showing.)
What renders
- 📅 Calendar — your primary calendar's next
daysdays: all-day events on their date (a calendar fact, never shifted), timed events in your time zone; cancelled events struck through. - ✉️ Unread mail — the latest unread subjects and senders. Headers only: the plugin fetches message metadata, never bodies, and shows at most five header rows per refresh.
- 📁 Drive — your most recently modified files, each linked into Drive.
Each section fails alone: a disabled API names itself (and where to enable it), a revoked grant says to re-authorize, and the other sections keep rendering.
Your Gmail in the inbox
Once your node is connected (client id, client secret and refresh token all set), your Gmail
Inbox appears in your unified inbox (/{you}/Inbox) as the 📨 Gmail source under Mail —
read live from Gmail every time the inbox opens or you search, never copied into the mesh unless you switch a copy on for this account on this installation (Settings → Inbox copies; nothing is copied by default, and connecting Google never starts a copy). The
search box searches Gmail itself (Gmail's own operators work), a row opens the thread in Gmail, and
a row is bold while it is unread. Nothing else to set up: the inbox uses the same node, and only you
can see your rows. Until you connect, the inbox simply has no Gmail rows.
Delete and Undo need more than the read-only consent. The sign-in above grants
gmail.readonly, so Delete on a Gmail row says it is read-only and changes nothing. To let Delete
move a message to Gmail's Trash (and Undo bring it back), mint a refresh token on your own OAuth
client with the https://www.googleapis.com/auth/gmail.modify scope as well — for example in the
OAuth 2.0 Playground (⚙️ ▸ Use your own OAuth
credentials, after adding https://developers.google.com/oauthplayground to the client's
authorized redirect URIs) — and store it as refreshToken on the node's Edit view. How it
works: Gmail in the unified inbox.
Security, by design
- Read-only scopes.
calendar.readonly,gmail.readonly,drive.metadata.readonly— Google's consent screen shows exactly this, and the plugin cannot send, delete or modify. (Only if you store a token you minted withgmail.modifyyourself can the inbox's Delete move a message to the Trash; nothing here can send.) - The client is the installation's (or your own, if you already had one). Your grant is your Google consent only; the developer credential never sits in your node.
- Secrets render only as masks —
····plus the last four characters. The one surface that contains the real values is the node's own Edit view (that is how you set them), protected by your space's access control: keep the node in your own space. - Revocation is instant: myaccount.google.com/permissions kills the refresh token; deleting the OAuth client in the console kills everything.
What the mesh calls
oauth2.googleapis.com/token (the refresh grant, per refresh), then
www.googleapis.com/calendar/v3/calendars/primary/events,
gmail.googleapis.com/gmail/v1/users/me/messages (+ per-message metadata), and
www.googleapis.com/drive/v3/files. All through the mesh's bounded HTTP pool; a stalled call
degrades to a diagnosis on the page, never a hang.