I’m trying to write a event subscriber in Python. everything works fine except that when my webhook responder gets the
webhook_callback_verification
challenge from Twitch my response to that is never accepted. (judged by the fact that even after I seem to respond exactly as documended twitch keeps on retrying the came challenge twice before bailing out)
so I send my webhook subscription:
def create_webhook_subscription(self, streamer_id, webhook_callback, secret):
if not self.authenticate():
raise ("Authentication failed")
webhook_payload = dict()
webhook_payload['type'] = "channel.follow"
webhook_payload['version'] = "1"
webhook_payload['condition'] = {"broadcaster_user_id": streamer_id}
webhook_payload['transport'] = {"method": "webhook", "callback": webhook_callback, "secret": secret}
print (json.dumps(webhook_payload))
my_headers = self.headers
my_headers['Content-Type'] = "application/json"
response = requests.post("https://api.twitch.tv/helix/eventsub/subscriptions", headers=my_headers, data=json.dumps(webhook_payload))
print (response)
print (response.text)
and I get 202 response and response payload:
{"total":1,"data":[{"id":"22c85499-c9eb-4f9a-9622-e4f03c7d3bdf","status":"webhook_callback_verification_failed","type":"channel.follow","version":"1","condition":{"broadcaster_user_id":"211177006"},"created_at":"2021-04-19T09:23:33.195462943Z","transport":{"method":"webhook","callback":"https://example.com/twitch/webhook/211177006"},"cost":0}],"limit":10000,"max_total_cost":10000,"total_cost":0,"pagination":{}}
withing few seconds form the request my webserver part gets the challenge GET:
Host: example.com
User-Agent: Go-http-client/1.1
Connection: close
Content-Length: 399
Content-Type: application/json
Twitch-Eventsub-Message-Id: ba4f85cc-9a16-414f-98da-e7988c8eda07
Twitch-Eventsub-Message-Retry: 0
Twitch-Eventsub-Message-Signature: sha256=19c1fb8d6bda746da9ea7879a2a63db745af3d1f5d5f0e3d27f2e6e39d926819
Twitch-Eventsub-Message-Timestamp: 2021-04-19T09:40:25.260601505Z
Twitch-Eventsub-Message-Type: webhook_callback_verification
Twitch-Eventsub-Subscription-Is-Batching-Enabled: false
Twitch-Eventsub-Subscription-Type: channel.follow
Twitch-Eventsub-Subscription-Version: 1
Accept-Encoding: gzip
content_length 399
{'subscription': {'id': '9259a682-9003-4605-ba73-e3f22c4f9089', 'status': 'webhook_callback_verification_pending', 'type': 'channel.follow', 'version': '1', 'condition': {'broadcaster_user_id': '211177006'}, 'transport': {'method': 'webhook', 'callback': 'https://example.com/twitch/webhook/211177006'}, 'created_at': '2021-04-19T09:40:25.255105133Z', 'cost': 1}, 'challenge': 'rcOOHKoYMfmu15O3MfVL7i_JF1gr0oZ9ogOOQ5p4q8k'}
and even if I respond status 200 and only the challenge string as payload it is not accepted as valid response. and I have no clue why since according to the documentation it should.
code sending the response:
def do_POST(self):
if 'twitch/webhook' in self.path:
print (self.headers)
content_length = int(self.headers['Content-Length'])
print (f"content_length {content_length}")
signature = self.headers['Twitch-Eventsub-Message-Signature']
message_type = self.headers['Twitch-Eventsub-Message-Type']
post_raw_data = self.rfile.read(content_length)
post_data = json.loads(post_raw_data)
print (post_data)
if message_type == 'webhook_callback_verification':
challenge = post_data['challenge']
print (f"twitch webhook_callback_verification received {self.path}, sending challenge string response {challenge}")
self.send_response(200)
#self.send_header("Content-Type", "text/plain")
#self.end_headers()
print (challenge.encode())
self.wfile.write(challenge.encode())
return
and testing it with curl:
> curl -v -XPOST -d '{"challenge": "O3JdzjVJbhPBf0WPn40ENCvoZH1J1tJDbIuXtspG-v0"}' -H "Twitch-Eventsub-Message-Type: webhook_callback_verification" -H "Twitch-Eventsub-Message-Signature: sha256=fef57ce1b4d984b0749f8d67d2ee9aef6d8779922947bc9c41926746595a4518" https://example.com/twitch/webhook/12123124
Note: Unnecessary use of -X or --request, POST is already inferred.
* Trying 10.10.10.10...
* TCP_NODELAY set
* Connected to example.com (10.10.10.10) port 443 (#0)
* ALPN, offering h2
* ALPN, offering http/1.1
* successfully set certificate verify locations:
* CAfile: /etc/ssl/cert.pem
CApath: none
* TLSv1.2 (OUT), TLS handshake, Client hello (1):
* TLSv1.2 (IN), TLS handshake, Server hello (2):
* TLSv1.2 (IN), TLS handshake, Certificate (11):
* TLSv1.2 (IN), TLS handshake, Server key exchange (12):
* TLSv1.2 (IN), TLS handshake, Server finished (14):
* TLSv1.2 (OUT), TLS handshake, Client key exchange (16):
* TLSv1.2 (OUT), TLS change cipher, Change cipher spec (1):
* TLSv1.2 (OUT), TLS handshake, Finished (20):
* TLSv1.2 (IN), TLS change cipher, Change cipher spec (1):
* TLSv1.2 (IN), TLS handshake, Finished (20):
* SSL connection using TLSv1.2 / ECDHE-RSA-AES256-GCM-SHA384
* ALPN, server did not agree to a protocol
* Server certificate:
* subject: CN=ketola.io
* start date: Mar 31 15:52:39 2021 GMT
* expire date: Jun 29 15:52:39 2021 GMT
* subjectAltName: host "example.com" matched cert's "example.com"
* issuer: C=US; O=Let's Encrypt; CN=R3
* SSL certificate verify ok.
> POST /twitch/webhook/12123124 HTTP/1.1
> Host: example.com
> User-Agent: curl/7.64.1
> Accept: */*
> Twitch-Eventsub-Message-Type: webhook_callback_verification
> Twitch-Eventsub-Message-Signature: sha256=fef57ce1b4d984b0749f8d67d2ee9aef6d8779922947bc9c41926746595a4518
> Content-Length: 60
> Content-Type: application/x-www-form-urlencoded
>
* upload completely sent off: 60 out of 60 bytes
* Closing connection 0
* TLSv1.2 (OUT), TLS alert, close notify (256):
O3JdzjVJbhPBf0WPn40ENCvoZH1J1tJDbIuXtspG-v0
can anyone see what I’m doing wrong here?