Google Pub/Sub
OIDC JWT verified against Google's JWKS — with the push envelope unwrapped so events arrive as events.
Google Cloud Pub/Sub push
authenticates with an OIDC JWT in Authorization: Bearer, verified against
Google’s rotating JWKS (scheme family 5) with
issuer, audience, expiry, and service-account checks. Because Gmail push,
Google Play RTDN, and Workspace Events all deliver through Pub/Sub, this one
provider covers them all.
import { createWebhookHandler } from 'webhooks-sdk'
import { googlePubSub } from 'webhooks-sdk/google-pubsub'
const handler = createWebhookHandler({
provider: googlePubSub({
audience: 'https://example.com/webhooks/pubsub',
serviceAccountEmail: '[email protected]',
}),
on: {
message: async (event) => {
const notification = event.payload.message.json()
await handle(notification)
},
},
})
export const POST = handler.fetch
Options
| Option | Type | Default | |
|---|---|---|---|
audience |
string | string[] |
— (required) | The aud claim configured on the push subscription. |
serviceAccountEmail |
string | string[] |
— (required) | The invoker service account(s) allowed to call you. |
keys |
Jwk[] | KeySet |
Google’s JWKS, cached | Pin keys, or plug in your own cache. |
fetch |
typeof fetch |
global fetch |
Override for proxies or tests. |
tolerance |
number |
300 |
Clock skew allowed on exp/iat, in seconds. |
eventType |
string | ((payload) => string | undefined) |
'message' |
Derive the event name — a string names a message attribute. |
The envelope is unwrapped for you
Pub/Sub wraps every message in a push envelope with base64-encoded data.
The provider unwraps it: event.payload.message carries the decoded bytes
with text() and json() accessors, plus messageId, publishTime,
attributes, and orderingKey. A Gmail or Play event reaches your handler
as an event, not as an envelope.
event.id— the Pub/SubmessageId, stable across redelivery, so idempotency works out of the box.event.timestamp— the message’spublishTime.event.type—'message'by default. Route on an attribute instead witheventType: 'type', or derive:eventType: (p) => p.message.attributes.kind.
No handshake, but a deadline
Pub/Sub sends no setup challenge — it just expects a 2xx before the subscription’s ack deadline, and redelivers otherwise. Keep handlers fast; if the work is slow, enqueue it and return.
Key caching
The provider fetches Google’s JWKS once and caches it — honoring
Cache-Control, refreshing when an unknown key id arrives (rate-limited),
and falling back to the last good keys if a refresh fails. When Google is
unreachable and nothing is cached, verification fails with
key_unavailable (a 500) — deliberately not invalid_signature,
because it’s an outage, not an attack.
Pass keys to pin JWKs (great for tests) or to supply your own KeySet
backed by shared storage.
Standalone & testing
import {
verifyGooglePubSubWebhook, // (raw, options) — throws on failure
parseGooglePubSubWebhook, // (raw, { eventType? }) — the envelope
signGooglePubSubWebhook, // (privateKey, options) — the Authorization header, for tests
} from 'webhooks-sdk/google-pubsub'
Prefer the provider over verifyGooglePubSubWebhook in long-running
processes — the standalone function builds a fresh key set (and JWKS fetch)
per call unless you pass pinned keys. See
Testing for generating a test key
pair.