This commit is contained in:
107
Mod/API/Authentication.cs
Normal file
107
Mod/API/Authentication.cs
Normal file
@ -0,0 +1,107 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace ScoreTracker.API
|
||||
{
|
||||
internal class Authentication
|
||||
{
|
||||
private static bool _signedIn = false;
|
||||
private static string _authToken;
|
||||
|
||||
/// <summary>
|
||||
/// Are we signed in?
|
||||
/// </summary>
|
||||
public static bool IsSignedIn()
|
||||
{
|
||||
return _signedIn;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the steam ticket and user info
|
||||
/// </summary>
|
||||
/// <returns>the steam ticket</returns>
|
||||
private static async Task<string> GetSteamTicket()
|
||||
{
|
||||
Plugin.Log.Info("Getting steam ticket...");
|
||||
return (await new SteamPlatformUserModel().GetUserAuthToken()).token;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Login the user
|
||||
/// </summary>
|
||||
/// <param name="onSuccess">callback for successful login, returns the token</param>
|
||||
/// <param name="onFail">callback for failed login</param>
|
||||
/// <returns>an IEnumerator</returns>
|
||||
public static async Task LoginUser(Action<string> onSuccess, Action<string> onFail)
|
||||
{
|
||||
if (_signedIn && !string.IsNullOrEmpty(_authToken))
|
||||
{
|
||||
onSuccess(_authToken);
|
||||
return;
|
||||
}
|
||||
|
||||
var ticketTask = GetSteamTicket();
|
||||
await Task.Run(() => ticketTask.Wait());
|
||||
|
||||
var ticket = ticketTask.Result;
|
||||
if (string.IsNullOrEmpty(ticket))
|
||||
{
|
||||
Plugin.Log.Error("Login failed :( no steam auth token");
|
||||
onFail("No Steam Auth Token");
|
||||
return;
|
||||
}
|
||||
|
||||
Plugin.Log.Info("Logging in...");
|
||||
var request = await Request.PostJsonAsync($"{Consts.ApiUrl}/auth/login", new Dictionary<object, object> {
|
||||
{ "ticket", ticket }
|
||||
}, false);
|
||||
if (request.IsSuccessStatusCode)
|
||||
{
|
||||
var authToken = request.Headers.GetValues("Authorization").First();
|
||||
Plugin.Log.Info($"Login successful! auth token: {authToken}");
|
||||
|
||||
onSuccess(authToken);
|
||||
_signedIn = true;
|
||||
_authToken = authToken;
|
||||
}
|
||||
else
|
||||
{
|
||||
Plugin.Log.Error($"Login failed! body: {request.StatusCode}");
|
||||
onFail($"Login failed: {request.StatusCode}");
|
||||
|
||||
_signedIn = false;
|
||||
_authToken = null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Validates the auth token and logs out if it's invalid
|
||||
/// </summary>
|
||||
/// <returns>whether the token is valid</returns>
|
||||
public static async Task<bool> ValidateAuthToken()
|
||||
{
|
||||
if (!_signedIn || string.IsNullOrEmpty(_authToken)) // If we're not signed in, return false
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var request = await Request.PostJsonAsync($"{Consts.ApiUrl}/auth/validate", new Dictionary<object, object> {
|
||||
{ "token", _authToken }
|
||||
}, false);
|
||||
|
||||
if (request.IsSuccessStatusCode)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
_signedIn = false;
|
||||
_authToken = null;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
82
Mod/API/Request.cs
Normal file
82
Mod/API/Request.cs
Normal file
@ -0,0 +1,82 @@
|
||||
using Newtonsoft.Json;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Net.Http;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace ScoreTracker.API
|
||||
{
|
||||
internal class Request
|
||||
{
|
||||
private static readonly HttpClient client = new HttpClient();
|
||||
|
||||
private class AuthHelper
|
||||
{
|
||||
public bool IsLoggedIn;
|
||||
public string FailReason = "";
|
||||
|
||||
public async Task EnsureLoggedIn()
|
||||
{
|
||||
if (Authentication.IsSignedIn() && await Authentication.ValidateAuthToken())
|
||||
{
|
||||
return; // Already logged in with a valid token
|
||||
}
|
||||
|
||||
await Authentication.LoginUser(
|
||||
token => {
|
||||
IsLoggedIn = true;
|
||||
PersistHeaders(new Dictionary<string, string>
|
||||
{
|
||||
{ "Authorization", $"Bearer {token}" }
|
||||
});
|
||||
},
|
||||
reason =>
|
||||
{
|
||||
FailReason = reason; // Store the reason for failure
|
||||
client.DefaultRequestHeaders.Clear(); // Clear headers
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Persist the given headers for all future requests
|
||||
/// </summary>
|
||||
/// <param name="headers">the headers to persist</param>
|
||||
public static void PersistHeaders(Dictionary<string, string> headers)
|
||||
{
|
||||
client.DefaultRequestHeaders.Clear(); // Clear existing headers
|
||||
foreach (var header in headers)
|
||||
{
|
||||
client.DefaultRequestHeaders.Add(header.Key, header.Value);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a POST request to the given URL with the given data
|
||||
/// </summary>
|
||||
/// <param name="url">the url to post to</param>
|
||||
/// <param name="data">the data to post</param>
|
||||
/// <param name="checkAuth">whether to check for authentication</param>
|
||||
/// <returns>the task</returns>
|
||||
public static async Task<HttpResponseMessage> PostJsonAsync(string url, Dictionary<object, object> json, bool checkAuth = true)
|
||||
{
|
||||
if (checkAuth)
|
||||
{
|
||||
var authHelper = new AuthHelper();
|
||||
await authHelper.EnsureLoggedIn();
|
||||
if (!authHelper.IsLoggedIn)
|
||||
{
|
||||
throw new Exception($"Failed to log in: {authHelper.FailReason}");
|
||||
}
|
||||
}
|
||||
var jsonString = JsonConvert.SerializeObject(json, Formatting.None);
|
||||
var content = new StringContent(jsonString, Encoding.UTF8, "application/json");
|
||||
|
||||
// Send the POST request
|
||||
var response = await client.PostAsync(url, content);
|
||||
return response;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,9 +0,0 @@
|
||||
using IPALogger = IPA.Logging.Logger;
|
||||
|
||||
namespace ScoreTracker.Common
|
||||
{
|
||||
internal class Logger
|
||||
{
|
||||
public static IPALogger Log { get; set; }
|
||||
}
|
||||
}
|
7
Mod/Consts.cs
Normal file
7
Mod/Consts.cs
Normal file
@ -0,0 +1,7 @@
|
||||
namespace ScoreTracker
|
||||
{
|
||||
internal class Consts
|
||||
{
|
||||
public static string ApiUrl = "http://localhost:7500";
|
||||
}
|
||||
}
|
@ -4,5 +4,6 @@
|
||||
<PropertyGroup>
|
||||
<ImportBSMTTargets>True</ImportBSMTTargets>
|
||||
<BSMTProjectType>BSIPA</BSMTProjectType>
|
||||
<LangVersion>8.0</LangVersion>
|
||||
</PropertyGroup>
|
||||
</Project>
|
13
Mod/Installers/AppInstaller.cs
Normal file
13
Mod/Installers/AppInstaller.cs
Normal file
@ -0,0 +1,13 @@
|
||||
using Zenject;
|
||||
|
||||
namespace ScoreTracker.Core
|
||||
{
|
||||
internal class AppInstaller : Installer
|
||||
{
|
||||
|
||||
public override void InstallBindings()
|
||||
{
|
||||
Plugin.Container = Container;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,6 +1,12 @@
|
||||
using IPA;
|
||||
using ScoreTracker.Common;
|
||||
using IPALogger = IPA.Logging.Logger;
|
||||
using SiraUtil.Zenject;
|
||||
using IPA.Loader;
|
||||
using Zenject;
|
||||
using ScoreTracker.Core;
|
||||
using System.Threading.Tasks;
|
||||
using ScoreTracker.API;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace ScoreTracker
|
||||
{
|
||||
@ -8,24 +14,36 @@ namespace ScoreTracker
|
||||
public class Plugin
|
||||
{
|
||||
internal static Plugin Instance { get; private set; }
|
||||
internal static IPALogger Log { get; private set; }
|
||||
internal static DiContainer Container; // Workaround to access the Zenject container in SceneLoaded
|
||||
|
||||
[Init]
|
||||
public Plugin(IPALogger logger)
|
||||
public Plugin(IPALogger logger, PluginMetadata metadata, Zenjector zenjector)
|
||||
{
|
||||
Instance = this;
|
||||
Logger.Log = logger; // Setup the logger
|
||||
Log = logger; // Setup the logger
|
||||
|
||||
// Install our Zenject bindings
|
||||
zenjector.Install<AppInstaller>(Location.App);
|
||||
}
|
||||
|
||||
[OnStart]
|
||||
public void OnApplicationStart()
|
||||
{
|
||||
Logger.Log.Info("OnApplicationStart");
|
||||
Log.Info("OnApplicationStart");
|
||||
|
||||
Task.Run(async () =>
|
||||
{
|
||||
await Request.PostJsonAsync("http://localhost:7500/boobies", new Dictionary<object, object> {
|
||||
{ "boobies", "yes" }
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
[OnExit]
|
||||
public void OnApplicationQuit()
|
||||
{
|
||||
Logger.Log.Info("OnApplicationQuit");
|
||||
Log.Info("OnApplicationQuit");
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -40,6 +40,17 @@
|
||||
<DisableZipRelease>True</DisableZipRelease>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="BGNet, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" />
|
||||
<Reference Include="BS_Utils, Version=1.12.0.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<Private>False</Private>
|
||||
<HintPath>$(BeatSaberDir)\Plugins\BS_Utils.dll</HintPath>
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
</Reference>
|
||||
<Reference Include="SiraUtil, Version=3.1.2.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<Private>False</Private>
|
||||
<HintPath>$(BeatSaberDir)\Plugins\SiraUtil.dll</HintPath>
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
</Reference>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Core" />
|
||||
<Reference Include="System.Xml.Linq" />
|
||||
@ -86,13 +97,26 @@
|
||||
<HintPath>$(BeatSaberDir)\Beat Saber_Data\Managed\UnityEngine.UIModule.dll</HintPath>
|
||||
<Private>False</Private>
|
||||
</Reference>
|
||||
<Reference Include="UnityEngine.UnityWebRequestModule, Version=0.0.0.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<Private>False</Private>
|
||||
<HintPath>$(BeatSaberDir)\Beat Saber_Data\Managed\UnityEngine.UnityWebRequestModule.dll</HintPath>
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
</Reference>
|
||||
<Reference Include="UnityEngine.VRModule">
|
||||
<HintPath>$(BeatSaberDir)\Beat Saber_Data\Managed\UnityEngine.VRModule.dll</HintPath>
|
||||
<Private>False</Private>
|
||||
</Reference>
|
||||
<Reference Include="Zenject, Version=0.0.0.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<Private>False</Private>
|
||||
<HintPath>$(BeatSaberDir)\Beat Saber_Data\Managed\Zenject.dll</HintPath>
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
</Reference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Common\Logger.cs" />
|
||||
<Compile Include="API\Authentication.cs" />
|
||||
<Compile Include="API\Request.cs" />
|
||||
<Compile Include="Consts.cs" />
|
||||
<Compile Include="Installers\AppInstaller.cs" />
|
||||
<Compile Include="Plugin.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
</ItemGroup>
|
||||
@ -109,6 +133,9 @@
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Newtonsoft.Json">
|
||||
<Version>13.0.3</Version>
|
||||
</PackageReference>
|
||||
</ItemGroup>
|
||||
<ItemGroup />
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
|
2
Mod/ScoreTracker.csproj.DotSettings
Normal file
2
Mod/ScoreTracker.csproj.DotSettings
Normal file
@ -0,0 +1,2 @@
|
||||
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
|
||||
<s:String x:Key="/Default/CodeInspection/CSharpLanguageProject/LanguageLevel/@EntryValue">CSharp80</s:String></wpf:ResourceDictionary>
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"$schema": "https://raw.githubusercontent.com/nike4613/ModSaber-MetadataFileSchema/master/Schema.json",
|
||||
"id": "score-tracker-mod",
|
||||
"id": "ScoreTracker",
|
||||
"name": "ScoreTracker",
|
||||
"author": "fascinated7",
|
||||
"version": "0.0.1",
|
||||
@ -9,7 +9,7 @@
|
||||
"dependsOn": {
|
||||
"BSIPA": "^4.2.2",
|
||||
"BS Utils": "^1.12.0",
|
||||
"BeatSaberMarkupLanguage": "^1.6.3"
|
||||
"SiraUtil": "^3.1.0"
|
||||
},
|
||||
"loadAfter": [ "BS Utils", "BeatSaberMarkupLanguage" ]
|
||||
"loadAfter": [ "BS Utils", "SiraUtil" ]
|
||||
}
|
Reference in New Issue
Block a user