Bad Request when sending message to Extension PubSub

Hello everyone and thanks for your time.

Sorry for this noobish question but I’m experiencing a bit with this and I don’t quite get what am I missing. I’m trying to send a message to PubSub from my EBS and I’m getting a 400 Bad Request response, so clearly I’m doing something wrong, would you please point my in the right direction?

This is the code I’m using. C# .Net

HttpRequestMessage httpRequest = new HttpRequestMessage(HttpMethod.Post, $"https://api.twitch.tv/extensions/message/{streamerID}");

httpRequest.Headers.Add("Authorization", $"Bearer {GenerateToken(streamerID)}");
httpRequest.Headers.Add("Client-Id", "NotARealClientId");
httpRequest.Content = new StringContent("{\"content_type\":\"application/json\", \"message\":\"{\"foo\":\"bar\"}\", \"targets\":[\"broadcast\"]}", System.Text.Encoding.UTF8, "application/json");

var response = httpClient.SendAsync(httpRequest).Result;

This is the GenerateToken code (I’m using a temporary key secret here just in case you want decode the JWT)

const string SECRET_KEY = "lsdkjflk3j345908usdflksjlvcxkjvxlkj23+ASx8q=";

public static string GenerateToken(long channelID)
{
    var payload = new Dictionary<string, object>()
    {
        {"exp", DateTimeOffset.UtcNow.AddDays(1).ToUnixTimeSeconds() },
        {"channel_id", channelID },
        {"pubsub_perms",
            new Dictionary<string, object>()
            {
                { "send", new List<string>() { "*" } },
                { "listen", new List<string>() { "*" } }
            }
        },
        {"role", "external" }
    };

    string token = Jose.JWT.Encode(payload, System.Text.Encoding.UTF8.GetBytes(SECRET_KEY), Jose.JwsAlgorithm.HS512);

    return token;
}

Tell me if you guys needs to see another part of the code. This is probably something really easy to fix but I cant figure it out!

Thank you

When you get a 400 Bad Request, whats the body response?

Also you are base64decoding your Secret before use?

And explicitly making the channeID be a “string” not a integer/number?

I did some modifications to the GenerateToken method to change what you pointed (base 64 encoding and converting channelID.ToString()). What I wasn’t doing was reading the body response, my fault.

"{\"error\":\"Bad Request\",\"status\":400,\"message\":\"Request body was not parsable. Attempted Content-Type: \\\"application/json\\\"\"}"

Problem is in the Content-Type right? Just to be sure

Edit: Yep, indeed. Now I’m getting another set of errors but since I’m now reading the body response I can figure it out… I hope!

Thank you for your time Barry! Much appreciated!

Hurrah!

Usually on a 4xx code you still get a body you can read and utilise to error checking.

The secret should be base64DEcoded not encoded for clarity. (The secret is given to you base64encoded, so you need to decode for use first.

Yep, I meant “base 64 decoding” haha. Now I’m getting a 403 forbidden response, the body is a set of weird characters though:

"\u001f�\b\0\0\0\0\0\0\u0003,�1\n�0\u0010Dѫ,S�\u0010L�V��\u0004�(YE�\r$�\u0012�n4i�<�\u001b\u0012c�p\u0018C\\v�Ea��\u0004g�����M\n��ut�\u0012q#\fG��ښ�\"c8v�L\u001a2��R�h�?�f*�XG�\a�\v\0\0��\u0003\0�-�7�\0\0\0"

Probably missing some convertion here. However I’ve seen other users with this 403 responses too, wasn’t this because of testing locally?

403 forbidden and gibberish means you need to enable GZIP on the part of the code making that request.

Usually a 403 is a scope issue.

1 Like

You rocks Barry. Now I can read the message code and verify that JWT couldn’t be verified. I’ll keep investigating. Thank you again for your time =)

(btw, I’m looking in other posts as well. I’ve seen other users with this responses too so I might find something. So I don’t bother you that much!)

This may help:

let tokenPayload = {
    exp: Math.floor(new Date().getTime() / 1000) + 60,
    channel_id: ''+channel,
    role: 'external',

    pubsub_perms: {
        send: [
            'broadcast'
        ]
    }
}

The only thing I see different is that you perms dictionary has two keys and both with a *

