Webhook Signature Validation Issue Python

I am trying to verify the x-sendbird-signature in a webhook request using python. I have followed the documentation but signature I am generating is different from what I have received via webhook.

Webhook Header
POST /inspect/01f7mzz7ecbqjtb983h25g1rsy HTTP/1.1
requestinspector.com
Accept: /
X-Datadog-Trace-Id: 5337463762656296860
Cf-Request-Id: 0a8ba40da800005229e182e000000001
Content-Type: application/json
X-Datadog-Sampling-Priority: 1
User-Agent: SendBird
Content-Length: 374
Accept-Encoding: gzip
X-Sendbird-Signature: 433d4a0877c86f0faf53a980ce8666bb65b8f308af2cb799eab40bfc72c64c24
X-Signature: 433d4a0877c86f0faf53a980ce8666bb65b8f308af2cb799eab40bfc72c64c24
X-Datadog-Parent-Id: 7172508198697502604
Unicode-Escaped: true

Response Body:
{“category”:“open_channel:create”,“created_at”:1623129257000,“operators”:,“app_id”:“3CF528A5-523E-4E6B-9CA0-749360D6853E”,“channel”:{“is_super”:false,“is_distinct”:false,“name”:“testing-channel2”,“custom_type”:"",“is_ephemeral”:false,“data”:"",“channel_url”:“sendbird_open_channel_11554_8f2c89dae26ef3a19da04a06ec89a4cf98cda99f”,“is_discoverable”:false,“is_public”:false}}

Here is my code
body = {“category”:“open_channel:create”,“created_at”:1623129257000,“operators”:,“app_id”:“3CF528A5-523E-4E6B-9CA0-749360D6853E”,“channel”:{“is_super”:false,“is_distinct”:false,“name”:“testing-channel2”,“custom_type”:"",“is_ephemeral”:false,“data”:"",“channel_url”:“sendbird_open_channel_11554_8f2c89dae26ef3a19da04a06ec89a4cf98cda99f”,“is_discoverable”:false,“is_public”:false}}

def validate_signature(request: Request, body):
key = bytes(api_token, ‘utf-8’)
incoming_signature = request.headers.get(‘x-sendbird-signature’)
signature_to_compare = hmac.new(
key=key,
msg=bytes(str(body).encode(‘utf8’)),
digestmod=hashlib.sha256).hexdigest()

assert incoming_signature == signature_to_compare

incoming_signature = ‘433d4a0877c86f0faf53a980ce8666bb65b8f308af2cb799eab40bfc72c64c24’
signature_to_compare =‘6c9efbdd5db48c90634d6a08cb799f91d40d9bda372ea22602a62f65b0a1b824’

Key : b’19217214545b6393ae767de3454b4f9b2167eed1’

I tried various suggestions but with no help.

Thanks in Advance

Hi @Vijay,

Are you comparing the body as JSON or a string? It’s anticipated that you receive the body as a string, and do not convert it to JSON until after it’s been validated.

@Tyler Thanks for the quick response and help. Issue got resolved.

Glad to hear that! Was the issue that the body was being converted prior to signature validation?

Yeah so I was storing body as a json and while signature generation I was converting it back to string.

Makes sense! Glad it was able to be quickly fixed.

I am trying to verify the x-sendbird-signature in a webhook request using python. I have followed the documentation but signature I am generating is different from what I have received via webhook. This is the code that I have written for signature generation and comparison.

request_body = {
  'category': 'group_channel:message_read',
  'app_id': '',
  'channel': {
    'channel_url': '',
    'name': '',
    'data': '',
    'custom_type': 'SENDBIRD_DESK_CHANNEL_CUSTOM_TYPE',
    'is_distinct': False,
    'is_super': False,
    'is_public': False,
    'is_discoverable': False,
    'is_ephemeral': False,
    'type': 'group'
  },
  'read_updates': [
    {
      'user_id': '',
      'read_ts': 1705299189687,
      'channel_unread_message_count': 0,
      'total_unread_message_count': 2
    }
  ],
  'members': [
    {
      'user_id': '',
      'nickname': '',
      'profile_url': 'https://templates.joomla-monster.com/joomla30/jm-news-portal/components/com_djclassifieds/assets/images/default_profile.png',
      'metadata': {
        
      },
      'is_online': True,
      'push_enabled': True,
      'push_trigger_option': 'default',
      'unread_message_count': 2,
      'total_unread_message_count': 2,
      'channel_unread_message_count': 0,
      'channel_mention_count': 0,
      'state': 'joined',
      'is_active': True,
      'is_hidden': 0
    }
  ]
}

x_sendbird_signature = request.META.get('HTTP_X_SENDBIRD_SIGNATURE')
api_token = bytes(sendbird_api_key, 'utf‐8')
http_request_body = bytes(str(request_body), 'utf‐8')
signature_to_compare = hmac.new(
    key=api_token,
    msg=http_request_body,
    digestmod=hashlib.sha256).hexdigest()

x_sendbird_signature that we got from request c960b45a060aff9ce2e9dd1d37709aca6de358c135b2f7b7fb35539ee2ee60f0

signature_to_compare generated from code e5ca33322459ec24348a24a749f849ab5315ba2d7ee63c7fdfa469333cee0009

I tried with various changes but didn’t work for me.

Any suggestions?

Thanks In Advance.

We are using Python 3.12.1 and Django==4.2.7

@Aanal_Shah,

Welcome to the Sendbird community.

It looks like the issue is that you’re modifying the body by converting it to a string, then to a bytes object.

The body should be as bytes or a string. You’re converting it from, what I assume is JSON, to a string and then to bytes. I wrote a quick demo in flask that works.

import hashlib
import hmac

from flask import Flask, jsonify, request

api_token = b'my_applicaton_token';

app = Flask(__name__)
@app.route('/webhook', methods=['POST'])
def webhook_receiver():
  data = request.get_data()
  signature_header = request.headers.get('x-sendbird-signature')
  compare_signatures(signature_header, data)
  return jsonify({'message': 'Webhook received successfully'}), 200

def compare_signatures(signature, message):
  signature_to_compare = hmac.new(key=api_token, msg=message, digestmod=hashlib.sha256).hexdigest()
  print(signature_to_compare == signature)

if __name__ == '__main__':
    app.run(debug=True, port=8000)

Here is a video showing the outcome of the comparison

Thank you for your quick reply. I resolved my issue as per your guidance.