Skip to content

fix(gchat): accept endpointUrl as a direct-webhook JWT audience#518

Open
mdnanocom wants to merge 1 commit into
vercel:mainfrom
mdnanocom:fix/gchat-endpoint-url-audience
Open

fix(gchat): accept endpointUrl as a direct-webhook JWT audience#518
mdnanocom wants to merge 1 commit into
vercel:mainfrom
mdnanocom:fix/gchat-endpoint-url-audience

Conversation

@mdnanocom
Copy link
Copy Markdown
Contributor

@mdnanocom mdnanocom commented May 18, 2026

Summary

When a Google Chat app's connection setting Authentication audience is set to HTTP endpoint URL (Google's recommended option for HTTP-hosted apps that aren't behind Cloud Run IAM, see Verify requests from Google Chat),
the bearer token Google sends is an OIDC ID token whose aud is the endpoint URL — not the GCP project number.
The adapter previously only verified against googleChatProjectNumber, so URL-audience tokens always failed with
401 Unauthorized and direct webhooks silently broke for any app configured this way.

This change makes the adapter verify direct-webhook JWTs against googleChatProjectNumber and/or endpointUrl, accepting either when both are configured (handy for multi-env setups that mix the two modes).
The constructor's fail-closed check accepts an explicit endpointUrl as a valid direct-webhook verifier alongside googleChatProjectNumber, pubsubAudience, and disableSignatureVerification.

Behavior

Config Direct-webhook aud accepted
googleChatProjectNumber only (current behavior) project number
endpointUrl only (new) endpoint URL
Both googleChatProjectNumber and endpointUrl (new) either
Neither, no pubsubAudience, no disableSignatureVerification constructor throws (unchanged)

Security note

Auto-detected endpoint URLs (the value handleWebhook falls back to from the incoming request.url when endpointUrl is not configured) are intentionally not promoted to verifier status. Treating an auto-detected URL as a valid audience would let any caller bypass verification by hitting the bot at a URL of their choice.
A new endpointUrlIsAudience flag captures whether the caller explicitly configured endpointUrl, and only that case enables URL-based verification. A regression test guards this.

Implementation

  • verifyBearerToken accepts string | string[] (OAuth2Client.verifyIdToken
    already supports both).
  • handleWebhook builds directAudiences = [projectNumber, explicitEndpointUrl].filter(Boolean) and passes a single string when only one verifier is configured (to preserve the prior call shape) or an array when both are.
  • Docs updated in apps/docs/content/adapters/official/google-chat.mdx: describe both authentication-audience modes and document endpointUrl as an accepted verifier.
  • Changeset: @chat-adapter/gchat: patch.

Test plan

  • pnpm --filter @chat-adapter/gchat test — 250/250 pass (5 new tests).
  • pnpm typecheck — 33/33 tasks pass.
  • pnpm check (Ultracite/Biome) clean.
  • pnpm konsistent clean.
  • Validated end-to-end against a real Google Chat app configured with
    "HTTP endpoint URL" as authentication audience: webhooks that previously
    401'd now verify and process correctly.

@mdnanocom mdnanocom requested a review from a team as a code owner May 18, 2026 08:52
@vercel
Copy link
Copy Markdown
Contributor

vercel Bot commented May 18, 2026

@mdnanocom is attempting to deploy a commit to the Vercel Team on Vercel.

A member of the Team first needs to authorize it.

@mdnanocom mdnanocom force-pushed the fix/gchat-endpoint-url-audience branch from b3aaa5d to 62ad0bb Compare May 18, 2026 09:00
When a Google Chat app is configured with **HTTP endpoint URL** as its
authentication audience (the recommended option for HTTP-hosted apps not
behind Cloud Run IAM), Google issues OIDC tokens whose `aud` is the
endpoint URL rather than the GCP project number. The adapter previously
only verified against `googleChatProjectNumber`, so URL-audience tokens
always 401'd.

Verify the bearer token against `googleChatProjectNumber` and/or an
explicitly-configured `endpointUrl`, accepting either when both are set.
The constructor's fail-closed check now also accepts an explicit
`endpointUrl` as a valid direct-webhook verifier. Auto-detected endpoint
URLs (populated from the request URL inside `handleWebhook`) are
intentionally NOT promoted to verifier status — that would let a caller
hitting the bot at any URL bypass verification.

Tests cover the URL-audience-only path, the both-audiences path, and
the auto-detected-URL-must-not-bypass-verification regression.

Co-authored-by: Cursor <cursoragent@cursor.com>
@mdnanocom mdnanocom force-pushed the fix/gchat-endpoint-url-audience branch from 62ad0bb to e01684a Compare May 18, 2026 09:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant