Getting a 403 error while making a pubsub call

I have read through a number of other threads relating to this problem, and from what I can tell I am not falling into any of those categories. I was once in the problem where I was getting 403 and garbled text, but that was corrected after adding the gzip to the request.
I added the .toString to the channel id to make sure it was a string also.

// JWT needs the secret as a buffer
const BINARY_SECRET = Buffer.from(process.env.EXTENSION_SECRET_KEY, 'base64');

// Our export object
let twitch = {};

// Create JWT's to go with requests
async function createServerJWT (channel) {
    // 60min expiry
    let timeNow = new Date();
    timeNow.setMinutes(timeNow.getMinutes() + 60);

    // Create and sign JWT. Role must be 'external'
    let rawJWT = {
        exp: Math.floor(timeNow/1000),
        user_id: process.env.DEVELOPER_USER_ID, // the account that owns the extension
        channel_id: channel,
        role: 'external',
        pubsub_perms: {
            send: ["*"]
    return await signAsync(rawJWT, BINARY_SECRET);

// Twitch PubSub messaging
twitch.sendPubSub = async function (channel, target, contentType, message) {
    try {
        let devJWT = await createServerJWT(channel.toString());
        // Target has to be a list. Turn strings into one element lists
        if (typeof target == 'string') {
            target = [target];

        let res = await{
            url: ""+channel,
            headers: {
                "Client-ID": process.env.EXTENSION_CLIENT_ID,
                "Authorization": "Bearer " + devJWT,
                'Accept-Encoding' : 'gzip, deflate'
            json: {
                content_type: contentType,
                targets: target,
                message: message
            gzip: true
    } catch (e) {
        console.log("Failed to send Twitch PubSub message: " + e);
        throw {
            status: 500,
            msg: "Failed to send PubSub message"

The return now when I post is:
Failed to send Twitch PubSub message: StatusCodeError: 403 - {“error”:“Forbidden”,“status”:403,“message”:“Error (403): JWT could not be verified”}

My Call for this on the EBS looks like this:
await twitch.sendPubSub(req.body.channelID, ‘broadcast’, ‘application/text’, req.body.message);

And the actual post for the pubsub looks like this:
$.post(’/ext/PubsubBroadcast’, {
channelID: channelID,
message: trimOldLevels()

trimOldLevels() returns a stringified JSON.

Any help would be appreciated.


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

    pubsub_perms: {
        send: [

let pck = {
    message: JSON.stringify(payload),
    content_type: 'application/json',
    targets: tokenPayload.pubsub_perms.send

let signedJwt = jwt.sign(tokenPayload, secret);{
    url: '' + channel,
    headers: {
        Authorization: 'Bearer ' + signedJwt,
        'Client-ID': client_id,
        'Content-Type': 'application/json'
    body: JSON.stringify(pck),
    gzip: true
}, function(e, r, b) {
    if (e) {
    } else if (r.statusCode == 204) {
    } else {

or equivalent, instead.

This is what I’m using on production without issue.

I think just your targets are wrong, should be broadcast instead of *

The example is

  "exp": 1503343947,
  "user_id": "27419011",
  "role": "external",
  "channel_id": "27419011",
  "pubsub_perms": {
1 Like

Thanks for the help. I have hardcoded in the broadcast instead of making it passed in through my other function, and switched the content type to be application/json, and passed the strignified pck into body instead of passing it as JSON.

All that said, I figured out my problem was way simpler… I had put the wrong Client_ID in my .env…

Note to self: Double check Id’s =(

Again thanks for your swift response. You are awesome.