How does Twitch’s new EventSub work?

Preamble

In the beginning, Twitch created Webhooks. And the world rejoiced, finally we no longer have to long poll for things such as followers! Hurrah! But it came with a gotcha, since Twitch Webhooks is based on the Websub specification, each and every Webhook you wanted to listen to had to be renewed, now this is limited to 10 days (max) or the time remainging on the Token you authenticate with (when asking to listen to priviledge topics such as subscribers), which generally meant you had to remake certain subscriptions every 4 hours, after of course renewing the token with the refresh token. Since the maximum length of a Twitch user token is 4 hours. (Implicit tokens use 60 days, but you can’t refresh those).

This wasn’t a good solution for developers, since it creates siginificant load, and “wasting” of your rate limits with Helix. Even more so when working with multiiple streamers.

Twitch has acknowledged this as a problem, which leads us to the new product of EventSub.

Aside from ignoring the need to remake your subscriptions periodically, EventSub also doesn’t need a correspoding Helix endpoint to exist for the topic you wish to listen on (this also means a different format for the data payloads), and means EventSub can make new topic types without waiting for Helix to create the endpoint first.

So, now onwards to the meat of this post!

EventSub, hows does it work?

The long and short of it, is that everything is sent via HTTP Post requests, to your SSL protected endpoint.

Unlike Webhooks, Eventsub only authenticates using App Access Tokens (for server to server requests), but how does this work when you are attempting subscribe to a channels new Subscribers topic, or the Ban/timeout events topics?

Authentication

Well, when you make a subscription request to EventSub, Twitch looks at your App Access token, then checks in the background if the requested broadcaster has connected to your Application at any point, with the relevant scopes, and not revoked that connection.

So, it’s a “two legged” approach to authentication. But means that you, the developer, don’t need to store the broadcasters access or refresh tokens, after they have authorised. though you generally would in order to perform “catch up” if your application goes offline/restarts for any reason.

Infrastructure

So to utilise EventSub, you need to create a portal that allows broadcasters to grant access to your Application to their account with the relevant scopes, using “regular” User Authentication.

Then on authentication you check/create eventsub subscriptions as needed, using your App Access Token. You should also store and use the returned User Acces token for use on catchups

