Upgrade zoom lib from 2.3 to 2.13 - Fail to join the meeting

Hello support. I’m trying to update zoom libraries but I’m a bit confused.

I have a web platform write in php where access is granted only to registered user.
The administrator schedule a meeting and choose which users can partecipate.
So when the meeting have to start, administrator create the meeting and users can join it.

With the 2.3 libraries I used ZoomApi to create meeting and with zoom-meeting-2.3.0.js I create interface to join meeting for the users.

Class to create meeting using ZoomApi
class ZoomAccount extends Uncgits\ZoomApi\ZoomApiConfig {
            public function __construct() {
              $this->setApiKey(ZOOM_API_KEY);
              $this->setApiSecret(ZOOM_API_SECRET);
      
              // generate a token from Firebase library
              $zoom_meeting_token = $this->refreshToken();
            }
          }
          
          $meetingsClient = new \Uncgits\ZoomApi\Clients\Meetings;
          $adapter = new \Uncgits\ZoomApi\Adapters\Guzzle;
          $config = new ZoomAccount ;
          $api = new \Uncgits\ZoomApi\ZoomApi([
              'client' => $meetingsClient,
              'adapter' => $adapter,
              'config' => $config,
          ]);
            
          $params = "{\"topic\":\"Meeting title\",\"type\":2,\"start_time\":\"".date("c")."\",\"duration\":50,\"timezone\":\"Europe/Rome\",\"agenda\":\"\",\"settings\":{\"join_before_host\":true,\"alternative_hosts\":\"\",\"global_dial_in_countries\":[\"\"],\"registrants_email_notification\":true, \"waiting_room\" : 0}}";
          
          $result = $api->addParameters(json_decode($params, TRUE))->createMeeting(ZOOM_EMAIL_ACCOUNT);
          $zoom_data = $result->getContent();

For the client users, then I create signature with this function

function ZoomGenerateSignature($api_key, $api_secret, $meeting_number, $role){
	$time = time() * 1000 - 30000;//time in milliseconds (or close enough)
	$data = base64_encode($api_key . $meeting_number . $time . $role);
	$hash = hash_hmac('sha256', $data, $api_secret, true);
	$_sig = $api_key . "." . $meeting_number . "." . $time . "." . $role . "." . base64_encode($hash);
	
	//return signature, url safe base64 encoded
	return rtrim(strtr(base64_encode($_sig), '+/', '-_'), '=');
}

and then I call zoom with this js code

$(document).ready(function(){
          ZoomMtg.setZoomJSLib('https://source.zoom.us/2.3.0/lib', '/av');
          ZoomMtg.preLoadWasm();
          ZoomMtg.prepareJssdk();
          const meetConfig = {
          	apiKey: '<?PHP echo ZOOM_API_KEY;?>',
          	meetingNumber: '<?PHP echo $DATA_MEETING["ZoomMeetingID"];?>',
          	leaveUrl: '<?PHP echo SITE_URL."zoom_iframe.php?action=leave";?>',
          	userName: '<?PHP echo $USER_DATA["Surname"]." ".$USER_DATA["Name"];?>',
          	userEmail: '<?PHP echo $USER_DATA["Email"];?>',
          	passWord: '<?PHP echo $DATA_MEETING["ZoomMeetingPassword"];?>', // if required
          	role: 0 // 1 for host; 0 for attendee
          };
          
          ZoomMtg.init({
          	leaveUrl: meetConfig.leaveUrl,
          	isSupportAV: true,
          	success: function() {
          		ZoomMtg.join({
          			signature: '<?PHP echo $signature;?>',
          			apiKey: meetConfig.apiKey,
          			meetingNumber: meetConfig.meetingNumber,
          			userName: meetConfig.userName,
          			// password optional; set by Host
          			passWord: meetConfig.passWord,
          			error(res) { 
          				console.log(res) 
          			}
          		})	
          	}
          })
        });

Now. I want to use last libraries and I understand that I’ve have to use APP SDK.

I use this funcion to create the token

function getZoomAccessToken() {
    $key = ZOOM_API_SECRET;
    $payload = array(
        "iss" => ZOOM_API_KEY,
        'exp' => time() + 3600,
    );
    return JWT::encode($payload, $key);    
}

I use this function to create meeting

