API v2 /usrs/{userId}/token SECRET_TOKEN is not working and seems unclear what is required

API Endpoint(s) and/or Zoom API Event(s)

The issue pertains to api.zoom.us/v2/users/__USERID__/token)

Description

So I presently have the ZoomMtg.join working great, but in our situation we need a separate area where people with the Host role are able to connect. This is a zoom account (business I think) that has one account associated that is used by teachers. So really we just want to be able to get the ZAK for that account, and then allow them to start the meeting.

The issue that I am having is what exactly is YOUR_SECRET_TOKEN. Each of the situations that I tried (see below) keep returning Invalid access token. As I mentioned before, I can connect just fine to the meeting as a participant so it’s not the keys I don’t think.

The example curl is

<?php

$curl = curl_init();

curl_setopt_array($curl, [
  CURLOPT_URL => "https://api.zoom.us/v2/users/example@sample.org/token",
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_ENCODING => "",
  CURLOPT_MAXREDIRS => 10,
  CURLOPT_TIMEOUT => 30,
  CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
  CURLOPT_CUSTOMREQUEST => "GET",
  CURLOPT_HTTPHEADER => [
    "Authorization: Bearer YOUR_SECRET_TOKEN"
  ],
]);

$response = curl_exec($curl);
$err = curl_error($curl);

curl_close($curl);

if ($err) {
  echo "cURL Error #:" . $err;
} else {
  echo $response;
}

I tried to send:

  • A signature comprised of
    $token_payload = [
      'appKey' => $sdkKey,
      'sdkKey' => $sdkKey,
      'iat' => $iat,
      'exp' => $exp,
      'tokenExp' => $exp
    ];
  • Just the app secret
  • a base64_encode(sdkKey.':'.sdkSecret)

But none of these are working. Can someone let me know what exactly should be sent and in what format.

Hi @shaneonabike
YOUR_SECRET_TOKEN is the authorization method to access our API.
This secret token can be generated using a Server to Server Oauth app or General App..

Sorry your response is somewhat vague. I already setup the General App so that’s fine and working when joining meetings.

Is it the Client Secret token provided that matches to the Client ID? Or is there some other format that needs to be provided? Also does it need to be encrypted?

What I already tried (as mentioned above) is a few different solutions but none of them seem to work.

  • Just the secret token
  • Pay load (see above Signature comprised of comment)
  • Base64 encoded (client id and client secret)

Help :slight_smile:

Hey!
You have to generate an access token after authorizing your General app.
Once you authorize the app, you will receive an authorization code in your URL, you will grab that one to make a call to our OAuth endpoint to get an access token.

Here is a blog that might be helpful

Here is a sample app that might help you as a guide too:

HI there,

So are you indicating that I need to setup an oAuth? Also does that need to be publlished? I took a look at the Postman post but it’s not really related to my scenario. I’m ok in terms of having created a connection it’s just the SECRET TOKEN I’m really having an issue with.

What I did realize is that the Secret Token is not the Client Secret.

So in my General App I have a SECRET_TOKEN but when I tried with curl I still get Invalid Token. Then I tried it with a Server-to-Server oAuth app that I have and that also didn’t work (same message).

So my questions are:

  • I am going to assume that we use a Server-toServer oAuth because you can’t really create an oAuth app. Is that correct?
  • Does it need to be published or can I leave it in dev mode?
  • Do I need to have an oAuth or can I use a General App
  • When we attempt to access /users/{user_id}/token is it correct to use the email address as the user_id?

Also, so we are on the same page this would be the correct secret token right? (Sorry I tried to upload an image but I am not allowed). In an App(s)

  • General App : Features> Access
  • Server-to-Server App: Feature

It indicates

These features allow you to access Zoom data via APIs and Event Subscriptions. You can also allow your app to access Zoom’s APIs and receive real-time updates on events.

I don’t really want to use Postman honestly. I would rather keep this simple and happy to test with CURL. I just need to figure out why the secret token is not working. Is there any way you are able to check my account perhaps?

Also I feel like we are confusing the concept of oAuth access token and ZAK. I only want the ZAK token not an oAuth, which what I understand is that I don’t need the oAuth but I could be wrong.

Hi @shaneonabike
Thanks for getting back to me and I understand if you dont want to use Postman, Curl is totally fine!

I see where the confusion can be, the secret token that you can see in your General App: Features > Access or Server to Server App: Feature, is not to be used to make API requests, that Secret token is used to verify event notifications sent by Zoom (webhooks)

I do not know where you got that code snippet from but I want to agree with you that the fact that the token is called “Your_secret_token” can be very misleading, it should be called “access_token”, which is the token that you should be generating using either a Server to Server OAuth app or a General app and that token should be generated using your client ID and client Secret.