try

    {"pubsub_perms",
        new Dictionary<string, object>()
        {
            { "send", new List<string>() { "broadcast" } 
        }
    },

[ – This is no longer valid – See post below]
Sorry for the delay. Nope… Still the same unfortunately. I’ve been reading other posts where you answer too and now I just want to clarify something.

1- Should I use the secret key provided by twitch under Extension Settings -> Secret Keys? Taking into consideration that I’m using external as a role in the payload.

2- If not and I need to create one, and I’m taking the risk in sounding very very noobish (lol), how do I make one? Do I need to pay attention to something in particular? Or a web like https://www.grc.com/passwords.htm would be enough to generate one and use it as a secret key?

3- What about the algorithm that I need to use? Does it matter?

Thanks!
[ — ]

EDIT: I’ve changed the algorithm from HS512 to HS256 and now I’m just getting an empty response with a 204 CODE as the examples in the reference page shows, NOW it’s working right? Or it’s just a placebo haha

I was going to show the code but I suddenly thought about something, so let me tell you what I’m trying to do first.

I’m getting info in real time from Overwolf’s PubSub from games being played by streamers with the Twitch Game Events App. Basically, everytime something happens on the game it forwards the new data to Overwolf’s PubSub where my EBS is listening to.

When I want to send some data to my twitch extension I thought about sending that data (or at least the important data) received from the Overwolf’s PubSub to Twitch’s PubSub, and in the extension front-end listen for events from Twitch’s PubSub and update the info on the extension, not sure if I’m making myself clear.

Does it have any sense or I’m just wrong from the beggining?

That is the correct response yes.

204 is the HTTP Code for “created” and there is no return information so the body is blank.

Some API’s return a 204 and some information about the created item, but the item created cannot be loaded via the API in this example.

Sounds good to me. I’ve not fully read the Overwolf docs, so it might be possible to make the extension connect to the Overwolf pubsub which saves a relay via EBS, but the EBS can do more useful stuff like event filtering etc…

Hi Barry! Thank you for your patience.

Alright, so if what I told you before does make sense then I have another question before moving on.

When I started developing my extension, the developer-rig was on the first versions. I clone the repository and got the new rig-version. I did some changes on the .env.local file, specifically on the EXT_CHANNEL property and what I noticed is that the onAuth() function is still returning the “old” EXT_CHANNEL name. I read the readme.html in case that I’m missing something and there was a lot of new things to configure. I’ve always used the yarn start command to start the dev-rig. My front-end extension and the back-end are hosted on a local IIS. If I change something on any of them the changes are indeed updated. So my question now is: is this still the way to start the dev-rig?

My question comes because window.Twitch.ext.listen() isn’t getting triggered. What I did was to open the dev-rig, run a unit test on my EBS to post to the PubSub and I was hoping to ext.listen() to receive the message I’m sending. But I’m not sure if this is how it’s intended to work and if PubSub works when testing locally.

Thank you again for helping me out!

When you updated the contents of .env.local and restarted the rig. Did you use the existing views or create new views?

You should create new views.

I didn’t know the .env.local was still in use, a lot of the rig is on yarn start -s <secret> -c location/of/your/config where -c gives the path to a JSON file for the format

{
  "channel": "<channel name>",
  "clientID": "<client id>",
  "ownerName": "<owner name>",
  "version": "<version>"
}

https://github.com/twitchdev/developer-rig#configuring-the-developer-rig

Yep Barry, I wasn’t creating any new views, now the channelId is being updated properly.

Last question, once I got this sorted out I think I shouldn’t have any other problem. If I want to listen for the messages I send to the PubSub from my EBS, I have to use the window.Twitch.ext.listen right? If so I’m doing something wrong because the callback for the listen function isn’t getting fired. Is this because I’m testing locally?

Here’s the simple code I’m using:

if (window.Twitch.ext) {

    window.Twitch.ext.listen("broadcast", function(target, contentType, message) {
        console.log('Recv');
    });
    
    window.Twitch.ext.onAuthorized(function(auth) {   
        _auth = auth.token;

        console.log(auth);

        $.ajax({ 
            url: "https://localhost:444/api/master/Start",
            type: "GET",
            crossDomain: true,
            data: {
                cn: auth.channelId,
                t: _auth
            },
            success: function (res) {
                console.log("done?: " + res);
            }
        });
    });
  
    window.Twitch.ext.onContext(function(context, contextFields) {
        console.log("onContext");
    });

    window.Twitch.ext.onError(function(err) {
        console.log("err: " + err);
    }); 
}

How I’m testing this? When the extension is loaded locally then I open my EBS code and send some messages to the PubSub. I was hoping that if I send a message to the PubSub then the front-end extension will get the message. Perhaps I’m wrong in my logic.

I got this error response when trying to update the broadcaster configuration. This is a generic error, and is not only for PubSub requests.

In my case, the problem was that I had to encode non-ascii characters. I simply used the code here:

and that was it.

You nercro’ed a post with the wrong answer.

This post pertained to decoding a response, not encoding a body to be posted.