Google Chat
Proxy for the Google Chat v1 REST API. Unlike sheets/docs/slides/drive,
Chat is idp_passthrough only — its API doesn't support service-account
domain-wide-delegation user-impersonation, so there is no google_sa mode.
Every request carries the caller's own OAuth grant, and the policy layer
adds operator-controlled guards on which spaces an agent may touch.
Overview
Typical uses:
- An agent lists the spaces a user is in and summarizes recent messages
(
list_spaces,list_messages,get_message). - A notification bot posts and later edits/deletes its own message, or
reacts to one (
create_message,update_message,delete_message,create_reaction,delete_reaction). - A space-admin workflow provisions a space and manages membership
(
create_space,add_member,remove_member,delete_space).
Two notes specific to Chat:
chat:admintools have upstream prerequisites. Space and membership management require the calling user to be a space manager and a configured Google Chat app; these tools may legitimately return403PERMISSION_DENIED— a Chat-side authorization outcome, not a gateway fault. The dedicatedchat:adminscope lets an operator grant messaging (chat:write) without also granting space/member management.- The Google OAuth client must have the Chat API enabled and the Chat app configured for user-auth calls to succeed.
Default policy
The connector ships policy/chat.rego
(package pbac.connectors.identos.google_chat). It enforces:
1. IdP-routing subject obligation
subject_obligations contains obl if {
input.resource.type == "urn:connector:identos:google-chat"
input.connector.upstream_auth.type == "idp_passthrough"
input.subject.idp_provider != "google"
obl := { "type": "require_authn_at", "idp": "google", ... }
}
Same pattern as google-drive/gmail — a caller from a non-Google session gets a step-up obligation before any Chat call succeeds.
File access control
Blocking a specific space is operator config on the general file-access
control, shared with the Google file connectors — not a rule in this
connector. An identity source matches a space by its own id: ids in
blocked are denied on reads and writes, ids in write_blocked are denied on
writes only, so reads still work.
curl -X PUT http://localhost:8080/admin/policy/data/pbac/operator/controls/file_access \
-H "X-Admin-API-Key: $PBAC_ADMIN_API_KEY" \
-d '{
"sources": {
"chat-spaces": {
"type": "identity",
"blocked": ["spaces/AAAA_leadership"],
"write_blocked": ["spaces/BBBB_announce"]
}
},
"governed": {
"urn:connector:identos:google-chat": ["chat-spaces"]
}
}'
The control reads the id from the space_id tool argument and applies to
chat:read, chat:write and chat:admin calls alike. Listing a connector under governed is what activates it; omit it
and the control is inert. Full mechanics (pooling, deny-wins, fail-closed) live
in the File Access Control guide.
Scope model
| Scope | Used for | |
|---|---|---|
| PBAC scopes (internal) | chat:read, chat:write, chat:admin | Route authorization — chat:admin gates space/member management separately from messaging, so you can grant an agent chat:write (post/edit/react) without space-create/delete or member changes |
| Upstream OAuth scopes | chat.spaces, chat.messages, chat.memberships | The Google-side grants the user must consent to. (delete_space additionally needs chat.delete, a restricted scope not requested by default.) |
See Google Workspace: multi-connector setup for how chat's scopes compose with drive, gmail, and calendar on a single Google IdP.
Manifest reference
- ID:
identos.google-chat - Version:
1.2.0 - Resource type:
urn:connector:identos:google-chat
Supported auth modes
| Type | Details |
|---|---|
idp_passthrough | requires IdP google |
Setup fields
| ID | Label | Default | Secret? | Notes |
|---|---|---|---|---|
base_url | API base URL | https://chat.googleapis.com | no | — |
Scopes
| Scope |
|---|
chat:read |
chat:write |
chat:admin |
Routes
| Method | Pattern | Scope | Resource template |
|---|---|---|---|
GET | /v1/spaces | chat:read | — |
GET | /v1/spaces/{space_id} | chat:read | chat://{{space_id}} |
GET | /v1/spaces/{space_id}/members | chat:read | chat://{{space_id}} |
GET | /v1/spaces/{space_id}/members/{member_id} | chat:read | chat://{{space_id}} |
GET | /v1/spaces/{space_id}/messages | chat:read | chat://{{space_id}} |
POST | /v1/spaces/{space_id}/messages:search | chat:read | chat://{{space_id}} |
GET | /v1/spaces/{space_id}/messages/{message_id} | chat:read | chat://{{space_id}} |
GET | /v1/spaces:findDirectMessage | chat:read | — |
GET | /v1/spaces/{space_id}/messages/{message_id}/reactions | chat:read | chat://{{space_id}} |
POST | /v1/spaces/{space_id}/messages | chat:write | chat://{{space_id}} |
PATCH | /v1/spaces/{space_id}/messages/{message_id} | chat:write | chat://{{space_id}} |
DELETE | /v1/spaces/{space_id}/messages/{message_id} | chat:write | chat://{{space_id}} |
POST | /v1/spaces/{space_id}/messages/{message_id}/reactions | chat:write | chat://{{space_id}} |
DELETE | /v1/spaces/{space_id}/messages/{message_id}/reactions/{reaction_id} | chat:write | chat://{{space_id}} |
POST | /v1/spaces | chat:admin | — |
PATCH | /v1/spaces/{space_id} | chat:admin | chat://{{space_id}} |
DELETE | /v1/spaces/{space_id} | chat:admin | chat://{{space_id}} |
POST | /v1/spaces/{space_id}/members | chat:admin | chat://{{space_id}} |
DELETE | /v1/spaces/{space_id}/members/{member_id} | chat:admin | chat://{{space_id}} |
MCP tools
| Name | Scope | Description |
|---|---|---|
list_spaces | chat:read | List Chat spaces the authenticated user is a member of. |
get_space | chat:read | Get a single Chat space's metadata. |
list_members | chat:read | List memberships in a Chat space. |
get_member | chat:read | Get a single membership in a Chat space. |
list_messages | chat:read | List messages in a Chat space. |
search_messages | chat:read | Search messages the caller can see. Set space_id to "-" to search every space they are a member of. A POST that only reads. |
get_message | chat:read | Get a single message by ID. |
find_direct_message | chat:read | Find the direct-message space with a specific user. Pass name=users/{user} as a query parameter. |
list_reactions | chat:read | List reactions on a message. |
create_message | chat:write | Post a message to a Chat space. Pass {"text": "..."} or a cards payload. Reversible via delete_message. |
update_message | chat:write | Update a message you authored. Pass updateMask (e.g. "text") as a query parameter and the new fields in the body. |
delete_message | chat:write | Delete a message you authored. |
create_reaction | chat:write | Add an emoji reaction to a message. Pass {"emoji": {"unicode": "👍"}}. |
delete_reaction | chat:write | Remove a reaction you added from a message. |
create_space | chat:admin | Create a named Chat space. Requires a configured Chat app / appropriate caller rights. Pass the Space body (displayName, spaceType). |
update_space | chat:admin | Update a Chat space. Pass updateMask (e.g. "displayName") as a query parameter and the new fields in the body. |
delete_space | chat:admin | Delete a Chat space. Irreversible — requires space-manager rights. |
add_member | chat:admin | Add a member to a Chat space. Pass the Membership body (member.name = users/{user}). |
remove_member | chat:admin | Remove a member from a Chat space. |