Cannot send PubSub from EBS

I am trying to send a PubSub message from a PHP EBS. Using the documentation at Reference | Twitch Developers and a php-jwt library, I came up with the following code to create the payload.

$jwt = new JWT(base64_decode(SOTD_SECRET), 'HS256', 3600, 10);
$token = $jwt->encode([
'exp' => $exp,
'user_id' => $userID,
'channel_id' =>$broadcasterID,
 'role'=>'external',
 'is_linked'=>'true',
 'pubsub_perms'=>['send'=>['broadcast']]
 ]);

The secret is from Extension Secrets under Extension Client Configuration.
I have used several online jwt ā€œtestersā€ and and I am confident that the token is created and signed correctly.

I’m using the following code to send the post.

$message="refreshLeaders";	
$body=json_encode(["broadcaster_id" => $broadcasterID,'message' => $message,'target' => ['broadcast'],])	;
$curl = curl_init($url);
mylog("Body: $body");
$url='https://api.twitch.tv/helix/extensions/pubsub/';
curl_setopt($curl, CURLOPT_URL, $url);
curl_setopt($curl, CURLOPT_POST, true);
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
$headers = array( 'Authorization' => 'Bearer ' . $token, 'Client-ID' => SOTD_CLIENT_ID, 'Content-Type' => 'application/json');
curl_setopt($curl, CURLOPT_HTTPHEADER, $headers);
curl_setopt($curl, CURLOPT_POSTFIELDS, $body);
$resp = curl_exec($curl);
curl_close($curl);
mylog("Response from Send Message: ".$resp);

The Client ID is the extension client ID
The response I get is {ā€œerrorā€:ā€œUnauthorizedā€,ā€œstatusā€:401,ā€œmessageā€:ā€œOAuth token is missingā€}. This seems strange since the documentation clearly states " Authorization - Signed JWT created by an Extension Backend Service (EBS), following the requirements documented in [Signing the JWT]".

I’ve tried different combinations of client id and secrets. I’ve tried opaque_user_id instead of user_id. I’ve tried different combinations for target.
I’ve read as much documentation as I could find, as well as any forum posts on the topic. I cannot figure out what I’m doing wrong.

The URL is

https://api.twitch.tv/helix/extensions/pubsub

Not

https://api.twitch.tv/helix/extensions/pubsub/

You don’t need 'is_linked'=>'true',

And make sure both the user_id and channel_id are strings not integers

So

$userID = '123123';

Not

$userID = 123123;

The trailing backslash was a BIG issue. I usually find details like that, but missed it… over and over. Thank you.

Debugging as I type this, so… rubber ducky

ā€˜is_linked’ is gone.
user_id and channel_id were both strings, but I re-cast them anyway to be certain. (verified at jwt.io)
Now the response is {ā€œerrorā€:ā€œUnauthorizedā€,ā€œstatusā€:401,ā€œmessageā€:ā€œjwt token is requiredā€}
Checked expiration. ā€œexpā€ is +1 hour. (verified on jwt.io)
I have tried without base64_decodeing the secret. Same error so I changed it back.
I re-wrote the header and found two errors. No colon and no space after Authorization.
New error is {ā€œerrorā€:ā€œUnauthorizedā€,ā€œstatusā€:401,ā€œmessageā€:ā€œClient ID is missingā€}.
Of course it’s missing. Same reason. No colon and no space: Same with Content-Type. I changed the header creation to

$headers = array( 
			'Authorization: '.'Bearer ' . $token,
			'Client-ID: ' . SOTD_CLIENT_ID,
			'Content-Type: ' .'application/json');

Problem solved and the message is being recieved by the extension.
Thank you again! This has caused me uncounted hours of grief.

I got as far as the URL before debugging your actual PHP to spot the header malconstruct :stuck_out_tongue:

No worries. I just needed to get past the URL error. The backslash was a leftover from trying the api.twitch.tv/extensions/message/CHANNELID endpoint. Feeling doubly ignorant since I recall some time in the past week seeing you mention this exact thing in an older post.
Off Topic: I tried letting my cat be a rubber ducky, but he just walks away.

2 Likes