Trying to post PubSub message, getting 403

Can’t say I’ve used either of those tools for testing purposes so I don’t know if those achieve the expected results.

Here’s my example

Granted this is for sending to “global” but it’s easy to modify for single channel

or if you want a channel example

        const sigPubSubPayload = {
            "exp": Math.floor(new Date().getTime() / 1000) + 60,
            "user_id": config.extension.owner_id,
            "role": "external",
            "channel_id": channel_id,
            "pubsub_perms": {
                "send": [
                    "broadcast"
                ]
            }
        }
        const sigPubSub = jwt.sign(sigPubSubPayload, config.extension.secret);

        got({
            url: 'https://api.twitch.tv/extensions/message/' + sigPubSubPayload.channel_id,
            method: 'POST',
            headers: {
                "Client-ID": config.extension.client_id,
                "Content-Type": "application/json",
                "Authorization": "Bearer " + sigPubSub
            },
            body: JSON.stringify({
                "message": JSON.stringify({
                    event: which,
                    data
                }),
                "content_type": "application/json",
                "targets": sigPubSubPayload.pubsub_perms.send
            }),
            gzip: true
        })
        .then(resp => {
            // Same story here with the rate limit its around 60 per minute per topic
            console.error('Relay PubSub OK', channel_id, resp.statusCode, resp.headers['ratelimit-ratelimitermessagesbychannel-remaining'], '/', resp.headers['ratelimit-ratelimitermessagesbychannel-limit']);
        })
        .catch(err => {
            if (err.response) {
                console.error('Error', err.statusCode, err.response.body);
            } else {
                console.error('Generic Error', err);
            }
        })

config.extension.secret is the base64 decoded secret

config.extension.secret = Buffer.from(config.extension.extension_secret, 'base64');

Edit: I did a test. generated a JWT via JWT.io and it posted successfully.

Final things to check

  • The extension is installed and active on the target channel?
  • You did copy/paste the whole secret and JWT.io didn’t report a “weak secret”?

The error doesn’t suggest these things, since it’s having and issue with your JWT as a whole

Test code:

const fs = require('fs');
const path = require('path');

const config = JSON.parse(fs.readFileSync(
    path.join(
        __dirname,
        'config.json'
    )
));

// prepare for use
config.extension.secret = Buffer.from(config.extension.extension_secret, 'base64');

const got = require('got');

var sigPubSub = 'FromJWTIO';

        got({
            url: 'https://api.twitch.tv/extensions/message/56410307',
            method: 'POST',
            headers: {
                "Client-ID": config.extension.client_id,
                "Content-Type": "application/json",
                "Authorization": "Bearer " + sigPubSub
            },
            body: JSON.stringify({
                "message": JSON.stringify({
                    event: 'test',
                    data: {}
                }),
                "content_type": "application/json",
                "targets": ['broadcast']
            }),
            gzip: true
        })
        .then(resp => {
            // Same story here with the rate limit its around 60 per minute per topic
            console.error('Relay PubSub OK', 'test', resp.statusCode, resp.headers['ratelimit-ratelimitermessagesbychannel-remaining'], '/', resp.headers['ratelimit-ratelimitermessagesbychannel-limit']);
        })
        .catch(err => {
            if (err.response) {
                console.error('Error', err.statusCode, err.response.body);
            } else {
                console.error('Generic Error', err);
            }
        })