fix(gchat): accept endpointUrl as a direct-webhook JWT audience#518
Merged
Conversation
Contributor
|
@mdnanocom is attempting to deploy a commit to the Vercel Team on Vercel. A member of the Team first needs to authorize it. |
62ad0bb to
e01684a
Compare
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>
Ensure URL-audience Google Chat webhooks come from the expected Chat service identities, and document endpointUrl setup in the package README. Co-authored-by: Cursor <cursoragent@cursor.com>
Auto-detect the public endpoint URL from incoming requests so simple deployments don't need to hardcode it, but keep the inferred value strictly out of the direct-webhook JWT audience set so a spoofed Host header can't influence verification. Co-authored-by: Cursor <cursoragent@cursor.com>
…nfer endpoint URL only after verification Two fixes to the direct-webhook verification introduced in this PR: 1. Project-number-audience tokens are self-signed JWTs from chat@system.gserviceaccount.com, not OIDC ID tokens. verifyIdToken can never validate them: it checks Google's OIDC federated certs and only accepts accounts.google.com issuers, so the previous iss claim check was unreachable and real project-number tokens always failed. Verify them per Google's reference implementation instead: verifySignedJwtWithCertsAsync against the Chat service account's X.509 certs (cached 1h) with issuer chat@system.gserviceaccount.com. Endpoint-URL tokens keep the OIDC verifyIdToken path plus email/email_verified claim checks. When both verifiers are configured, either token type is accepted. 2. Move button-click endpoint URL inference after successful verification. Previously an unauthenticated request (401) could still poison inferredEndpointUrl via a spoofed Host header, first-write-wins, and the poisoned URL flowed into every outbound card's action routing. Adds regression tests for both; updates docs, README, and the changeset.
0d93d95 to
923260c
Compare
bensabic
approved these changes
Jul 15, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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
audis the endpoint URL — not the GCP project number.The adapter previously only verified against
googleChatProjectNumber, so URL-audience tokens always failed with401 Unauthorizedand direct webhooks silently broke for any app configured this way.This change makes the adapter verify direct-webhook JWTs against
googleChatProjectNumberand/orendpointUrl, 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
endpointUrlas a valid direct-webhook verifier alongsidegoogleChatProjectNumber,pubsubAudience, anddisableSignatureVerification.Behavior
audacceptedgoogleChatProjectNumberonly (current behavior)endpointUrlonly (new)googleChatProjectNumberandendpointUrl(new)pubsubAudience, nodisableSignatureVerificationSecurity note
Auto-detected endpoint URLs (the value
handleWebhookfalls back to from the incomingrequest.urlwhenendpointUrlis 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
endpointUrlIsAudienceflag captures whether the caller explicitly configuredendpointUrl, and only that case enables URL-based verification. A regression test guards this.Implementation
verifyBearerTokenacceptsstring | string[](OAuth2Client.verifyIdTokenalready supports both).
handleWebhookbuildsdirectAudiences = [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.apps/docs/content/adapters/official/google-chat.mdx: describe both authentication-audience modes and documentendpointUrlas an accepted verifier.@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 konsistentclean."HTTP endpoint URL" as authentication audience: webhooks that previously
401'd now verify and process correctly.