function createZoomMeeting($token) {
    $client = new Client([
        // Base URI is used with relative requests
        'base_uri' => 'https://api.zoom.us',
    ]);
 
    $response = $client->request('POST', '/v2/users/me/meetings', [
        "headers" => [
            "Authorization" => "Bearer " . $token
        ],
        'json' => [
            "topic" => "Test",
            "type" => 2,
            "start_time" => date("c"),
            "duration" => "30", // 30 mins
            "password" => "123456"
        ],
    ]);
 
    $data = json_decode($response->getBody(), TRUE);
   
    return $data;
}

I use this code for generate the signature

function ZoomGenerateSignature($api_key, $api_secret, $meeting_number, $role){
	$payload = array(
        "sdkKey" => $api_key,
        "mn" => $meeting_number,
        "role" => $role,
        "iat" => time(),
        "exp" => time() + 3600, //60 seconds as suggested
        "tokenExp" => time() + 3600,
    );
	
	return JWT::encode($payload, $api_secret, 'HS256');
}

And all it works until here. But I don’t understand the field "“sdkKey” in the signature payload. If I put here the SDK_CLIENT_ID I get “Signature si invalid”, but if I use the API_KEY it seems to work.

Then when I’ve to launch the client I use this code

<script>
  const client = ZoomMtgEmbedded.createClient();

  let meetingSDKElement = document.getElementById('meetingSDKElement');

  client.init({ zoomAppRoot: meetingSDKElement, language: 'en-US' });

  client.join({
    //sdkKey: sdkKey,
    sdkKey: '<?PHP echo ZOOM_SDK_CLIENT_ID;?>',
    signature: '<?PHP echo $signature;?>', // role in SDK signature needs to be 0
    meetingNumber: <?PHP echo $data["id"];?>,
    password: '<?PHP echo $data["password"];?>',
    userName: 'Mario Rossi'
  });

</script>

But I get this error message: “Fail to join the meeting.” (in the console log I’ve got this: Uncaught (in promise) {type: 'JOIN_MEETING_FAILED', reason: 'Fail to join the meeting.', errorCode: 200}) And I don’t know why.

Can anyone help me?

you have to migrate from JWT app to Meeting SDK app for authentification

here are some infos

and here a php implementation for creating the new signature

1 Like

Thanks Jurgen for your reply.

I update PHP code and I succesfully generate a meeting after Server to Server OAuth authentication.

Here the code:

//OAuth Autentication
$curl = curl_init("https://zoom.us/oauth/token");
curl_setopt($curl, CURLOPT_URL, "https://zoom.us/oauth/token");
curl_setopt($curl, CURLOPT_POST, true);
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);

$headers = array(
   "Authorization: Basic ".base64_encode(ZOOM_OAUTH_CLIENT_ID.":".ZOOM_OAUTH_CLIENT_SECRET),
   "Content-Type: application/x-www-form-urlencoded",
);
curl_setopt($curl, CURLOPT_HTTPHEADER, $headers);

$data = "grant_type=account_credentials&account_id=".ZOOM_OAUTH_ACCOUNT_ID;

curl_setopt($curl, CURLOPT_POSTFIELDS, $data);

$result = curl_exec($curl);
$result_zoom_oauth = json_decode($result, TRUE);
curl_close($curl);

And with Zoom API I create a meeting

//Meeting creation
$curl = curl_init("https://api.zoom.us/v2/users/me/meetings");
curl_setopt($curl, CURLOPT_URL, "https://api.zoom.us/v2/users/me/meetings");
curl_setopt($curl, CURLOPT_POST, true);
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);

$headers = array(
   "Authorization: Bearer ".$result_zoom_oauth["access_token"],
   "Content-Type: application/json"
);
curl_setopt($curl, CURLOPT_HTTPHEADER, $headers);

$data = '{"topic":"TEST","type":2,"start_time":"2023-06-12T15:59:00Z","duration":50,"timezone":"Europe/Rome","agenda":"","settings":{"join_before_host":true,"alternative_hosts":"","global_dial_in_countries":[""],"registrants_email_notification":true, "waiting_room" : 0}}';

curl_setopt($curl, CURLOPT_POSTFIELDS, $data);

$result = curl_exec($curl);
$result_zoom_meeting = json_decode($result, TRUE);
curl_close($curl);

The problem now is when I create client SDK to access the meeting. The error is:

