Firebase cloud functions implementation of JWT generateSignature()

For those of you building on top of firebase, here is a quick Typescript implementation of the JWT generateSignature() method (see: https://marketplace.zoom.us/docs/sdk/native-sdks/web/essential/signature) as a firebase cloud function.

Note: I tried to do an implementation without loading express but for some reason I could not the cors to work properly.

/**
 * A quick TypeScript implementation of the Zoom JWT generate signature() method with `firebase cloud functions`. see:
 *    - https://marketplace.zoom.us/docs/sdk/native-sdks/web/essential/signature
 *    - https://firebase.google.com/docs/functions
 *    - https://firebase.google.com/docs/functions/typescript
 * 
 * Configuration Notes
 * 
 * for Zoom JWT Credentials: 
 *    for apiKey/apiSecret, see: https://marketplace.zoom.us/develop/create
 * install firebase cloud functions dependencies:
      ```
      // from firebase tools CLI
      cd ./functions
      npm install --save express cors
      ```
 * save Zoom JWT apiKey/Secret to firebase functions as environment vars
      ```
      // from firebase tools CLI
      firebase functions:config:set zoom.apikey="[apiKey]"
      firebase functions:config:set zoom.apisecret="[apiSecret]"
      ```
 * get the cloud function endpoint from the project page in the firebase console: 
 *    e.g. https://console.firebase.google.com/u/0/project/[project name]/functions/list
 *    (ends in "zoomSig")
 * 
 * test: 
      curl --location --request POST 'https://[firebase-cloud-functions-server]/zoomSig' \
      --header 'Content-Type: application/json' \
      --data-raw '{"meetingNumber": [123456789], "role": 0}'
 *      
 */


import * as functions from 'firebase-functions';
const cors = require('cors')
const express = require('express');
const app = express();


// // Start writing Firebase Functions
// // https://firebase.google.com/docs/functions/typescript

const crypto = require('crypto') // crypto comes with Node.js
const apiKey = functions.config().zoom.apikey;
const apiSecret = functions.config().zoom.apisecret;
const zoom_GenerateSignature = (meetingNumber:number, role:number):string => {
  // Prevent time sync issue between client signature generation and zoom 
  const timestamp = new Date().getTime() - 30000
  const msg = Buffer.from(apiKey + meetingNumber + timestamp + role).toString('base64')
  const hash = crypto.createHmac('sha256', apiSecret).update(msg).digest('base64')
  const signature = Buffer.from(`${apiKey}.${meetingNumber}.${timestamp}.${role}.${hash}`).toString('base64')
  return signature
}

app.use(cors({ origin: true }));
app.post('/', (request:any, response:any) => {
  const {meetingNumber, role} = request.body;
  // console.log( "zoomSig:", meetingNumber, role , "\nrequest.body=", request.body, "\nrequest", request)
  if (!meetingNumber || typeof role != "number") {
    response.status(400).send({ message: 'missing meetingNumber or role' });
    return 
  }
  const signature:string = zoom_GenerateSignature( meetingNumber, role );
  response.send({signature});
});

// // Expose Express API as a single Cloud Function:
export const zoomSig = functions.https.onRequest(app);








/**
 * this method works from `curl`, but does NOT work with correctly with CORS. don't know why.
 */
export const zoomSigWithoutExpress = functions.https.onRequest((request, response) => {
  if(request.method !== "POST"){
    response.status(400).send({ message: 'Please send a POST request'});
    return;
  }
  // cors is NOT working in this example
  cors({ origin: true })(request, response, () => {
    const {meetingNumber, role} = request.body;
    console.log( "zoomSig:", meetingNumber, role , "\nrequest.body=", JSON.stringify(request.body), "\nrequest", request)
    if (!meetingNumber || typeof role != "number") {
      response.status(400).send({ message: 'missing meetingNumber or role' });
      return 
    }
    const signature:string = zoom_GenerateSignature( meetingNumber, role );
    response.send({signature});
  });
}); 



Thanks for sharing @mlininkl!!