Can't validate event notification endpoint URL

Our web services are .NET using ServiceStack.

Hey Elisa. Yes, we are still having issues validating our endpoint (endpoint.url_validation) and would appreciate any assistance.

Our web service Ams/ZoomEventListenerOAuth is written in .NET C# and uses the ServiceStack framework to handle the http request/response. Questions:

  1. What is the proper JSON structure of the http response body? Is it
{"plainToken":"qgg8...FL8Aw", "encryptedToken":"426b6...d0200"}

or

{"message":{"plainToken":"qgg8...FL8Aw","encryptedToken":"426b6...d0200"},"status":200}
  1. Is our web service Ams/ZoomEventListenerOAuth generating the correct “encryptedToken” value of “426b6261eae90ccda682afd4444d021c00d5179396020793424cab15815d020” given the following input?

    • webhook secret token = “64vx8M8dRUeVXnKc8DX6uw”
    • plainToken = “qgg8vlvZRS6UYooatFL8Aw”
  2. Is there anything else Zoom is checking on the http response that is causing it to fail validation? (e.g. SSL certificate is not from a trusted publisher, or TLS 1.2+ is not being used, etc.)

Hi @ym.engineering
this is the proper JSON structure:

{“plainToken”:“qgg8…FL8Aw”, “encryptedToken”:“426b6…d0200”}

Thanks, @elisa.zoom , for verifying the JSON structure in question 1. Any insight on questions 2 and 3 above?

Hi, I just let you know the validations is working , using the below code in C#:

