Connect my app user to a zoom user, and receive webhooks when that user finishes a recording

I am new to Zoom Dev and I am having a hard time finding the write way to ask this question such that the search results on Google or this forum point me in the right direction. My apologies if this is obvious, and if there is a link that explains it, please let me know.

  1. Goal: I have many user accounts on my app, and they can connect their zoom account to my app’s account. I then want to be made aware of when a recording completes and download that recording if needed.
  2. I have: Set up Zoom OAuth App (type: user account) and validated my webhook using the “endpoint.url_validation” event and sha256 based response. They are working and the Zoom (dev/test) app was successfully added to my test user after the flow completed.
  3. I would like help understanding: how I can connect that user’s zoom account to my app’s account. I see I can pass a “state” param in the OAuth workflow to add my UID, but that seems very flimsy and corruptible. Furthermore, when I get a webhook’s response there isn’t any identifiers on it that say “this webooks is for this user so handle it accordingly.”

Am I even going about this the right way? i.e. maybe OAuth app isn’t what I need to do this, or maybe I have to build something into the Zoom client to accomplish this?

1 Like

@sshadmand Hope you will be fine.

For external Zoom Users’ data access, you need to first publish your OAuth App to Zoom Marketplace publically.

:point_up_2: And please share which webhook you are binding, where from you are trying to get a unique user id or identifying which users account belongs to this event.

By the way every webhook payload have account_id which is global unique id.

Thanks for the reply, Naeem.

Once published through, how will I be able to associate my app’s user ID to the once I get from Oauth. The pipeline doesn’t offer any sort of safe transport of my user’s UID through the auth process, that I can see.

You have account_id globally unique across your account users and that account_id is always in the webhook payload.

Still, if anything is not clear please ask. Thanks

Thanks Again, Naeem. There is still something I am not understanding, specifically around how I am able to safely connect my user to their corresponding Zoom user. Let me try a different approach, to show the step that is trying me up:

Given this snippet below:
I am able to confirm the person Oauthing with Zoom is a valid Zoom user. Let’s say my user’s ID is ‘12345’. In order to Oauth they leave my site using a redirect. Upon their return, while I can ASSUME it is the same user that started the request, I can’t be sure unless I can send ‘12345’ to the Zoom OAuth workflow. I saw that there is a “state” param that I can send in the initial Auth url get param, but since it is an open/public URL anyone can put any param in the state. That means I COULD incorrectly associate the Zoom auth with the wrong user on my side. So, I am looking for the proper way to be certain that ‘12345’ on my side is linked to the person that just OAuthed, to tie all the users together securly.

async (req, res) => {
  const ZOOM_GET_AUTHCODE = 'https://zoom.us/oauth/token?grant_type=authorization_code&code='
  const ZOOM_AUTH = 'https://zoom.us/oauth/authorize?response_type=code&client_id='
  const redirectUrl = ZOOM_REDIRECT_URL
  const authCode = req.query.code

   // ASSUMING I GRAB UID FROM STATE. 
  // BUT UNSAFE SINCE SATE IS OPEN URL PARAM AND CAN BE HIJACKED
  const uid = req.query.state 
  if (authCode) {
    let url = ZOOM_GET_AUTHCODE + authCode + '&redirect_uri=' + redirectUrl
    request.post(url, (error, response, body) => {
      body = JSON.parse(body || '{}')
      const accessToken = body.access_token
      const refreshToken = body.refresh_token
      if(accessToken) {
        request.get('https://api.zoom.us/v2/users/me', async (error, response, body) => {
          if (error) {
            console.log('API Response Error: ', error)
          } else {
            console.log(body)
            body = JSON.parse(body)
            
            // record that we are now integrated
            if (uid){
                  // ******************************
                 // connect ZOOM ID to UID , but how do I safely get UID in this step?
                // ****************************************
            }

            res.redirect(WEB_URL + '/settings/integrations#complete')
          }
        }).auth(null, null, true, body.access_token)
      }else {
        res.send('Something went wrong')
      }
    }).auth(ZOOM_CLIENT_ID, ZOOM_CLIENT_SECRET)
    return
  }
  // If no auth code is obtained, redirect to Zoom OAuth to do authentication
  res.redirect(ZOOM_AUTH + ZOOM_CLIENT_ID + '&redirect_uri=' + redirectUrl)
}

@sshadmand You have no need to set your site user id to state as a parameter.

For your scenario, you can easily and securely achieve that by making your redirect endpoint for authorized users only and disabling it for anonymous users on your server.

e.g

const verifyToken = (req, res, next) => {
  if (req.headers && req.headers.authorization && req.headers.authorization.split(' ')[0] === 'JWT') {
    jwt.verify(req.headers.authorization.split(' ')[1], process.env.API_SECRET, function (err, decode) {
      if (err) req.user = undefined;
      User.findOne({
          _id: decode.id
        })
        .exec((err, user) => {
          if (err) {
            res.status(500)
              .send({
                message: err
              });
          } else {
            req.user = user;
            next();
          }
        })
    });
  } else {
    req.user = undefined;
    next();
  }
}

Here is your redirect get request endpoint. This redirect is from Zoom once the user successfully authorized with the OAuth flow

app.get('api/zoom/redirect/', verifyToken,(req, res)=>
{
   // Here you have your site user from the session

});

Hope that answers. If still, anything is not clear share it here. Thanks

Thanks again. I will chew/think on that and let you know! Again, I appreciate you sticking with me :slight_smile:

1 Like