Google Workspace: multi-connector setup
This page is not about a single connector — it's about running
identos.google-drive, identos.google-gmail,
identos.google-calendar, identos.google-chat and
identos.google-directory together off a single Google IdP.
One IdP, many connectors
The expected setup looks like this:
identity_provider
└── google (registered once, OAuth client_id/client_secret from Google Cloud)
managed_resource
├── drive-user → google-drive + idp_passthrough + google IdP
├── gmail-user → google-gmail + idp_passthrough + google IdP
├── calendar-user → google-calendar + idp_passthrough + google IdP
├── calendar-rooms → google-calendar + google_sa + service account
├── chat-user → google-chat + idp_passthrough + google IdP
└── directory → google-directory+ idp_passthrough + google IdP
A google_sa resource such as calendar-rooms needs one admin API step
after you create it in the Admin Portal, or every call gets 502. The
Google Calendar page gives the command.
A single Google login grants access to all of them at the same time. A Claude Code session hitting this gateway picks up tools from every one because they're all provisioned against the same authenticated subject.
Why you almost always want google-directory alongside the others
The rest of the Google APIs identify people by opaque numeric ID. Chat
returns members and message senders as users/102770538280811199852 with no
name and no email; Drive's list_file_permissions returns permission holders;
Calendar attendees arrive as bare addresses needing profile lookup. Two Chat
tools — find_direct_message and add_member — require a users/{id} that
no Chat tool can produce.
identos.google-directory is the resolver for all of that. Without it, an
agent can read a Chat space's whole history and still not be able to tell you
who said what.
Do not assume an ID from one Google API works in another. Observed live: Drive
permission IDs are 20 digits and can carry leading zeros
(01611141363991203537), while Chat member IDs are 21 digits without
(102770538280811199852) — different identifier spaces. google-directory
resolves the People API space, which is the one Chat uses.
How scope growth works
The tricky part is OAuth scopes. Each connector declares
upstream_scopes on its idp_passthrough auth option, the Google OAuth
scopes its users need. Every one of the five asks for openid and
userinfo.email, and then these:
| Connector | Google scopes, after https://www.googleapis.com/auth/ |
|---|---|
| Drive | drive.readonly, drive.file, drive.labels.readonly, drive.labels |
| Gmail | gmail.readonly, gmail.send, gmail.modify, gmail.labels |
| Calendar | calendar.readonly, calendar.events, calendar.acls.readonly |
| Chat | chat.spaces, chat.messages, chat.memberships |
| Directory | directory.readonly |
When you provision a managed resource in idp_passthrough mode, the
gateway automatically merges that connector's upstream_scopes
into the target IdP's scopes list (IdpScopeMerger). Subsequent
user logins at /authorize pass the union of all those scopes to
Google, and the user consents once for everything.
before drive-user provision: idp.scopes = [openid, email]
after drive-user provision: idp.scopes = [openid, email, userinfo.email,
drive.readonly, drive.file,
drive.labels.readonly, drive.labels]
after gmail-user provision: idp.scopes = [... + gmail.readonly,
gmail.send, ...]
after calendar-user provision: idp.scopes = [... + calendar.readonly,
calendar.events,
calendar.acls.readonly]
Scope names are shortened here. The IdP row holds the full
https://www.googleapis.com/auth/... form.
The merge is additive and idempotent — uninstalling a connector does not remove scopes (another managed resource may still need them, and we can't safely attribute scopes to a single installer).
When a user's stored Google token lacks a new scope
If you install or provision a Google connector after users have
already signed in at Google through PolicyArc, their stored Google tokens
do not carry the newly added scopes. In idp_passthrough mode the gateway
compares the stored token's granted scopes with the ones the connector
requires, and the first call that finds one missing returns 401:
{"error": "idp_link_required", "idp": "google", "link_url": "https://<your-environment>/gateway/link?ls=..."}
The message alongside it is "Connect your google account to use this tool." A user who has never linked Google gets the same response.
Two paths out:
- Recommended: the user opens the
link_urland consents on Google's screen. PolicyArc requests the expanded scope list. No operator action required. - Batch: delete the affected users' stored tokens with
DELETE /admin/api/upstream-identity-tokens?subjectId=<sub>&idpId=<id>, where<id>is the IdP's numericidfromGET /admin/api/identity-providers, not itsidpIdstring. A string gets400. Their next gateway call returns the same link.
Going forward, the rule is: install and provision Google connectors before inviting users to the gateway, and users never see this step.
Pre-configuring the Google OAuth app
On the Google Cloud side, you still need to add these scopes to the OAuth 2.0 client's OAuth consent screen configuration so Google will permit them. The PBAC auto-merge handles the PBAC side; it can't teach Google about scopes its consent screen hasn't been told to offer.
The scopes the five connectors request in idp_passthrough mode, taken
from their manifests. Add only the lines for the connectors you install;
the table under How scope growth works says
which connector needs which.
openid
https://www.googleapis.com/auth/userinfo.email
https://www.googleapis.com/auth/drive.readonly
https://www.googleapis.com/auth/drive.file
https://www.googleapis.com/auth/drive.labels.readonly
https://www.googleapis.com/auth/drive.labels
https://www.googleapis.com/auth/gmail.readonly
https://www.googleapis.com/auth/gmail.send
https://www.googleapis.com/auth/gmail.modify
https://www.googleapis.com/auth/gmail.labels
https://www.googleapis.com/auth/calendar.readonly
https://www.googleapis.com/auth/calendar.events
https://www.googleapis.com/auth/calendar.acls.readonly
https://www.googleapis.com/auth/chat.spaces
https://www.googleapis.com/auth/chat.messages
https://www.googleapis.com/auth/chat.memberships
https://www.googleapis.com/auth/directory.readonly
Set the consent screen to External → Production (or Internal for a Workspace-only deployment) once you're past testing and want tokens to survive the 7-day expiry.
Two managed resources, one connector
Running the same connector in both idp_passthrough and google_sa
mode requires two managed resources (e.g. calendar-user and
calendar-rooms). A single managed resource picks one auth mode at
provision time; there's no runtime switch.
Operator data does not tell them apart. room_only_mode is one key in
pbac.operator.connectors.identos.google-calendar, so turning it on
applies to every Google Calendar resource, calendar-user included. A rule
you write yourself can tell the two resources apart by
input.context.rs_context.resource_name.
Troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
401 with "error": "idp_link_required" | The user has no linked Google token, or signed in before the connector was provisioned | Open the link_url, or delete the stored token with the admin API |
502 "Upstream auth failed: token resolution requires an authenticated subject" | Client used client_credentials (no user context) | Issue the call with an authorization_code token. google_sa mode does not avoid this, because it impersonates the calling user and a client_credentials call gets 502 No user identity in context; cannot impersonate |
404 on /admin/api/connectors/identos.google-gmail/install | Jar was built before the connector was added | Rebuild the jar (mvn package -DskipTests) and recreate the instance |
| PDP denies every Gmail send with "recipient domain ... is on the operator denylist" | blocked_recipient_domains data is set and a recipient matches | Review the operator-data row pbac.operator.connectors.identos.google-gmail, which Rego reads as data["pbac.operator.connectors.identos.google-gmail"] |
See also
- Google Drive connector
- Gmail connector
- Google Calendar connector
- Google Chat connector
- Google Directory connector — resolves the principal IDs the others return
- MCP clients — how Claude Code and other MCP clients attach to the gateway
Send feedback about this page