And you need a server that can recieve HTTP Posts from Twitch for verification of the connection and accepting data payloads. (Generlly here you’ll HTTP 2xx OK as quickly as possible and send the data into background processes.

TLDR: for example if you can call Get Broadcaster Subscriptions (at the moment you obtained the Access Token) then your ClientID can subscribe to the channel.subscribe EventSub.

Summary

Hopefully this should help out anyone that is new to EventSub or getting involved with the Twitch Channel Points Hackathon.

Since many people may look at EventSub to recieve new Channel point redemptions but might trip up over the initial authentication setup.

There are a number of examples on my GitHub that might help, but if you need any further help, please join us on the TwitchDev Discord Server!

Just thought I would write up this post with a ramble of notes on how EventSub Authentication works, since theres gonna be a spike in people asking and probably getting stuck if they are new to working with Twitch.

Twitch API Examples

I spend a lot of time on the Twitch Developer forums and Discord helping out other third party developers. That among other things led to me being asked to become a Twitch Ambassador, which is probably a story for another post.

As part of spending a lot of time helping of Forums/Discord, it become useful to write up some examples in various languages for people to refer to, since some people prefer code examples over documentation, and it’s easier to demonstrate how to tie multiple calls/endpoints together for the desired result.

To that end my GitHub Repo at barrycarlyon/twitch_misc now exists and holds examples from Authentication flows (from Implicit to server access and regular user in-between), extension config/pubsub, and examples for Webhooks and the new Eventsub (which is worth a look!). So if you are looking for some examples do checkout the Repository. Some of the examples can even be tested on GitHub itself via GitHub pages, the examples available are listed in the readme and at the Github Pages site.

Twitch also recently made the requirement that all calls to helix (aka the New API) need to be Authenticated using a Bearer, which made it difficult for Extensions to get the viewers details. So to that end I created a basic example of how to do that in an Extension with a “User Profile Extension” example. Which is at BarryCarlyon/twitch_profile_extension. So this covers a good way to handle that flow.

Right now most of the examples are nodeJS, or PHP, but there are some in Python kicking about!

I’ll be looking at adding more examples and other examples in other languages as we go!

I’m usually really bad at commenting my code as I prefer reading the code, but I made a conscious effort to add useful code comments on these repos!

What is ArtificialNext?

Artificial (Next) is a show on Twitch, it’s currently in it’s third Season! It is an Emmy and Peabody nominated (2020)/winning (2019) show.

Season three is very interesting as due to COVID, as it is all being done remotely, the Actors all stay at their respective homes, and dial in to the central “office” to be “included” in the show. And in in cases, the show runner has not met the new actors/actress for this season!

The premise of the show, is that Dr. Matt Lin, developer of a robot body and AI, aims to “replace” his lost daughter. The show follows the development of that AI.

At the end of Season 2 that AI, guided by Twitch Chat decided to murder someone involved with the program. Season 3 follows on, where the AI has been reset, but the daughter of the person that was “murdered” is seeking vengeance, for want of a a better term! (Please DO check out the official Website and the vods to understand the full story line! This is my understanding and you should make your own!)

For Season 3 I’ve been contracted to design and develop, the user/viewer interaction systems, but HOW does this work?

To put it simply, Artificial is a Full Motion Video Game, where Twitch votes on the direction as a democracy, each possible route is planned out by the writers, and the writers (and actors) respond to the Chat choices, each season “story” (or consequential choices) route is picked by the chat.

I am contracted to design and develop the systems that interact between chat and the show, as smoothly and as transparently as possible. The Extension details can be seen at the Extension page.

You can checkout more about the show and watch the past seasons, on Twitch or on IMDb

ArtificialNext is live at 1 am UK time Friday Mornings, or 5pm PDT Thursday.

The Future and all things change?!

Long time no write.

Hopefully gonna start writing again, (not the first time I’ve said that, so we’ll see how we go!)

Anywho, I do currently find myself unemployed after quitting my previous Job on Monday, nothing on my part but the actions of others leading to the actions of others forcing my hand.

I am currently deciding what to do next!

If you know whom I used to work for you can go look it up yourself, I don’t feel the need to talk about it myself.

I am continuing to do various bits and pieces for the Streamers I work with on Twitch, so nothing has changed there, I’m just out of full time work!

Watch this space is the TLDR!

In the mean time, go checkout my Twitch Extensions

Google oAuth and offline access

Been doing a lot of various stuff and things for CohhCarnage and some of that stuff has involved building an achievements tracking system for the website.

One of those achievements, is for YouTube Subscription. Where the achievement is awarded to the logged in user, if the user has subscribed to a given YouTube channel, in this case Cohh’s YouTube.

In order to make sure that people can’t “cheat” the system, we ask them to link their Google/YouTube account with the website and use the relevant API to look up their Subscription status.

Initially this worked fine, but I ran into some issues where the oAuth token stored has expired and thus I can’t do a status check, for cases where the user links their YouTube to their Cohhilition Account then doesn’t subscribe on YouTube until after 24 hours later (or some caching issue with Google).

So, the simple fix using Googles PHP Library for oAuth’ing is to just do a

<?php

$client->setAccessType('offline')

Now, this works fine for the most part, you happily get a refresh token, and can thus renew your token.

Then comes a hiccup, if for whatever reason you have offline access type on, and the user has previously authorised the application and it’s offline permission, you DON’T get a refresh token in some cases. Some user cases include:

  • you’ve lost their token,
  • or got a bad one
  • or the user managed to find the authentication loop (again) when they shouldn’t, and thus a new code/token combo is generated

Normally you are using something like:

<?php

$client = new Google_Client();
$client->setClientId($client_id);
$client->setClientSecret($client_secret);
$client->setRedirectUri($redirect_uri);
$client->addScope("email");
$client->addScope("profile"); 
$client->setAccessType('offline');// last forever/give me a refresh

But in order to make sure that you get a refresh_token EVERY time someone goes through the authentication loop, you have to adjust as follows:

<?php

$client = new Google_Client();
$client->setClientId($client_id);
$client->setClientSecret($client_secret);
$client->setRedirectUri($redirect_uri);
$client->addScope("email");
$client->addScope("profile"); 
$client->setAccessType('offline');// last forever/give me a refresh
$client->setApprovalPrompt('force');// force a refresh token return everytime

Apparently, using

'offline'

is supposed to imply

'force'

according to some Stack Overflows posts, but this doesn’t seem the case.

In the end my full Google_Client setup looks like:

<?php

        $client = new Google_Client();
        $client->setAuthConfig($consumer);
        $client->addScope('profile');
        $client->addScope('email');
        $client->addScope('https://www.googleapis.com/auth/youtube.readonly');
        $client->setAccessType('offline');// asks for a refresh token
        $client->setApprovalPrompt('force');// forces the refresh token being returned
        $client->setIncludeGrantedScopes(true);
        $client->setRedirectUri($callback);

Just an odd thing I came across recently that I thought I would write up. Most of the notes here are from Stack Overflow post on the subject