DEV Community

Siarhei Hlasouski
Siarhei Hlasouski

Posted on

Verifying Google Chat request in NodeJS

Google Chat includes a bearer token in the Authorization header of every HTTPS Request to a bot. For example:

Authorization: Bearer %JWT%
Content-Type: application/json
User-Agent: Google-Dynamite
Enter fullscreen mode Exit fullscreen mode

Decoded JWT token by

  "alg": "RS256",
  "kid": "424189bd9a18927f7ee924f1a100601f1524f441",
  "typ": "JWT"
Enter fullscreen mode Exit fullscreen mode


  "aud": "1234567890",
  "exp": 1629047990,
  "iat": 1629044390,
  "iss": ""
Enter fullscreen mode Exit fullscreen mode

All bearer tokens sent with requests from Google chat will have as the issuer, with the audience field specifying the target bot's project number from the Google API Console. For example, if the request is for a bot with the project number 1234567890, then the audience is 1234567890. [Verifying bot authenticity]

  1. Extract the KID from the header: 424189bd9a18927f7ee924f1a100601f1524f441
  2. Use the KID to find the matching public key in the JWKS (JSON Web Key Set) endpoint
  3. Verify JWT token using corresponded public key and passing audience and issuer options.

Complete solution


import { NextFunction, Request, Response, Router } from 'express';
import jwt from 'jsonwebtoken';
import { JwksClient } from 'jwks-rsa';

const GOOGLE_CHAT_PROJECT_NUMBER = '1234567890';

const jwksClient = new JwksClient({
  cache: true,

const router: Router = Router();'/google-chat/events', verificationRequestMiddleware(), async (req, res) => {
  // process google chat event

function verificationRequestMiddleware() {
  return async (request: Request, response: Response, next: NextFunction) => {
    const isVerified = await verifyRequest(request);

    if (!isVerified) {
      throw new UnauthorizedError('Authentication failed');

    return next();

async function verifyRequest(request: Request): Promise<boolean> {
  const prefix = 'Bearer ';
  const authHeader = request.header('Authorization') as string;
  const token = authHeader?.startsWith(prefix) ? authHeader.slice(prefix.length) : null;

  if (!token) {
    return false;

  return new Promise<boolean>((resolve, reject) => {
    const getKey = (header, callback) => {
      jwksClient.getSigningKey(header.kid, (err, key) => {
        const signingKey = key.getPublicKey();
        callback(null, signingKey);

        issuer: ''
      (err: any, decoded: any) => {
        if (err) {
        } else {
Enter fullscreen mode Exit fullscreen mode

Top comments (1)

julbrs profile image
Julien Bras

Thanks for that 🙏
I was struggling with this
There is no Node example 😅

I have been able to reuse your example for my project!