ahavatammi
(Jeff Blumenthal)
December 13, 2022, 5:42pm
1
Hi,
I’m trying to verify my webhook vai c# and it is not working. Can I please get some assistance on my code to see where I’m going wrong?
Thank you.
log.Info("Recieved validation request");
JObject joPayload = JObject.Parse(payload);
var keySent = (string)joPayload.SelectToken("payload.plainToken");
log.Info("Key Sent: " + keySent);
string hashString = "";
var secret = keySent;
var message = keySent;
var encoding = new System.Text.ASCIIEncoding();
byte[] keyByte = encoding.GetBytes(secret);
byte[] messageBytes = encoding.GetBytes(message);
using (var hmacsha256 = new HMACSHA256(keyByte))
{
byte[] hashmessage = hmacsha256.ComputeHash(messageBytes);
hashString = Convert.ToBase64String(hashmessage);
}
StringBuilder sb = new StringBuilder();
foreach (char t in hashString)
{
//Note: X for upper, x for lower case letters
sb.Append(Convert.ToInt32(t).ToString("x2"));
}
response = "{\"plainToken\": \"" + keySent +"\",\"encryptedToken\": \"" + hashString + "\"}";
log.Info("Response: " + response);
Hi @ahavatammi
Thanks for reaching out!
Have you been able to troubleshoot this? or do you still need assistance here?
I will take a look into this in the meantime!
Best,
Elisa
ahavatammi
(Jeff Blumenthal)
December 20, 2022, 2:01pm
3
Hi Elisa,
Yes, can I please get some assistance? I have read many articles and still am unable to get this to work.
Thank you,
Jeff
Hi @ahavatammi
Here is a code snippet that might be helpful
const crypto = require('crypto')
const message = `v0:${request.headers['x-zm-request-timestamp']}:${JSON.stringify(request.body)}`
const hashForVerify = crypto.createHmac('sha256', ZOOM_WEBHOOK_SECRET_TOKEN).update(message).digest('hex')
const signature = `v0=${hashForVerify}`
if (request.headers['x-zm-signature'] === signature) {
// Webhook request came from Zoom
} else {
// Webhook request did not come from Zoom
}
And also the link to our sample app
console.log(req.headers)
// construct the message string
const message = `v0:${req.headers['x-zm-request-timestamp']}:${JSON.stringify(req.body)}`
const hashForVerify = crypto.createHmac('sha256', process.env.ZOOM_WEBHOOK_SECRET_TOKEN).update(message).digest('hex')
// hash the message string with your Webhook Secret Token and prepend the version semantic
const signature = `v0=${hashForVerify}`
// you validating the request came from Zoom https://marketplace.zoom.us/docs/api-reference/webhook-reference#notification-structure
if (req.headers['x-zm-signature'] === signature) {
// Zoom validating you control the webhook endpoint https://marketplace.zoom.us/docs/api-reference/webhook-reference#validate-webhook-endpoint
if(req.body.event === 'endpoint.url_validation') {
const hashForValidate = crypto.createHmac('sha256', process.env.ZOOM_WEBHOOK_SECRET_TOKEN).update(req.body.payload.plainToken).digest('hex')
response = {
message: {
plainToken: req.body.payload.plainToken,
encryptedToken: hashForValidate
Note how you would have to construct a message string first and then you would pass that message down to validate the URL
Hope this helps
ahavatammi
(Jeff Blumenthal)
December 21, 2022, 6:10pm
5
Hi Elisa,
Thank you for the code sample.
I was able to figure it out with the help of Free HMAC-SHA256 Online Generator Tool | Devglan . I used it to the create the return value and then compare it to my code.
Here is a working example for those coming behind me.
private static string UrlValidation(string sig, string tStamp, string payload, ILogger log)
{
log.LogInformation("Recieved validation request");
// get the plainToken
JObject joPayload = JObject.Parse(payload);
var plainToken = (string)joPayload.SelectToken("payload.plainToken");
log.LogInformation("plainToken: " + plainToken);
// secret token from the marketplace app
string secretToken = "<your token here>";
string message = plainToken;
// convert secret token to bytes
byte[] secretTokenByte = new UTF8Encoding().GetBytes(secretToken);
// convert the plainToken sent in to bytes
byte[] messageBytes = new UTF8Encoding().GetBytes(message);
// hash it
byte[] hashmessage = new HMACSHA256(secretTokenByte).ComputeHash(messageBytes);
// to lowercase hex
string hexMessage = String.Concat(Array.ConvertAll(hashmessage, x => x.ToString("x2")));
// create return message
string response = "{\"plainToken\": \"" + plainToken + "\",\"encryptedToken\": \"" + hexMessage + "\"}";
log.LogInformation("Response: " + response);
return response;
}
Hi @ahavatammi
Thanks for sharing your findings with the community!
Feel free to reach out back to us if you need anything else!
Best,
Elisa
I did try with UrlValidation function which is provided in the sample code, it returns the value which is not same as the x-zm-signature
value. Am I missing anything?
@ahavatammi , I’m not seeing plainToken in the payload object. How did you get that value?