If you are developing an internal application and your goal is to keep it private, you can stick with the Server to Server Oauth app.

So the curl command to generate an access token using your Server to server oauth app would look like this:

curl -X POST https://zoom.us/oauth/token -d 'grant_type=account_credentials' -d 'account_id={accountID}' -H 'Host: zoom.us' -H 'Authorization: Basic Base64Encoded(clientId:clientSecret)'

The access token that you receive when making this request, should be the one you use to make the API call to get your ZAK token.

I hope this helps, if not please let me know and I am happy to jump in a call with you to talk about this further

Ahhh ok now I am starting to understand. I am still having one last issue in terms of obtaining the Token though.

For some reason when I perform this action in CURL it works and I receive a token, but doing the same thing in PHP it does not.

curl -X POST https://zoom.us/oauth/token -d 'grant_type=account_credentials' -d 'account_id=123' -H 'Authorization: Basic base64'

And php

 $postData = [
    'grant_type' => 'account_credentials',
    'account_id' => $accountId,
  ];
  $accessToken = base64_encode($clientId . ':' . $clientSecret);

$ch = curl_init($url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($postData));
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Basic ' . $accessToken,
]);

$response = curl_exec($ch);

if ($response === false) {
    echo 'Error CURL: ' . curl_error($ch);
} else {
    $responseData = json_decode($response, true);
echo 'Response '.var_export($responseData,true);
}

For good measure I compared the base64 that was generated and it matches, as well as the Account ID and Client ID/Secret. The only (gutt feeling) issue I have is that my Account ID has a dash in it. I’m worried somehow that is messed up?

BTW I did fine this Troubleshooting Server-to-Server OAuth access tokens - Zoom Developers but it didn’t really help in my case since CURL is working but PHP not. I find it super odd.

I found the problem. It would appear as though http_build_query actually adds a hidden new line in the output (or something else) that is tripping up your end. I had to build the post elements by hand in PHP and it’s working.

I am having issues now with teh signature needed to connect to a meeting though.

1 Like

Finally the issue is that you cannot use the Server-to-Server in order to access the API. Therefore, using a General App (like I was before) works!

For anyone else that comes across this I just wanted to summarize what was discovered and how to get it to work, but it’s not entirely clear.

  • Server-to-Server app is used to obtain tokens/Zaks
  • General App is used to connect to the API

Connecting as a host

  • Obtain a access access code via a Server-to-Server App
  • Generate a Zak token for a specific Zoom account attached to your account
  • Generate a signature to access the API

Notes

  • For the access token and zak token you need the Account ID, Client ID/Secret for the Server-to-Server AND the email account for the user who is connecting as a host (that is on the Zoom account you are connecting too)
  • http_build_query didn’t work for me on PHP 8.x in the case with Zoom. When I built the post data myself it was fine, but not with that call no idea why.
  • My curl call function isn’t anything special, but you have to remember that if there is postData then the header needs to be Authorization: Basic $accessToken, otherwise it should be Authorization: Bearer $accessToken
  // Obtain Initial access
  $dataRetrieved = [];
  $postData = [
    'grant_type' => 'account_credentials',
    'account_id' => $accountId,
  ];
  $token = '';
  $accessToken = base64_encode($clientId . ':' . $clientSecret);
  $url = 'https://zoom.us/oauth/token';
  $error = zoompage_make_curlCall($url, $accessToken, $dataRetrieved, $postData);

  // We found an error we need to return it
  if (!empty($error)) {
    return $error;
  }
  else {
    //echo "<br>Found ".var_export($dataRetrieved,true);
    // Process access token
    if (isset($dataRetrieved['access_token'])) {
      $token = $dataRetrieved['access_token'];
    }
    else {
      error_log("Could not properly complete {$url}: Received message " . var_export($dataRetrieved,true));
      return ['error' => 'No Access Token'];
    }
  }
//echo '<br>Token found '.var_export($token,true);

  // Obtain Zak
  $dataRetrieved = [];
  $postData = [];
  $url = "https://api.zoom.us/v2/users/{$accountEmail}/token?type=zak&ttl=7776000";
  $error = zoompage_make_curlCall($url, $token, $dataRetrieved, $postData);

  $zakToken = '';
  // We found an error we need to return it
 if (!empty($error)) {
    return $error;
  }
  else {
    if (isset($dataRetrieved['token'])) {
      $zakToken = $dataRetrieved['token'];
    }
    else {
      error_log("Could not properly complete {$url}: Received message " . var_export($dataRetrieved,true));
      return ['error' => 'No Access Token'];
    }
  }
  return $zakToken;

Hope this helps someone else.

1 Like

This topic was automatically closed 24 hours after the last reply. New replies are no longer allowed.