{
    "type": "JOIN_MEETING_FAILED",
    "reason": "Signature is invalid.",
    "errorCode": 3712
}

The function that generate Signature is this

$payload = array(
        "sdkKey" => ZOOM_SDK_CLIENT_ID,
        "mn" => $result_zoom_meeting["id"],
        "role" => 0,
        "iat" => time(),
        "exp" => time() + 3600, //60 seconds as suggested
        "tokenExp" => time() + 3600,
    );
$signature = JWT::encode($payload, $api_secret, 'HS256');

I check the signature with https://jwt.io/ and it’s ok (“Signed Verified”).

I read a lot of topic about Signature error, but I cannot able to find what’s going on.
Can you halp me?

Thanks

//UPDATE
I try to use the meeting sdk sample at this address: GitHub - zoom/meetingsdk-web-sample: Zoom Meeting SDK web sample
I insert Meeting number and meeting password generated by code below and I obtain this error:

## Joining meeting timeout.

Signature is invalid.

Ok Solved.
I used a wrong SDK CLIENT ID (however it’s strange that no error advise about it).

I leave here all PHP code from Server to Server OAuth connection to Js SDK.

//OAuth Autentication
$curl = curl_init("https://zoom.us/oauth/token");
curl_setopt($curl, CURLOPT_URL, "https://zoom.us/oauth/token");
curl_setopt($curl, CURLOPT_POST, true);
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);

$headers = array(
   "Authorization: Basic ".base64_encode(ZOOM_OAUTH_CLIENT_ID.":".ZOOM_OAUTH_CLIENT_SECRET),
   "Content-Type: application/x-www-form-urlencoded",
);
curl_setopt($curl, CURLOPT_HTTPHEADER, $headers);

$data = "grant_type=account_credentials&account_id=".ZOOM_OAUTH_ACCOUNT_ID;

curl_setopt($curl, CURLOPT_POSTFIELDS, $data);

$result = curl_exec($curl);
$result_zoom_oauth = json_decode($result, TRUE);
curl_close($curl);

With Zoom API I create a meeting

//Meeting creation
$curl = curl_init("https://api.zoom.us/v2/users/me/meetings");
curl_setopt($curl, CURLOPT_URL, "https://api.zoom.us/v2/users/me/meetings");
curl_setopt($curl, CURLOPT_POST, true);
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);

$headers = array(
   "Authorization: Bearer ".$result_zoom_oauth["access_token"],
   "Content-Type: application/json"
);
curl_setopt($curl, CURLOPT_HTTPHEADER, $headers);

$data = '{"topic":"TEST","type":2,"start_time":"2023-06-12T15:59:00Z","duration":50,"timezone":"Europe/Rome","agenda":"","settings":{"join_before_host":true,"alternative_hosts":"","global_dial_in_countries":[""],"registrants_email_notification":true, "waiting_room" : 0}}';

curl_setopt($curl, CURLOPT_POSTFIELDS, $data);

$result = curl_exec($curl);
$result_zoom_meeting = json_decode($result, TRUE);
curl_close($curl);

The function that generate Signature is this

$payload = array(
        "sdkKey" => ZOOM_SDK_CLIENT_ID,
        "mn" => $result_zoom_meeting["id"],
        "role" => 0,
        "iat" => time() - 30,
        "exp" => time() + 3600, //60 seconds as suggested
        "tokenExp" => time() + 3600,
    );
$signature = JWT::encode($payload, $api_secret, 'HS256');

Finally js SDK

<script>
  const client = ZoomMtgEmbedded.createClient();

  let meetingSDKElement = document.getElementById('meetingSDKElement');

  client.init({ zoomAppRoot: meetingSDKElement, language: 'en-US' });

  client.join({
    sdkKey: '<?PHP echo ZOOM_SDK_CLIENT_ID;?>',
    signature: '<?PHP echo $signature ;?>', // role in SDK signature needs to be 0
    meetingNumber: <?PHP echo $result_zoom_meeting["id"];?>,
    password: '<?PHP echo $result_zoom_meeting["password"];?>',
    userName: 'My Name'
  });

</script>

use the “Meeting SDK app” and there Client-ID/Client Secret for the join meeting token

Update:
good that the problem is now solved, I would one small thing still change

  • iat: from “current time” to “current time - 30 seconds”

You’re right.

I update the code.

Thanks