I am having some issues generating a signature that matches the x-sendbird-signature
header of a webhook request. The webhook documentation is being used as reference. Hopefully another set of eyes can help resolve this.
Our service is written in Python 3.8 (Django) and we are using Django Rest Framework (DRF) to process the webhook requests.
Note, some of the contents of this post were redacted and the examples provided are from a testing application using mocked data.
Beeceptor is being used to capture webhook requests from our test application. The following is an example of a webhook request.
Headers
{
"content-length": "1897",
"accept-encoding": "gzip, deflate",
"accept": "*/*",
"user-agent": "SendBird",
"x-signature": "16e3cb25eea601c7a211e5a52d91fa1a31afdd33cc2a2ac5f248ea45189efb26",
"content-type": "application/json",
"unicode-escaped": "true",
"x-sendbird-signature": "16e3cb25eea601c7a211e5a52d91fa1a31afdd33cc2a2ac5f248ea45189efb26"
}
Response
{"category":"group_channel:message_send","sender":{"nickname":"john","user_id":"f6dc589f-bcdb-48d5-8848-d8d60e4cba5b","profile_url":"https:\/\/storyplace-stage.s3.amazonaws.com\/asset\/image\/3047f6c2-2d6e-4ab3-a049-07aef47cd170\/pixel_john.jpg","metadata":{"full_name":"John Baker"}},"silent":false,"custom_type":"","mention_type":"users","mentioned_users":[],"app_id":"9BDF228D-8E32-43DE-88B7-969BA4CF4C40","sender_ip_addr":"99.231.147.50","members":[{"is_blocking_sender":false,"unread_message_count":7,"total_unread_message_count":7,"is_active":true,"is_online":true,"is_hidden":0,"channel_mention_count":0,"nickname":"john","is_blocked_by_sender":false,"user_id":"f6dc589f-bcdb-48d5-8848-d8d60e4cba5b","channel_unread_message_count":2,"state":"joined","push_enabled":true,"push_trigger_option":true,"profile_url":"https:\/\/storyplace-stage.s3.amazonaws.com\/asset\/image\/3047f6c2-2d6e-4ab3-a049-07aef47cd170\/pixel_john.jpg","metadata":{"full_name":"John Baker"}},{"is_blocking_sender":false,"unread_message_count":8,"total_unread_message_count":8,"is_active":true,"is_online":true,"is_hidden":0,"channel_mention_count":0,"nickname":"youngastro","is_blocked_by_sender":false,"user_id":"6349077a-833a-4eda-bffd-8fc1381ae6f9","channel_unread_message_count":3,"state":"joined","push_enabled":true,"push_trigger_option":true,"profile_url":"https:\/\/storyplace-stage.s3.amazonaws.com\/asset\/image\/b88eff9a-7d01-43ef-8397-d8384c0eab5c\/earth.gif","metadata":{"full_name":"Young Astronaut"}}],"type":"MESG","payload":{"custom_type":"","created_at":1600368905025,"translations":{},"message":"g\n","data":"","message_id":7470735177},"channel":{"is_distinct":true,"name":"a6571743-7093-4d44-8ef9-32130833a930","custom_type":"private","is_ephemeral":false,"channel_url":"eb823739-c8c8-42ee-a078-5b53e47283b7","is_public":false,"is_super":false,"data":"","is_discoverable":false},"sdk":"JavaScript"}
DRF provides conveniences for handling requests, such as properties for the request byte array and parsed data, so the following script was written to eliminate any characteristics of the framework.
Source
import hashlib
import hmac
x_sendbird_signature = "16e3cb25eea601c7a211e5a52d91fa1a31afdd33cc2a2ac5f248ea45189efb26"
payload = '{"category":"group_channel:message_send","sender":{"nickname":"john","user_id":"f6dc589f-bcdb-48d5-8848-d8d60e4cba5b","profile_url":"https:\/\/storyplace-stage.s3.amazonaws.com\/asset\/image\/3047f6c2-2d6e-4ab3-a049-07aef47cd170\/pixel_john.jpg","metadata":{"full_name":"John Baker"}},"silent":false,"custom_type":"","mention_type":"users","mentioned_users":[],"app_id":"9BDF228D-8E32-43DE-88B7-969BA4CF4C40","sender_ip_addr":"99.231.147.50","members":[{"is_blocking_sender":false,"unread_message_count":7,"total_unread_message_count":7,"is_active":true,"is_online":true,"is_hidden":0,"channel_mention_count":0,"nickname":"john","is_blocked_by_sender":false,"user_id":"f6dc589f-bcdb-48d5-8848-d8d60e4cba5b","channel_unread_message_count":2,"state":"joined","push_enabled":true,"push_trigger_option":true,"profile_url":"https:\/\/storyplace-stage.s3.amazonaws.com\/asset\/image\/3047f6c2-2d6e-4ab3-a049-07aef47cd170\/pixel_john.jpg","metadata":{"full_name":"John Baker"}},{"is_blocking_sender":false,"unread_message_count":8,"total_unread_message_count":8,"is_active":true,"is_online":true,"is_hidden":0,"channel_mention_count":0,"nickname":"youngastro","is_blocked_by_sender":false,"user_id":"6349077a-833a-4eda-bffd-8fc1381ae6f9","channel_unread_message_count":3,"state":"joined","push_enabled":true,"push_trigger_option":true,"profile_url":"https:\/\/storyplace-stage.s3.amazonaws.com\/asset\/image\/b88eff9a-7d01-43ef-8397-d8384c0eab5c\/earth.gif","metadata":{"full_name":"Young Astronaut"}}],"type":"MESG","payload":{"custom_type":"","created_at":1600368905025,"translations":{},"message":"g\n","data":"","message_id":7470735177},"channel":{"is_distinct":true,"name":"a6571743-7093-4d44-8ef9-32130833a930","custom_type":"private","is_ephemeral":false,"channel_url":"eb823739-c8c8-42ee-a078-5b53e47283b7","is_public":false,"is_super":false,"data":"","is_discoverable":false},"sdk":"JavaScript"}'
signature = hmac.new(
key=b'REDACTED',
msg=bytes(payload.encode('utf8')),
digestmod=hashlib.sha256,
).hexdigest()
print(signature, x_sendbird_signature)
assert (signature==x_sendbird_signature)
Output
/REDACTED/venv/bin/python "/REDACTED/sendbird_signature_test.py"
37d8f06f0ab8ee163c7d3f3810816f62d78b19fdcdcf5fab0ebadfaf8970bd2e 16e3cb25eea601c7a211e5a52d91fa1a31afdd33cc2a2ac5f248ea45189efb26
Traceback (most recent call last):
File "/REDACTED/sendbird_signature_test.py", line 14, in <module>
assert (signature==x_sendbird_signature)
AssertionError
The documentation is not clear which API token I should use. I have tried with the master and several others generated in the dashboard. It’s not clear which token the server uses to generated the hash. I don’t see any options to specify a token for the webhook. I even tried using the application ID.
I have tried several different ways of encoding the key and message, several different webhook requests, and different ways of interpreting the JSON response (such as encoding to Python dict, dumping back to a string, encoding, and casting to byte array). I also tried capturing a webhook example directly from our staging server, and another request interceptor service.
Any assistance would be greatly appreciated. Thank you.