Collaboration spaces Module
What collaboration-space provisioning does — and what it does not
- It triggers on project activation. A standard project entering the active state provisions a space with each enabled provider. There is no per-project opt-in flag — each provider's switch governs all standard projects.
- It keeps membership in sync as people join and leave the project, additively by default. Removing people from the space when they leave is a setting you turn on.
- Lifecycle on close depends on the provider. Microsoft Teams archives its team when the project closes (never deleted); Slack, Google Chat and the shared drive are not archived automatically (see Delete / unlink).
How Collaboration spaces works
Two independent paths lead to the same place. Outbound: Vaks itself creates the space through whichever provider you configured (Teams, Slack, Google Chat). Inbound: a tool that manages its own space (a Slack channel your team already owns, a Teams team provisioned outside Vaks, a Google space set up by another system) declares the link back. Both converge on the same record and the same panel on the project.
ProjectCollabSpace record, shown in the project's Collaboration spaces panel regardless of which path created it.Providers are not identical in what they support: archive on close today only applies to Microsoft Teams (Slack channels and Google Chat spaces are created but not automatically archived when a project closes); public/private visibility is a Teams and Slack concept that Google Chat spaces don't have. An externally declared space is never touched by these lifecycle rules — only Vaks-provisioned ones are.
Prerequisites
Microsoft Teams is the identity-heaviest of the three — the table below is specific to it. Slack and Google Chat are lighter (no Entra/SSO dependency at all) and each has its own short prerequisites list in its setup section further down.
| Side | What you need |
|---|---|
| Identity | SSO or SCIM live, mapping the external identifier to the Entra object ID, so project members carry an Entra identity. This is the hard prerequisite — see the warning above. |
| Microsoft Entra | A dedicated app registration — separate from your SSO app — with Graph application permissions and admin consent. Only a tenant administrator can grant those. |
| Vaks PM | org:manage to configure it. A licence that includes Teams provisioning. |
Step 1 — The Entra app registration
Create a new app registration in Microsoft Entra admin center → Identity → Applications → App registrations → New registration, single tenant, no redirect URI. It authenticates as itself with the client-credentials flow — no user is involved.
Under API permissions → Add a permission → Microsoft Graph → Application permissions, add:
Team.Create
TeamMember.ReadWrite.All
Channel.Create
TeamSettings.ReadWrite.All
Optionally add User.Read.All to help resolve identities. Then press Grant admin consent and confirm the status column reads Granted for each — application permissions do nothing until consented.
Collect the Directory (tenant) ID and Application (client) ID from Overview, and create a client secret under Certificates & secrets, copying its value at once.
Sites.Read.All to this same app for the simple model, or use the two-app model described there. You can add it now or come back later.
.cer under Certificates & secrets → Certificates, copy the thumbprint).
Step 2 — In Vaks PM
Open Admin → Integrations → Collaboration spaces, choose Add a provider → Microsoft Teams, and configure it:
| Field | What to put |
|---|---|
| Entra tenant ID | The tenant GUID from step 1. |
| Client ID (app registration) | The application GUID. |
| Client secret or certificate | The secret value, or the thumbprint + private key if using certificate authentication (see above). Both are encrypted at rest, never shown again — leave blank when editing to keep what is stored. A private key on file takes precedence over the secret. |
| Team name pattern | How teams are named, e.g. [PROJECT] - {{project.name}}. Variables: {{project.name}}, {{project.key}}, {{client.name}}, {{project.id}}. |
| Channels to create | A comma-separated list, e.g. Planning,Dev,Client. General is created by Teams automatically. |
| Team visibility | Private or public. It cannot be changed after the team is created. |
| Who to invite | Whether membership follows the project's directly assigned members, the members of teams assigned to the project, or both. |
| Remove on leaving the project | Off by default (additive). On, someone removed from the project is removed from the team — never the owner. |
| Archive on close | On by default. Archives the team when the project completes or is archived. |
| Enabled | The switch that lets a space be created on project activation. |
Step 3 — Test, then provision
Press Test on the provider card to confirm the credentials are accepted by Graph.
Team.Create passes the test and only fails at the first real provisioning. So after a green test, provision one project and watch it succeed before trusting the setup.
From here provisioning is automatic: activate a standard project and its team is created. Each project's Collaboration spaces panel also has manual buttons — Provision now, Re-sync, Archive — and shows the last few provisioning log entries with counts of members added and skipped.
Step 4 — SharePoint documents for agents and members (optional)
Each provisioned team has a SharePoint site behind it. This add-on lets AI agents working on the project — and project members who have access to the team — read those documents as live context: nothing is copied, and it is read-only. Members browse them under the SharePoint tab of the project's documents area; agents reach them through their working context.
It requires additional Graph permission on the app, in one of two models:
- Read-all (simpler): add
Sites.Read.Allto the provisioning app. One consent; Vaks PM restricts reads to the drives of provisioned teams. - Selected-sites (tighter): the provisioning app holds
Sites.FullControl.Alland grants a second, minimal reader appSites.Selectedaccess to each team's site as it is provisioned. Agent reads go through the reader app, which can only see the sites it was granted.
Then enable it in two places — org-wide under Integrations → Collaboration spaces (on the SharePoint card: Enable SharePoint context, plus the identity model and, for the selected model, the reader app's client ID and secret), and per project (SharePoint document context, which also requires the project to have agents enabled).
Step 1 — The Slack app
Slack has no identity prerequisite — it needs a bot token and nothing else. Go to api.slack.com/apps → Create New App → From scratch, name it (e.g. Vaks PM) and pick the workspace it should live in.
Under OAuth & Permissions → Scopes → Bot Token Scopes, add:
channels:manage
groups:write
users:read.email
channels:manage creates and manages public channels, groups:write does the same for private ones, and users:read.email is how a project member's email is resolved to a Slack account for invites. Add whichever of the first two match the channel visibility you plan to use — both if you are not sure yet.
Scroll up to OAuth Tokens and press Install to Workspace (a workspace admin must approve if you are not one). Copy the Bot User OAuth Token — it starts with xoxb-.
Step 2 — In Vaks PM
Open Admin → Integrations → Collaboration spaces, choose Add a provider → Slack, and configure it:
| Field | What to put |
|---|---|
| Bot token | The xoxb-… value from step 1. Encrypted at rest, never shown again — leave blank when editing to keep the stored token. |
| Channel name prefix | Prepended to the slugified project name, e.g. proj- → #proj-website-revamp. |
| Channel visibility | Private or public. Needs the matching scope from step 1. |
| Invite members | On by default. Invites project members to the channel, matched by email — best-effort, members without a matching Slack account are silently skipped. |
| Enabled | The switch that lets a channel be created on project activation. |
Press Test to confirm the token is accepted, then activate a standard project to see its channel created — or, for a project that is already active, use the Provision now tool right on the Slack card (paste the project id) instead of cycling its status.
Step 1 — Google Cloud & Workspace
Google Chat is the heaviest of the three to set up, because the Chat API has no app-only mode: creating a space and adding members always requires a real Workspace user, so a service account has to impersonate one via domain-wide delegation. Expect a Google Cloud admin and a Workspace admin to be involved.
- Enable the API. In a Google Cloud project (create one if needed), go to APIs & Services → Library, search Google Chat API, and enable it.
- Configure the Chat app — do not skip this. Enabling the API is not enough: open the Configuration tab of the Google Chat API (
console.cloud.google.com/apis/api/chat.googleapis.com/hangouts-chat) and fill in an app name, an avatar URL (any https image) and a description. Since Vaks only makes REST calls (never an interactive bot), leave interactive features off (otherwise Google demands a connection URL you don't have). Make the app visible to your domain. Without this, every Chat call returns "PERMISSION_DENIED — the resource doesn't exist", even with perfect delegation — it's the most common trap. - Create a service account. IAM & Admin → Service Accounts → Create Service Account. Note its email (
…@….iam.gserviceaccount.com). Two authentication modes follow: service account key (download a JSON key via Keys → Add Key → JSON) or Workload Identity, keyless (nothing to download — see the dedicated section next, recommended if your organization blocks service account key creation). - Turn on domain-wide delegation. Still on the service account's page, check Enable Google Workspace Domain-wide Delegation and note the Client ID (a long number, not the email).
- Authorize the scopes, in the Workspace admin console. In admin.google.com (a Workspace super admin is required here) → Security → API controls → Domain-wide delegation → Add new: paste the numeric Client ID, and these scopes:
https://www.googleapis.com/auth/chat.spaces,https://www.googleapis.com/auth/chat.memberships - Pick a user to impersonate. Any real, licensed Workspace mailbox works — it is the identity the service account borrows to act as a human. An operations or automation account is a common choice; it just cannot be the service account itself.
Keyless — Workload Identity Federation (common to all Google integrations)
Many security-conscious organizations enforce the Google org policy iam.disableServiceAccountKeyCreation, which forbids downloading a service account key. There, the key method above is a wall. Vaks PM then offers a second authentication mode, Workload Identity Federation (WIF), which needs no downloaded key — the path a serious Google customer will accept.
How it works: Vaks signs an OIDC token with its own key (Google only knows its public half, pasted into the pool config), Google exchanges it for a federated token, then signs the domain-wide delegation assertion server-side. The key Vaks holds grants no access on its own: access is governed by the WIF config and an IAM grant, both revocable by you at any time.
signJwt: also enable the IAM Service Account Credentials API (iamcredentials.googleapis.com) in the project, in addition to the API of the integration concerned (Chat or Drive). It's a common miss — the test fails until it's on.
--workload-identity-pool=vaks stays the same everywhere), then one provider per integration you run in WIF — each with its own issuer and JWKS.
| Integration | Issuer emitted by Vaks | Suggested provider name | Where to generate the key |
|---|---|---|---|
| Google Chat | …/federation/gchat | vaks-gchat | Google Chat card (Collaboration spaces) |
| Google Drive — shared drive provisioning | …/federation/gdrive-provisioning | vaks-gdrive-prov | Google Drive card (Collaboration spaces) |
| Google Drive — document reading | …/federation/gdrive | vaks-gdrive-read | Google Drive card (Document sources) |
This separation is deliberate: each integration has its own key, so you can rotate or revoke it without breaking the others. If you only run one integration in WIF, you obviously only have one provider to create.
Once per organization
- Target the project (the ID, as text — not the number):
gcloud config set project PROJECT_ID - Create the identity pool — once only, reused by every integration:
gcloud iam workload-identity-pools create vaks --location=global --display-name="Vaks PM"
Once per integration
Repeat this block for each row of the table above that you enable. Every command on this page uses the same placeholders:
| Placeholder | What to put in its place |
|---|---|
PROJECT_ID | The Google Cloud project's text ID (e.g. my-prod-project). |
PROJECT_NUMBER | The project number (digits only, e.g. 3110105436) — shown on the Cloud console home page. It is not the PROJECT_ID: the two commands below do not expect the same thing. |
SA_EMAIL | The service account email (…@….iam.gserviceaccount.com). |
PROVIDER_NAME | The provider name for this integration (the "Suggested provider name" column above). |
<ISSUER> | The issuer shown on the card after generating the key — copy-paste it, never retype it. |
- Keyless service account. The service account from Step 1 works — just don't create any JSON key. Domain-wide delegation (Client ID + scopes in Workspace admin) stays the one for the integration concerned.
- Generate the signing key in Vaks. In that integration's card, switch Authentication to Workload Identity (keyless), then Generate signing key. Vaks shows the issuer, the kid and a public JWKS — the private key stays encrypted in the database, never exposed.
Create the file in Cloud Shell. Between the two
EOFmarkers, paste the exact contents of the card's "Public JWKS" field — the whole{"keys":[ … ]}block, verbatim, unchanged. This is not something to invent or copy from an example: it is your public key, the one carrying thekidshown on the card. (Quoting the firstEOFstops the shell interpreting the content.)cat > jwks-PROVIDER_NAME.json <<'EOF' [[ PASTE HERE THE PUBLIC JWKS SHOWN ON THE CARD ]] EOFA JWKS holds public key material only — there is nothing secret to protect in this file. The
kidit carries must be the one shown on the card: that is what lets you verify later that Google holds the right version.Prefer an editor?
cloudshell edit jwks-PROVIDER_NAME.json. You can check the file is valid JSON withpython3 -m json.tool jwks-PROVIDER_NAME.json. - Create the OIDC provider (inline JWKS, flag
--jwk-json-path— not--jwks-json-filein recent gcloud):gcloud iam workload-identity-pools providers create-oidc PROVIDER_NAME \ --location=global --workload-identity-pool=vaks \ --issuer-uri="<ISSUER>" --jwk-json-path=jwks-PROVIDER_NAME.json \ --attribute-mapping="google.subject=assertion.sub" - Let the pool impersonate the service account. This binding lives at the pool level, not the provider level: it identifies the service account by its subject, regardless of which provider the token came through. So if you reuse the same service account across integrations, one binding is enough — there is nothing to replay. Only redo it if the integration uses a different service account.
gcloud iam service-accounts add-iam-policy-binding SA_EMAIL \ --role=roles/iam.serviceAccountTokenCreator \ --member="principal://iam.googleapis.com/projects/PROJECT_NUMBER/locations/global/workloadIdentityPools/vaks/subject/SA_EMAIL"Note:
SA_EMAILappears in two places — the target service account (right afteradd-iam-policy-binding) and the member'ssubject/. Replace both with the same address. - Fill in the audience in the card: the full resource name of the provider you just created — it ends with
PROVIDER_NAME, not with the pool name.//iam.googleapis.com/projects/PROJECT_NUMBER/locations/global/workloadIdentityPools/vaks/providers/PROVIDER_NAME
kid. As long as the JWKS held by Google carries the old one, Google cannot find the token's key and falls back to OIDC discovery — hence the misleading Parsing error for OIDC discovery document, which looks like a URL problem when it is really a stale key. Copy the JWKS shown on the card, then:
gcloud iam workload-identity-pools providers update-oidc PROVIDER_NAME \
--location=global --workload-identity-pool=vaks \
--jwk-json-path=jwks-PROVIDER_NAME.json
Then check the stored kid matches the card's:
gcloud iam workload-identity-pools providers describe PROVIDER_NAME \
--location=global --workload-identity-pool=vaks --format="value(oidc.jwksJson)"
https://www.googleapis.com/auth/chat.spaces,https://www.googleapis.com/auth/chat.memberships,https://www.googleapis.com/auth/drive
A dedicated service account per integration remains cleaner for least privilege (each identity carries only its own scopes) — but it then needs its own IAM binding.
iss of the emitted token doesn't match the issuer declared on that provider. One provider per issuer — always. gcloud is explicit about it: ALREADY_EXISTS: Requested entity already exists if you try to recreate a provider with the same name.
--jwk-json-path option): Google never calls back to your server to fetch it. WIF therefore works even in an isolated deployment.
Step 2 — In Vaks PM
Open Admin → Integrations → Collaboration spaces, choose Add a provider → Google Chat, and configure it. First pick the Authentication mode: Service account key (the private-key field appears) or Workload Identity (keyless) (the audience field and the key-generation panel appear instead):
| Field | What to put |
|---|---|
| Service account email | The …@….iam.gserviceaccount.com address from step 1. |
| Service account private key | Paste the whole downloaded JSON file, its raw private_key value, or an already-clean PEM — all three are accepted and normalized. Encrypted at rest, never shown again — leave blank when editing to keep the stored key. |
| Impersonated Workspace user | The email from step 1's last point. |
| Space name prefix | Prepended to the project name, e.g. proj- → proj-Website revamp. Unlike Slack, Google Chat space names are not slugified. |
| Invite members | On by default. Invites project members to the space, matched by email — best-effort, members without a Chat account are silently skipped. |
| Enabled | The switch that lets a space be created on project activation. |
Press Test — it exchanges the service-account assertion for a token and does a small, read-only Chat API call, confirming the whole delegation chain works before anything is created. Then activate a standard project to see its space appear.
Delete a space, or unlink
Two actions live on each tile of a project's Collaboration spaces section, and they must not be confused:
| Action | Effect | Available on |
|---|---|---|
| Archive | A real, reversible remote action: the team/channel becomes read-only, content is kept. | Microsoft Teams, Slack |
| Delete space | Destructive and permanent on Google's side. Chat: the space and its messages are gone (spaces.delete) — requires adding the chat.delete scope to the domain-wide delegation. Shared drive: the drive and the files it holds are deleted. Neither has an archive — this is the only remote cleanup. | Google Chat, Google Drive |
| Unlink | Local-only cleanup: removes the Vaks link with no remote call. The remote space keeps running. Use it when the space/team/drive was already deleted on the provider side and Archive/Re-sync fail with 404. The project goes back to "Provision now". | All four providers |
chat.delete scope? In the same place as the two initial Chat scopes: admin.google.com → Security → API controls → Domain-wide delegation. Edit the existing row for your Client ID (don't create a new entry) and replace the scope list with all three:
https://www.googleapis.com/auth/chat.spaces,https://www.googleapis.com/auth/chat.memberships,https://www.googleapis.com/auth/chat.delete
Workload Identity needs no other change for deletion — it's a delegation scope, not an IAM role. Until chat.delete is in the list, only deletion fails; creation keeps working.
Step 1 — Google Cloud & Workspace
Google Drive provisioning creates one shared drive per project — the Google equivalent of the SharePoint site that comes with a Teams team. As with Google Chat, a service account alone isn't enough: creating a shared drive requires a real Workspace user, so domain-wide impersonation is mandatory, whichever authentication mode you pick.
drive scope), whereas reading documents stays on drive.readonly. You can use the same service account for both, or dedicate one to each role — recommended if you want a hard guarantee that the read path cannot modify anything. For the read side, see Document sources.
- Enable the API. In your Google Cloud project, APIs & Services → Library → Google Drive API → enable.
- Create a service account. IAM & Admin → Service Accounts. Note its email (
…@….iam.gserviceaccount.com). Two authentication modes follow: service account key (Keys → Add Key → JSON) or Workload Identity, keyless. - Enable domain-wide delegation. On the service account page, tick Enable Google Workspace Domain-wide Delegation and note the Client ID (the long number, not the email).
- Authorise the scope, in the Workspace admin console. admin.google.com → Security → API controls → Domain-wide delegation → Add new: paste the numeric Client ID, and this scope:
This scope covers creating the drive and managing its members. It subsumes reading: a service account dedicated solely to reading documents only needshttps://www.googleapis.com/auth/drivedrive.readonly. - Pick a user to impersonate. Any real, licensed Workspace mailbox that is allowed to create shared drives. An operations account is a good fit.
Keyless mode (Workload Identity) for this integration
The principle and the pool are common to every Google integration — see Keyless — Workload Identity Federation for creating the pool (do that once). What follows is the block specific to Drive provisioning: it needs its own provider, because its issuer differs from Google Chat's.
- In the Google Drive card (Step 2 below), switch Authentication to Workload Identity (keyless), then Generate signing key. The issuer shown ends with
/federation/gdrive-provisioning; copy the JWKS intojwks-gdrive-prov.json. - Create the dedicated provider, inside the existing
vakspool:gcloud iam workload-identity-pools providers create-oidc vaks-gdrive-prov \ --location=global --workload-identity-pool=vaks \ --issuer-uri="https://YOUR-TENANT/federation/gdrive-provisioning" \ --jwk-json-path=jwks-gdrive-prov.json \ --attribute-mapping="google.subject=assertion.sub" - The impersonation grant lives on the pool: if the service account is already bound (for Google Chat, say), there is nothing to redo. Otherwise replay the
add-iam-policy-bindingfrom the shared section. - Paste this audience into the card's WIF pool audience field:
//iam.googleapis.com/projects/PROJECT_NUMBER/locations/global/workloadIdentityPools/vaks/providers/vaks-gdrive-prov
Step 2 — In Vaks PM
Open Admin → Integrations → Collaboration spaces, then Add a provider → Google Drive. Pick the Authentication mode first: the form adapts (private key, or pool audience + key generation panel).
| Field | What to put in |
|---|---|
| Service account email | The …@….iam.gserviceaccount.com address from step 1. |
| Service account private key (key mode) | The whole JSON file, just its private_key value, or a clean PEM — all three forms are accepted. Encrypted at rest, never shown again; leave blank when editing to keep the stored key. |
| WIF pool audience (keyless mode) | The full resource name of the WIF provider, as //iam.googleapis.com/projects/…/providers/…. |
| Impersonated Workspace user | The email from the last point of step 1. Required in both modes. |
| Drive name prefix | Prepended to the project name, e.g. Project → Project Website revamp. |
| Role for added members | Viewer (default, least privilege — enough for document context) or Contributor if the team needs to drop files in. |
| Add project members | On by default. Adds project members and members of assigned teams, matched by email — best-effort, failures are logged without blocking creation. |
| Enabled | The switch that lets a drive be created when a project goes active. |
Click Test — the assertion is exchanged for a token and a small read-only Drive call is made, which validates the whole delegation chain before anything is created. Then activate a standard project to watch its drive appear. Re-provisioning a project that already has one creates nothing: it only re-syncs the members.
Membership sync
Membership is kept current in three ways: when project members or managers change, when the composition of an assigned team changes, and by a safety-net job that periodically reconciles every provisioned active project. Sync is add-only unless Remove on leaving is on, and it never removes the owner. Only active members with an Entra identity are added; everyone else is skipped and counted in the log.
Externally declared spaces
Everything above is Vaks creating the space (Microsoft Teams). The other direction exists too: a tool that creates the space itself — a Slack channel, a Google space, or a Teams team managed outside Vaks — declares the link back so it shows on the project. This path needs no Teams provisioning license.
It relies on an organization key restricted to the collab:link scope alone — deliberately narrow: the key can read and change nothing else on the project. Create it from Admin → Integrations → Collaboration spaces: add the Declared spaces API provider, which mints a key scoped to collab:link (the secret is shown only once).
| Method & route | Purpose |
|---|---|
PUT /api/v1/projects/{projectId}/collab-spaces/{provider} | Declare or update a space. Idempotent on (project, provider). Refuses (409) to overwrite a Vaks-provisioned space. |
GET /api/v1/projects/{projectId}/collab-spaces | List the spaces linked to a project. |
DELETE /api/v1/projects/{projectId}/collab-spaces/{provider} | Remove a declared link (external links only). |
{provider} is a free-form label (slack, google, teams…). The PUT body:
{
"url": "https://…", // required, https only, ≤ 2048 chars
"externalId": "C0123ABCD", // optional — the space's id in the tool
"name": "Acme project" // optional — display label
}
project.activated event: your tool then knows when to create the space, and declares it back with the PUT above.
Troubleshooting
Provisioning is deliberately silent — it never breaks a project update — so the project's Collaboration spaces panel and its log are where you look. Server messages come back in French regardless of interface language, reproduced verbatim below.
| Symptom | Cause & fix |
|---|---|
| Provisioning fails, every member skipped | No member has an Entra identity, so no owner can be designated. SSO or SCIM must be live and mapping to the Entra object ID. This is the number-one cause. |
| Test is green but the first provision fails | The test does not check Teams permissions. Confirm all four Graph permissions are added and consented. |
| Le provisioning Microsoft Teams requiert une licence active. | The licence does not include Teams provisioning. Import one under Admin → Organization → License. |
| Configuration Graph incomplète (tenantId / clientId). | A required field is empty. Complete the provider card. |
| Échec d'obtention du token Graph: … | Tenant ID, client ID or secret is wrong, or the secret expired. The trailing text is Microsoft's own reason. |
| Provisioning indisponible sur un projet externe | Expected — external-ticket containers never get a team. |
| Members briefly fail to add after creation | Entra propagation delay after asynchronous team creation. The system retries; it resolves on its own. |
| Agents cannot read SharePoint | Check, in order: the org toggle, the project toggle, the project has agents enabled, the team is provisioned, and the grant shows as granted (use Verify access). |
| invalid_auth / not_authed (Slack) | The bot token is wrong, revoked, or the app was uninstalled from the workspace. Reinstall and paste the new token. |
| missing_scope (Slack) | A required Bot Token Scope is missing. Add it under OAuth & Permissions and reinstall the app — adding the scope alone is not enough. |
| name_taken (Slack) | A channel with that exact name already exists. Vaks PM does not guess its ID reliably, so this surfaces as a provisioning failure rather than silently adopting the existing channel. |
| Test fails with an authorization/consent error (Google Chat) | Domain-wide delegation is missing, has the wrong scopes, or has not propagated yet (rarely up to 24h). Double-check the Client ID and the two scopes in the Workspace admin console, then retry. |
| Clé privée invalide (Google Chat) | The pasted value is not recognized as a PKCS#8 private key. Vaks PM accepts the whole downloaded JSON, the raw private_key field, or a clean PEM — but it must ultimately contain a -----BEGIN PRIVATE KEY----- block. |
| PERMISSION_DENIED / "the resource doesn't exist" (Google Chat) | In order of frequency: (1) the Chat app is not configured — enabling the API isn't enough, you must fill the Configuration tab (name/avatar/description); (2) the Google Chat service is off for the org; (3) the impersonated user is not a real, active, licensed Workspace mailbox; (4) delegation hasn't propagated yet (retry after a few minutes). |
| Parsing error for OIDC discovery document: Unexpected end of stream : expected '{' (WIF) | The provider has no JWKS attached. With an inline JWKS, Google never fetches a discovery document: seeing this error means it is trying to read <issuer>/.well-known/openid-configuration — a route Vaks does not serve, hence the non-JSON reply. Typical causes: the provider was created without --jwk-json-path, or jwks-<provider>.json was empty/missing when create-oidc ran. Check with providers describe … --format="yaml(oidc)" that jwksJson is populated, then fix it with providers update-oidc … --jwk-json-path=jwks-<provider>.json. |
| Google STS: federated token missing (WIF) | The pool audience doesn't match, or the provider's attribute mapping is wrong. Check the Workload Identity pool audience (the exact //iam.googleapis.com/…/providers/…) and the declared issuer. |
| Google signJwt: signedJwt missing (WIF) | The roles/iam.serviceAccountTokenCreator grant is missing on the service account for the pool principal, or the IAM Service Account Credentials API isn't enabled. |
| Space deletion refused (Google Chat) | The chat.delete scope is not in the domain-wide delegation. Add it to the authorized scopes in Workspace admin — creation doesn't need it, only deletion. |
Related: document sources, the full guide to SharePoint and Google Drive as read context · SCIM and OIDC, prerequisites for the Entra identity link · AI connector for the agents that read SharePoint context · all integrations.