Web SDK Production Credentials – Works Locally but Fails on Server

Title: Web SDK Production Credentials – Works Locally but Fails on Server

Description: We’re facing an issue where our production SDK Key/Secret works fine in our local environment, but when used in our server, we get a “Join Meeting Failed” error. Despite updating the Domain Allow List in the App Credentials, the error persists.

Browser Console Error:

{

“type”: “JOIN_MEETING_FAILED”,

“reason”: “Fail to join the meeting.”,

“errorCode”: 200

}

Which Web Meeting SDK version?
3.13.2

Meeting SDK Code Snippets:

async function startMeeting(payload: { studentId: string; activityId: string }) {
  const { studentId, activityId } = payload;
  const teachersQueueData = await getActiveTeacherQueue();

  const existingTeacher = await TeacherService.getTeacherById(teachersQueueData[0]);

  const meetingProps: CreateMeeting = {
    agenda: `Ask for help - Meeting with ${existingTeacher?.firstName + ' ' + existingTeacher?.lastName}`,
    default_password: false,
    duration: 60,
    password: '123456',
    pre_schedule: false,
    type: 1,
  };

  try {
    const meeting = await createZoomMeeting(meetingProps);
    const meetingPayload = {
      meetingResponse: JSON.stringify(meeting),
      studentId,
      teacherId: existingTeacher.id,
      activityId,
      zak: meeting.zak,
    };

    const createdMeeting = await MeetingService.create(meetingPayload);
    const msdkSignature = await getMsdkSignature({ meetingNumber: meeting.id, role: '1' });
    const signatureAddedMeetingData = {
      createdMeetingId: createdMeeting?.id,
      ...JSON.parse(createdMeeting.meetingResponse),
      signature: msdkSignature.signature,
    };

    return {
      data: signatureAddedMeetingData,
      message: successResponseMessages.MEETING_CREATED_SUCCESSFULLY,
    };
  } catch (error) {
    throw new DbTransactionError('AskForHelp', {}, errorResponseMessages.MEETING_CREATION_UNSUCCESSFUL);
  }
}
const getMsdkSignature = asyncHandler(async (authCredentials) => {
  const iat = Math.round(new Date().getTime() / 1000) - 30;
  const exp = iat + 60 * 60 * 2;
  const ouathHeader = { alg: 'HS256', typ: 'JWT' };

  const ouathPayload = {
    sdkKey: process.env.ZOOM_CLIENT_ID,
    sdkSecret: process.env.ZOOM_CLIENT_SECRET,
    mn: authCredentials.meetingNumber,
    role: authCredentials.role,
    iat: iat,
    exp: exp,
    tokenExp: iat + 60 * 60 * 2,
  };

  const sHeader = JSON.stringify(ouathHeader);
  const sPayload = JSON.stringify(ouathPayload);
  const signature = KJUR.jws.JWS.sign('HS256', sHeader, sPayload, process.env.ZOOM_CLIENT_SECRET);

  process.env.ZOOM_JWT_TOKEN = signature;

  return { signature: signature };
});
const createZoomMeeting = async (createParams: CreateMeeting) => {
  const access_token = await getAccessToken();
  try {
    const response = await axios({
      method: 'POST',
      url: 'https://api.zoom.us/v2/users/me/meetings',
      headers: {
        Authorization: 'Bearer ' + `${access_token}`,
        'Content-Type': 'application/json',
      },
      data: createParams,
    });

    const zakResponse = await axios({
      method: 'GET',
      url: `https://api.zoom.us/v2/users/me/token?type=zak`,
      headers: {
        Authorization: 'Bearer ' + `${access_token}`,
        'Content-Type': 'application/json',
      },
    });

    const res = { ...response.data, zak: zakResponse.data.token };

    return res;
  } catch (error) {
    throw error;
  }
};
const getAccessToken = async () => {
  try {
    const base_64 = btoa(process.env.OAUTH_CLIENT_ID + ':' + process.env.OAUTH_CLIENT_SECRET);
    const response = await axios({
      method: 'POST',
      url: `https://zoom.us/oauth/token?grant_type=account_credentials&account_id=${process.env.OAUTH_ACCOUNT_ID}`,
      headers: {
        Authorization: 'Basic ' + `${base_64} `,
      },
    });

    return response.data.access_token;
  } catch (err) {
    throw err;
  }
};

To Reproduce:

Use production SDK Key/Secret in local environment → Works fine.

Use the same credentials in the server → Fails with error code 200.

Screenshots:

@Navalojanan can you get the web tracking id from the response header?

Tag me in your response

@chunsiong.zoom Sure!
Note: I’m trying to include screenshots, but it’s showing that I can’t upload images in my post.

 x-zm-trackingid:   v=2.0;clid=us05;rid=WEB_bedb080a2fa04a2c4ac224a51c7332e4

@Navalojanan I’ll PM you, the preliminary finding points to the signature being rejected.