Facing error with verifying webhook signature

I am facing error while verifying webhook signature. Can anyone please help with this?
Sharing my django code here

class WebhookReciever(APIView):
    permission_classes = [AllowAny]

    def get(self, request):
        return HttpResponse(request.query_params.get("challenge", ""))

    def post(self, request):
        is_genuine = verify_signature(
            message=request.data,
            key=os.getenv("WEBHOOK_SECRET").encode("utf8"),
            signature=request.headers.get("X-Nylas-Signature"),
        )
        if not is_genuine:
            return HttpResponse("Signature verification failed!", status=401)

verify_signature function as follows

def verify_signature(message, key, signature):
    if isinstance(message, dict):
        message = json.dumps(message).encode("utf8")

    digest = hmac.new(key, msg=message, digestmod=hashlib.sha256).hexdigest()
    return hmac.compare_digest(digest, signature)

Hello @lsr2001 I’m not a django user…more Flask or Bottle…but I know Python :slight_smile: I’m comparing your code with mine…and the only thing that pop-up to me is this…

message = json.dumps(message).encode(“utf8”)

There’s no need to encode the message…

This is how I do it on Flask…

def verify_signature(message, key, signature):
    digest = hmac.new(key, msg=message, digestmod=hashlib.sha256).hexdigest()
    return hmac.compare_digest(digest, signature)

I tried this as well but it’s giving me error

in verify_signature
    digest = hmac.new(key, msg=message, digestmod=hashlib.sha256).hexdigest()
  File "/usr/lib/python3.8/hmac.py", line 153, in new
    return HMAC(key, msg, digestmod)
  File "/usr/lib/python3.8/hmac.py", line 88, in __init__
    self.update(msg)
  File "/usr/lib/python3.8/hmac.py", line 96, in update
    self.inner.update(msg)
TypeError: object supporting the buffer API required

Ok, that’s weird…what about this?

message.encode(“utf8”)

So you should pass…

def verify_signature(message, key, signature):
    message = message.encode("utf8")

    digest = hmac.new(key, msg=message, digestmod=hashlib.sha256).hexdigest()
    return hmac.compare_digest(digest, signature)

As I said…I’m not encoding my response…but I’m not json dumping anything either…

Here’s my GH repo if you want to take a look… python-read-webhooks/app.py at main · atejada/python-read-webhooks · GitHub

this didn’t work either

in verify_signature
    message = message.encode("utf8")
AttributeError: 'dict' object has no attribute 'encode'

Ok, clearly I’m not a Django guy…works things differently on Flask apparently…anyways…let’s tackle this from another angle…

Can you rotate your webhook secret? and update your os.getenv(“WEBHOOK_SECRET”)?

tried this as well but no help :cry:

Sorry this is not working as expected…had you tried printing out the values to see where the difference is getting created? I mean, I would print message, key and signature, also digest and try to figure out which one is not coming the right way…

I have validated Webhooks on Python, Ruby, Java, Kotlin and PHP…so there’s something sneaky going on here…we’re just not finding it…

This is what I’m receiving in request.data

{"specversion": "1.0", "type": "event.updated", "source": "/google/events/realtime", "id": "<id>", "time": 1724416325, "webhook_delivery_attempt": 4, "data": "<entire calendar event object>"}

The digest which I get using HMAC is completely different then X-Nylas-Signature
I had created the webhook secret from nylas v3 dashboard. Also, the Administration APIs are not working, giving me CORS error. When I tried to fetch webhooks from python sdk getting following error

webhooks = nylas.webhooks.list()
{'request_id': 'ed9da819-cc1d-4031-81bb-171110babc4d', 'data': None}
Traceback (most recent call last):
  File "<console>", line 1, in <module>
  File "/env/lib/python3.9/site-packages/nylas/resources/webhooks.py", line 45, in list
    return super().list(path="/v3/webhooks", response_type=Webhook)
  File "/env/lib/python3.9/site-packages/nylas/handler/api_resources.py", line 23, in list
    return ListResponse.from_dict(response_json, response_type)
  File "/env/lib/python3.9/site-packages/nylas/models/response.py", line 96, in from_dict
    for item in resp["data"]:
TypeError: 'NoneType' object is not iterable

please look into this… also if we can connect over call to resolve this issue works for me

Just for reference…here’s our Webhooks quickstart guide Webhooks Quickstart: create and read webhooks | Nylas Docs

Can you post the URL of your webhook service? Where are you hosting it? Or are you using something like Hookdeck?

Regarding nylas.webhooks.list() that seems to be a bug on the SDK…as it’s working just fine on Ruby…let me take a look and will submit a PR to fix it…

Also, here’s a blog post on using Pipedream to validate and read webhooks…How to set up Webhooks in Nylas API v3 using Pipedream | Nylas it’s more Node oriented, but easy to setup and might throw a light on what the problem is…

:man_facepalming:t2: The Python SDK is fine…it’s the code sample that is wrong…it’s showing list instead of list().

Which still makes me wonder why yours it’s not working…which Nylas version are you using??? I’m using 6.3.0

Can you also post a screenshot of your webhook on the dashboard? Like this…

I am using the same version nylas==6.3.0
How is the X-Nylas-Signature encoded? Like is there any other value used with WEBHOOK_SECRET something like this as it was in v2
Pass in your base 64 encoded<NYLAS_CLIENT SECRET>: with a colon.
Or
what specific part of request.data is encoded?
Sharing screenshot of webhook for reference. The urls are directly pointing to our app endpoints

I’m not sure about the internals on how the signature or secret are encoded…but to me your code is just fine…

Now, but caught my attention is that your webhooks are Disabled and the URL doesn’t look right… :thinking:

When you create a Webhook it creates a challenge that goes back to Nylas to validate that the webhooks is valid…so usually you call it like this…

myserver.com/webhook

It used to be Webhooks but that changed…

Adding my colleage @ram to see if he has some insights on this…

Yes challenge validation part is working fine. These webhooks got disabled because it kept on failing due to verify_signature returning False. Since then I didn’t reactivate it, captured all the values for key, message and signature and tried to debug but no help

Well, actually failing to verify_signature shouldn’t be a reason for a webhook to get disabled…you can actually skip verify_signature entirely and just read the webhooks, but it’s a good practice not to, because webhooks can be coming from an unknown source…have you checked the logs on the your V3 Dashboard?

logs say
got a non-200 response, 401 status code from the webhook endpoint, please make sure it's up and running
because i’ve returned that code if verify_signature fails

The exact problem lies here

digest = hmac.new(key, msg=message, digestmod=hashlib.sha256).hexdigest()
return hmac.compare_digest(digest, signature)

I just need to match digest with signature which is request.headers.get("X-Nylas-Signature")
So if I get what exactly is encoded here the issue would be resolved
cc: @Blag @ram

additional references
message=request.data
key=os.getenv("WEBHOOK_SECRET").encode("utf8")
signature=request.headers.get("X-Nylas-Signature")

Hi @lsr2001 - I’ve taken a look at verifying a webhook signature, let me know if this will helps: