How to validate Google Bearer Token?

100 Views Asked by At

I have a chat app in Google Workspace that uses a Google Cloud Function as its back-end. The cloud function can currently be invoked without authentication. It has to be this way because the credentials sent by chat are not compatible with IAM policies. So, I have to verify authentication by extracting and validating the bearer token sent with the request. My issue is that whenever I attempt to verify the token, I get this error:

No pem found for envelope {"alg":"RS256","kid":"e8196fb03ecdc8f203075ae13d9fbaf98d2dac3d","typ":"JWT"}

This is what my code looks like:


const googleapis = require("googleapis");
const { google } = googleapis;

function getToken(bearerToken) {
  return bearerToken.split(" ")[1];
}

async function verifyIdToken(token, aud) {
  const client = new google.auth.OAuth2();
  const ticket = await client.verifyIdToken({
    idToken: token,
    audience: aud,
  });
  return ticket.getPayload();
}

exports.helloChat = async function helloChat(req, res){

  const token  = getToken(req.headers.authorization)
  const payload = await verifyIdToken(token)

}

Here is a tutorial that shows all of the steps I have taken. It does not include validating the token in cloud function.

2

There are 2 best solutions below

3
John Hanley On

An OIDC Identity Token consists of three base64 parts concatenated by periods:

token = base64(header) + '.' + base64(payload) + '.' + base64(signature)

This error is displaying the base64 decoded header:

No pem found for envelope: {"alg":"RS256","kid":..

Your code works correctly for me. Verify that this line of code is returning a valid OIDC Identity Token:

const token  = getToken(req.headers.authorization)

The first few characters of the token will start with ey. Your example header starts with {"alg" which when base64 encoded starts with eyJhbGci.

You can have Google verify an identity token using this command. Replace ID_TOKEN with the OIDC Identity Token. The endpoint also works in the web browser.

curl https://www.googleapis.com/oauth2/v3/tokeninfo?id_token=ID_TOKEN
0
prismo On

I was able to resolve this thanks to @John and this answer

import { decode, verify } from "jsonwebtoken";

async function getGooglePublicKeys() {
  const response = await fetch(
    "https://www.googleapis.com/service_accounts/v1/metadata/x509/[email protected]"
  );
  const keys = await response.json();
  return keys as { [key: string]: string };
}

async function main(){
  const googlePublicKeys = await getGooglePublicKeys();
  
  const decodedToken = decode(token, { complete: true });
  const header = jwt?.header;
  const kid = header?.kid ?? "";

  if (!googlePublicKeys[kid]) {
    return;
  }
  
  const publicKey = googlePublicKeys[kid];

  try {
    const payload = verify(token, publicKey);
    console.log(payload);
  } catch (err) {
    console.error(err);
  }
}