using System; using System.Diagnostics; using System.Net; using System.Runtime.InteropServices; using System.Text; using System.Text.Json.Nodes; using System.Threading; using System.Threading.Tasks; namespace BetterRaid.Misc; public static class Tools { private static HttpListener? _oauthListener; private static Task? _oauthWaiterTask; // Source: https://stackoverflow.com/a/43232486 public static void StartOAuthLogin(string url, Action? callback = null, CancellationToken token = default) { if (_oauthListener == null) { _oauthListener = new HttpListener(); _oauthListener.Prefixes.Add("http://localhost:9900/"); _oauthListener.Start(); _oauthWaiterTask = WaitForCallback(callback, token); } OpenUrl(url); } private static async Task WaitForCallback(Action? callback, CancellationToken token) { if (_oauthListener == null) return; if (token.IsCancellationRequested) return; Console.WriteLine("Starting token listener"); while (!token.IsCancellationRequested) { var ctx = await _oauthListener.GetContextAsync(); var req = ctx.Request; var res = ctx.Response; if (req.Url == null) continue; Console.WriteLine("{0} {1}", req.HttpMethod, req.Url); // Response, that may contain the access token as fragment // It must be extracted client-side in browser if (req.Url.LocalPath == "/") { var buf = new byte[1024]; var data = new StringBuilder(); int bytesRead; while ((bytesRead = await req.InputStream.ReadAsync(buf, token)) > 0) { data.Append(Encoding.UTF8.GetString(buf, 0, bytesRead)); } req.InputStream.Close(); Console.WriteLine(data.ToString()); res.StatusCode = 200; await res.OutputStream.WriteAsync(Encoding.UTF8.GetBytes(OAUTH_CLIENT_DOCUMENT).AsMemory(0, OAUTH_CLIENT_DOCUMENT.Length), token); res.Close(); } if (req.Url.LocalPath == "/login") { var buf = new byte[1024]; var data = new StringBuilder(); int bytesRead; while ((bytesRead = await req.InputStream.ReadAsync(buf, token)) > 0) { data.Append(Encoding.UTF8.GetString(buf, 0, bytesRead)); } req.InputStream.Close(); var json = data.ToString(); var jsonData = JsonObject.Parse(json); if (jsonData == null) { Console.WriteLine("[ERROR] Failed to parse JSON data:"); Console.WriteLine(json); res.StatusCode = 400; res.Close(); continue; } if (jsonData["access_token"] == null) { Console.WriteLine("[ERROR] Missing access_token in JSON data."); res.StatusCode = 400; res.Close(); continue; } var accessToken = jsonData["access_token"]?.ToString(); App.TwitchOAuthAccessToken = accessToken!; res.StatusCode = 200; res.Close(); Console.WriteLine("[INFO] Received access token!"); callback?.Invoke(); _oauthListener.Stop(); return; } } } public static void OpenUrl(string url) { try { Process.Start(url); } catch { // hack because of this: https://github.com/dotnet/corefx/issues/10361 if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { url = url.Replace("&", "^&"); Process.Start(new ProcessStartInfo(url) { UseShellExecute = true }); } else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) { Process.Start("xdg-open", url); } else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) { Process.Start("open", url); } else { throw; } } } private const string OAUTH_CLIENT_DOCUMENT = @"
BetterRaid Twitch Login

Successfully logged in!

"; }