Getting User Meetings

Problem:

When I use my email, api key, and api secret, or my coworkers email, api key, and api secret, I am able to use a restful api to return meetings from users/{email}/meetings.

However if I use my coworkers email and my api values no meetings are returned.

Is there some association that I am unaware of that ties api values to work only with the email that is used to create the api key or secret?

Passes:

var jwt = Zoom.Token.Create(key_1, secret_1, null, 1);
var list = ActionHandeler.GetMeetings(email_1, jwt);

var jwt = Zoom.Token.Create(key_2, secret_2, null, 1);
var list = ActionHandeler.GetMeetings(email_2, jwt);

Fails:

var jwt = Zoom.Token.Create(key_1, secret_1, null, 1);
var list = ActionHandeler.GetMeetings(email_2, jwt);

Hey @vaulx.kranak,

When using the JWT Token, you should be able to get meetings for any Zoom User in your Zoom Account.

Are you using a library to do this?

What errors are you getting?

Thanks,
Tommy

@tommy
With The Pass I get the following as a response:

{StatusCode: 200, ReasonPhrase: 'OK', Version: 1.1, Content: System.Net.Http.StreamContent, Headers:
{
  Connection: keep-alive
  x-zm-trackingid: WEB_0a1a20b2f29b45410d0c2bac3309d6e2
  X-Content-Type-Options: nosniff
  Pragma: no-cache
  Strict-Transport-Security: max-age=31536000
  X-XSS-Protection: 1; mode=block
  Cache-Control: no-store, must-revalidate, no-cache
  Date: Fri, 15 Nov 2019 19:29:45 GMT
  Set-Cookie: cred=BB3592440B2D4F196CAFF41C82A2B7A9; Path=/; Secure; HttpOnly
  Server: ZOOM
  Content-Length: 632
  Content-Type: application/json; charset=UTF-8
  Expires: Thu, 01 Jan 1970 00:00:00 GMT
}}

With the fail i get The following as a response:

{StatusCode: 404, ReasonPhrase: 'Not Found', Version: 1.1, Content: System.Net.Http.StreamContent, Headers:{  Connection: keep-alive  x-zm-trackingid: WEB_435e09820d220f47e3503fe026509d70  X-Content-Type-Options: nosniff  Pragma: no-cache  X-RateLimit-Limit: 100000  X-RateLimit-Remaining: 98986  Cache-Control: no-store, no-transform, must-revalidate, no-cache  Date: Fri, 15 Nov 2019 19:25:41 GMT  Set-Cookie: cred=132C1A27AD32BC82AB852097A44AB1ED; Path=/; Secure; HttpOnly  Server: ZOOM  Content-Length: 94  Content-Type: application/json; charset=UTF-8  Expires: Thu, 01 Jan 1970 00:00:00 GMT}}

Here is the code I am using:

using System;
using System.IdentityModel.Tokens.Jwt;
using System.Text;
using Microsoft.IdentityModel.Tokens;

namespace Zoom
{
    public static class Token
    {
        public static string Create(
            string key,
            string secret,
            string audience,
            int daysValid)
        {
            var tokenHandler = new JwtSecurityTokenHandler();

            // Create JWToken
            var token = tokenHandler.CreateJwtSecurityToken(issuer: key,
                audience: audience,
                subject: null,
                notBefore: null,
                expires: DateTime.UtcNow.AddDays(daysValid),
                signingCredentials:
                new SigningCredentials(
                    new SymmetricSecurityKey(
                        Encoding.Default.GetBytes(secret)),
                    SecurityAlgorithms.HmacSha256Signature));

            return tokenHandler.WriteToken(token);
        }

        public static JwtSecurityToken Read(string token, string secret)
        {
            var tokenHandler = new JwtSecurityTokenHandler();
            var jwToken = tokenHandler.ReadJwtToken(token);
            jwToken.SigningKey = new SymmetricSecurityKey(Encoding.Default.GetBytes(secret));

            return jwToken;
        }
    }
}
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;

