Skip to content

Instantly share code, notes, and snippets.

@MeepsKitten
Created February 11, 2022 05:24
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save MeepsKitten/2aec5b7a4902e7c67f9793132d109204 to your computer and use it in GitHub Desktop.
Save MeepsKitten/2aec5b7a4902e7c67f9793132d109204 to your computer and use it in GitHub Desktop.
Twitch Implicit OAuth Unity Example
/*
* Simple Twitch IMPLICIT OAuth flow example
* by HELLCAT & MeepsKitten
*
* At first glance, this looks like more than it actually is.
* It's really no rocket science, promised! ;-)
* And for any further questions contact me directly or on the Twitch-Developers discord.
*
* 🐦 https://twitter.com/therealhellcat & https://twitter.com/MeepsKitten
* 📺 https://www.twitch.tv/therealhellcat & https://twitch.tv/MeepsKitten
*/
using System;
using System.IO;
using System.Net;
using System.Text.RegularExpressions;
using UnityEngine;
using UnityEngine.Networking;
public class TwitchOAuth : MonoBehaviour
{
[SerializeField] private string twitchAuthUrl = "https://id.twitch.tv/oauth2/authorize";
[SerializeField] private string twitchClientId = "PUT YOUR CLIENT ID HERE";
[SerializeField] private string twitchRedirectUrl = "http://localhost:8080/";
private string _twitchAuthStateVerify;
private string _authToken = null;
/// <summary>
/// Starts the Twitch OAuth flow by constructing the Twitch auth URL based on the scopes you want/need.
/// </summary>
public void InitiateTwitchAuth()
{
string[] scopes;
string s;
// list of scopes we want
scopes = new[]
{
"chat:edit",
"chat:read",
"channel:read:redemptions",
"user:read:subscriptions",
"user:read:broadcast",
"bits:read",
"channel:read:hype_train",
"channel:manage:redemptions"
};
// generate something for the "state" parameter.
// this can be whatever you want it to be, it's gonna be "echoed back" to us as is and should be used to
// verify the redirect back from Twitch is valid.
_twitchAuthStateVerify = ((Int64)(DateTime.UtcNow.Subtract(new DateTime(1970, 1, 1))).TotalSeconds).ToString();
// query parameters for the Twitch auth URL
s = "client_id=" + twitchClientId + "&" +
"redirect_uri=" + UnityWebRequest.EscapeURL(twitchRedirectUrl) + "&" +
"state=" + _twitchAuthStateVerify + "&" +
"response_type=token&" +
"scope=" + String.Join("+", scopes);
// start our local webserver to receive the redirect back after Twitch authenticated
StartLocalWebserver();
// open the users browser and send them to the Twitch auth URL
Application.OpenURL(twitchAuthUrl + "?" + s);
}
/// <summary>
/// Opens a simple "webserver" like thing on localhost:8080 for the auth redirect to land on.
/// Based on the C# HttpListener docs: https://docs.microsoft.com/en-us/dotnet/api/system.net.httplistener
/// </summary>
private void StartLocalWebserver()
{
HttpListener httpListener = new HttpListener();
httpListener.Prefixes.Add(twitchRedirectUrl);
httpListener.Start();
httpListener.BeginGetContext(new AsyncCallback(IncomingHttpRequest), httpListener);
}
/// <summary>
/// Handles the incoming HTTP request
/// </summary>
/// <param name="result"></param>
private void IncomingHttpRequest(IAsyncResult result)
{
HttpListener httpListener;
HttpListenerContext httpContext;
HttpListenerRequest httpRequest;
HttpListenerResponse httpResponse;
string responseString;
// get back the reference to our http listener
httpListener = (HttpListener)result.AsyncState;
// fetch the context object
httpContext = httpListener.EndGetContext(result);
// if we'd like the HTTP listener to accept more incoming requests, we'd just restart the "get context" here:
httpListener.BeginGetContext(new AsyncCallback(IncomingAuth),httpListener);
// the context object has the request object for us, that holds details about the incoming request
httpRequest = httpContext.Request;
// build a response to send JS back to the browser for OAUTH Relay
httpResponse = httpContext.Response;
responseString = "<html><body><b id=\"auth\">Login Complete</b><br>" +
"<script type=\"text/javascript\">" +
"var xhr = new XMLHttpRequest(); " +
$"xhr.open(\"POST\", \"{UnityWebRequest.EscapeURL(twitchRedirectUrl)}\");" +
"xhr.send(window.location);" + //Sending the window location (the url bar) from the browser to our listener
"</script></body></html>";
byte[] buffer = System.Text.Encoding.UTF8.GetBytes(responseString);
// send the output to the client browser
httpResponse.ContentLength64 = buffer.Length;
System.IO.Stream output = httpResponse.OutputStream;
output.Write(buffer, 0, buffer.Length);
output.Close();
}
private void IncomingAuth(IAsyncResult ar)
{
//mostly the same as IncomingHttpRequest
HttpListener httpListener;
HttpListenerContext httpContext;
HttpListenerRequest httpRequest;
httpListener = (HttpListener)ar.AsyncState;
httpContext = httpListener.EndGetContext(ar);
httpListener.BeginGetContext(new AsyncCallback(IncomingAuth), httpListener);
httpRequest = httpContext.Request;
//this time we take an input stream from the request to recieve the url
string url;
using (var reader = new StreamReader(httpRequest.InputStream,
httpRequest.ContentEncoding))
{
url = reader.ReadToEnd();
}
//regex to extract the OAUTH and auth state
Regex rx = new Regex(@".+#access_token=(.+)&scope.*state=(\d+)");
var match = rx.Match(url);
//if state doesnt match reject data
if (match.Groups[2].Value != _twitchAuthStateVerify) return;
_authToken = match.Groups[1].Value;
Debug.Log("AUTH: " + _authToken);
httpListener.Stop();
}
/* JS NOTEPAD
var xhr = new XMLHttpRequest();
xhr.open(\"POST\", \"http://localhost:8080//\");
xhr.send(window.location);}}
*/
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment