Event subscription and python BaseHTTPServer challenge responder

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?

The Twitch CLI might have track down/test the issue https://github.com/twitchdev/twitch-cli/blob/main/docs/event.md#verify-subscription.

Looks like this python code you may need to write some headers manually similar to, this post in Java

most likely a content-length header

Your curl -v output didn’t seen to spit back but in the way of response headers. So that looks like the issue.

But the TwtichCLI should shed some light on whats going on.

Thank you for the tip. It was indeed the missing Content-Length header that was the culprit.

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.