namespace Zoom
{
    public static class ActionHandeler
    {
        public static List<UCUI.Models.Meeting> GetMeetings( string email, string jwt )
        {
            var _meetings = new List<UCUI.Models.Meeting>( );
            try
            {
                var response = WebRequests.Get( $"users/{email}/meetings", jwt );
                if( string.IsNullOrEmpty( response ) ) return _meetings;
                dynamic responseObj = JObject.Parse( response );
                dynamic meetings = JArray.Parse( responseObj.meetings.ToString( ) );
                foreach( var meeting in meetings )
                {
                    DateTime.TryParse( meeting.start_time.ToString( ), out DateTime startTime );
                    DateTime endTime = startTime.AddMinutes( int.Parse( meeting.duration.ToString( ) ) );
                    //Log( $"GetMeetings: DateTime.Now: {DateTime.Now}. endTime: {endTime}" );
                    if( DateTime.Now < endTime )
                    {
                        //Log( $"Adding meeting" );
                        _meetings.Add( new UCUI.Models.Meeting( )
                        {
                            Subject = meeting.topic.ToString( ),
                            StartTime = startTime,
                            EndTime = endTime,
                            OnLineMeetingUrl = meeting.join_url.ToString( ),
                            Description = string.Empty,
                            Owner = "",
                            UniteUserId = "",
                            IsInProgress = default( bool )
                        } );
                    }
                }
            }
            catch( Exception ex )
            {
                // Log( $"GetMeetings - Exception: {ex}" );
            }
            return _meetings;
        }
    }
}
using System;

namespace UCUI.Models
{
    public class Meeting
    {
        public Meeting();

        public Guid Id { get; set; }
        public string Subject { get; set; }
        public DateTime StartTime { get; set; }
        public DateTime EndTime { get; set; }
        public string Description { get; set; }
        public string OnLineMeetingUrl { get; set; }
        public string Owner { get; set; }
        public string UniteUserId { get; set; }
        public bool IsInProgress { get; set; }
    }
}
using Intel.BCP.Utils.Services;
using Ninject;
using System;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading.Tasks;
using Zoom.Static;

namespace Zoom
{
    public static class WebRequests
    {
        public static HttpClient Client { get; set; } = null;

        public static string Get( string path, string jwt )
        {
            Task<string> response = null;
            try
            {
                response = GetRequest( new Uri( $"https://api.zoom.us/v2/{path}" ), jwt );
                response.Wait( );
            }
            catch( Exception ex )
            {
                NinjectConfig.Kernel.Get<ILoggingService>( ).DebugLog( $"Get - Exception: {ex}" );
                throw;
            }
            
            return response.Result;
        }

        private static async Task<string> GetRequest( Uri uri, string jwt, bool allowAutoRedirect = true)
        {
            string result = string.Empty;
            try
            {
                var response =  GetCall( uri, jwt ).Result;
                if( response.IsSuccessStatusCode ) 
                    result = await response.Content.ReadAsStringAsync( );
            }
            catch( WebException ex )
            {
                NinjectConfig.Kernel.Get<ILoggingService>( ).DebugLog( $"Get - Exception: {ex}" );
                throw;
            }
            return result;
        }

        private static async Task<HttpResponseMessage> GetCall( Uri uri,  string jwt)
        {
            try
            {
                ServicePointManager.SecurityProtocol |= SecurityProtocolType.Tls11 | SecurityProtocolType.Tls12;
                var client = GetClient( uri );              
                client.DefaultRequestHeaders.Accept.Clear( );
                client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue( "Bearer", jwt);
                client.DefaultRequestHeaders.Accept.Add( new MediaTypeWithQualityHeaderValue( "application/json" ) );
                client.DefaultRequestHeaders.Add( "User-Agent", "Mozilla" );
                return await client.GetAsync( uri );                
            }
            catch( WebException ex )
            {
                NinjectConfig.Kernel.Get<ILoggingService>( ).DebugLog( $"Get - Exception: {ex}" );
                throw;
            }
        }

        private static HttpClient GetClient(Uri uri)
        {
            if( Client == null )
                Client = new HttpClient { BaseAddress = uri, Timeout = TimeSpan.FromSeconds( 30 ) };
            return Client;
        }

    }
}

Thanks @vaulx.kranak for sharing your code.

I was able to successfully get another members meetings of my Zoom account that did not create the JWT.

Does your email_2 user belong to your Zoom account?

Please share the response body. You only shared the response headers ^

The responses body looks something like this for the 404 Not Found error message:

{
    "code": 1001,
    "message": "User X not exist or not belong to this account."
}

Thanks,
Tommy