[FunctionName(“Events”)]
public static async Task Run(
[HttpTrigger(AuthorizationLevel.User, “post”)] HttpRequest req,
ILogger log)
{
log.LogInformation(“C# Zoom Webhook function processed a request.”);

        log.LogInformation("C# HTTP trigger function processed a request.");

        string hashed = "";
        string requestBody = await new StreamReader(req.Body).ReadToEndAsync();
        var data = JsonConvert.DeserializeObject<ZoomWebhookPayload>(requestBody);

        if (!(string.IsNullOrEmpty(data.Event)) && data.Event == "endpoint.url_validation")
        {
            var encoding = new System.Text.ASCIIEncoding();
            var sha256 = new System.Security.Cryptography.HMACSHA256();
            sha256.Key = encoding.GetBytes("My_TOKEN_SECRE_HERE");
            var hash = sha256.ComputeHash(encoding.GetBytes(data.payload.plainToken));
            hashed = ToHex(hash, false);
        }
        return new OkObjectResult(new
        {
            plainToken = data.payload.plainToken,
            encryptedToken = hashed
        });
    }
    
    private static string ToHex(byte[] bytes, bool upperCase)
    {
        StringBuilder result = new StringBuilder(bytes.Length * 2);
        for (int i = 0; i < bytes.Length; i++)
            result.Append(bytes[i].ToString(upperCase ? "X2" : "x2"));
        return result.ToString();
    }
    protected class ZoomWebhookPayload
    {
        public ZoomWebhookEventPayload payload { get; set; }
        public string Event { get; set; }
    }

    protected class ZoomWebhookEventPayload
    {
        public string plainToken { get; set; }

    }

Hi Naeem,

I want to get zoom webhook event in .net framework API. How to implement it?

Thanks,

@arkarthi2006 Hi, please get :point_up_2: the working C#.Net REST Endpoint code for Zoom Webhook Validation. Thanks

[quote=“Naeem Ahmed, post:9, topic:80380, username:freelancer.nak”]
ValidateUrlEvent
ValidateUrlEvent is custome event or c# library?

Thanks for this solution. This looks asp.net core. And it does work. However, I have asp.net mvc (.net framework) and i have tried many ways to return the data and it just does not work. The encryption and all don’t seem to be the issue. Anyone got it working with framework?

@arkarthi2006 It is a custom class with Payload of Zoom Webhook validation request.

Here is complete .Net Core (C#) code

ZoomController.cs

using Microsoft.AspNetCore.Mvc;
using System.Text;


[ApiController]
[Route("[controller]")]
public class ZoomController : ControllerBase
{

    [HttpGet(Name = "Test")]
    public ActionResult ZoomT()
    {
        return Ok();
    }

    [HttpPost(Name = "events")]
    public ActionResult Events(VerificationTokenEvent notification)
    {
        var encoding = new System.Text.ASCIIEncoding();

        var sha256 = new System.Security.Cryptography.HMACSHA256();
        sha256.Key = encoding.GetBytes("YAR-Yg7rXT-Ofcd9KrX1p3Q");

        var hash = sha256.ComputeHash(encoding.GetBytes(notification.payload.plainToken));

        var hashed=ToHex(hash,false);

        return Ok(
            new
            {
                plainToken = notification.payload.plainToken,
                encryptedToken = hashed
            }
        );
    }

    private static string ToHex(byte[] bytes, bool upperCase)
    {
        StringBuilder result = new StringBuilder(bytes.Length * 2);
        for (int i = 0; i < bytes.Length; i++)
            result.Append(bytes[i].ToString(upperCase ? "X2" : "x2"));
        return result.ToString();
    }
}

Here are custom Classes :point_down:

ZoomEvents.cs

public class VerificationTokenEvent
{
    public Payload payload { get; set; }

}

public class Payload
{
    public string plainToken { get; set; }
}
1 Like

Thanks for your reply. Your code was working fine in c#. But it was returned as below,

“URL validation failed. Try again later.”

My code is as below,

[HttpPost]
[Route(“ReceiveEvent”)]
public async Task ReceiveEvent(ZoomWebhookPayload notification)
{
try
{

	var encoding = new System.Text.ASCIIEncoding();
	var sha256 = new System.Security.Cryptography.HMACSHA256();
	sha256.Key = encoding.GetBytes("My Secret Token here");
	var hash = sha256.ComputeHash(encoding.GetBytes(notification.payload.plainToken));
	var hashed = ToHex(hash, false);
	var validationResponse = new
	{
		message = new
		{
			plainToken = notification.payload.plainToken,
			encryptedToken = hashed

		},

		status = 200
		};
		return Ok(validationResponse);				
	}
     catch (Exception ex)
        {
            return BadRequest(ex.Message);
        }
    }

public class ZoomWebhookPayload
{
public ZoomWebhookEventPayload payload { get; set; }
public string Event { get; set; }
}
public class ZoomWebhookEventPayload
{
public string plainToken { get; set; }

}

Anything I missed?

Hi guys facing the same issue i have put an error log on the very first line of code to check but its not even logging an error.
Please assist me
language I’m using is Laravel
I’m also attaching the code and error
here is the link to screenshots

I hope you’re doing well.

Please send a response as like :point_down:

      {
                plainToken = 'notification.payload.plainToken',
                encryptedToken = hashed
 }

How to return Ok() with anynonmous type in ActionResult

Like this :point_down:

public ActionResult Test(string code)
{
            return Ok(new
            {
                plainToken = "",
                encryptedToken = ""
            });
}

I get this error:
Cannot implicitly convert type ‘System.Web.Http.Results.OkNegotiatedContentResult<<anonymous type: string plainToken, string encryptedToken>>’ to ‘System.Web.Mvc.ActionResult’

try to return IHttpActionResult

1 Like

Hello, are you get solution in asp.net mvc (.net framework) ??
I need it too please

Hi @MrMustafa, here is the .Net Core code and it will help you to get validate the event notification URL.
If testing locally: After testing of values are get passed properly don’t use breakpoint, if it takes time to respond then URL get invalid.
Your URL should be serve with https not with http

var encoding = new System.Text.ASCIIEncoding();

                var sha256 = new System.Security.Cryptography.HMACSHA256();
                sha256.Key = encoding.GetBytes(ZoomSDKSecretToken);

                var hash = sha256.ComputeHash(encoding.GetBytes(root.payload.plainToken));
                var hashed = ToHex(hash, false);

                return Ok(
                    new
                    {
                        plainToken = root.payload.plainToken,
                        encryptedToken = hashed
                    }
                );



public string ToHex(byte[] bytes, bool upperCase)
        {
            StringBuilder result = new StringBuilder(bytes.Length * 2);
            for (int i = 0; i < bytes.Length; i++)
                result.Append(bytes[i].ToString(upperCase ? "X2" : "x2"));
            return result.ToString();
        }