using System.Reflection; using System.Text.Json; using System.Text.Json.Nodes; using HarmonyLib; using SPTarkov.Reflection.Patching; using SPTarkov.Server.Core.Routers; using SPTarkov.Server.Core.Models.Common; using SPTarkov.Server.Core.Models.Eft.Launcher; using SPTarkov.Server.Core.Callbacks; using SPTarkov.Server.Core.Controllers; using System.Threading; namespace PersonalAuthMod; /// /// AsyncLocal to pass the password from the patch to the router /// public static class AuthContext { private static readonly AsyncLocal _currentPassword = new(); private static readonly AsyncLocal _currentSessionID = new(); public static string? CurrentPassword { get => _currentPassword.Value; set => _currentPassword.Value = value; } public static string? CurrentSessionID { get => _currentSessionID.Value; set => _currentSessionID.Value = value; } } /// /// Patch HttpRouter.HandleRoute to capture session ID globally before deserialization happens. /// public class HttpRouterHandleRoutePatch : AbstractPatch { protected override MethodBase GetTargetMethod() { return typeof(HttpRouter).GetMethod("HandleRoute", BindingFlags.NonPublic | BindingFlags.Instance)!; } [PatchPrefix] public static void Prefix(MongoId sessionID) { // Capture the session ID for other patches (like profile filtering) AuthContext.CurrentSessionID = sessionID.ToString(); } } /// /// Patch LauncherCallbacks.Login to enforce database authentication. /// Returns true to let the original method execute and fetch the MongoId from memory. /// Returns false if DB verification fails, aborting the original method. /// public class LauncherCallbacksLoginPatch : AbstractPatch { protected override MethodBase GetTargetMethod() { return typeof(LauncherCallbacks).GetMethod(nameof(LauncherCallbacks.Login))!; } [PatchPrefix] public static bool Prefix(string url, LoginRequestData info, MongoId sessionID, ref ValueTask __result) { if (string.IsNullOrWhiteSpace(info.Username) || string.IsNullOrWhiteSpace(info.Password)) { __result = new ValueTask("FAILED"); return false; } if (!PersonalAuthMod.Instance!.DbManager.ValidateCredentials(info.Username, info.Password)) { Console.WriteLine($"[PersonalAuthMod] Login FAILED for user: {info.Username} (Invalid credentials via DB)"); __result = new ValueTask("FAILED"); return false; } Console.WriteLine($"[PersonalAuthMod] Login SUCCESS for user: {info.Username}, Validated by DB."); return true; } } /// /// Patch LauncherCallbacks.Register to enforce database registration. /// Returns true to let the original method execute and create the account in SaveServer. /// Returns false if DB registration fails (e.g. user exists). /// public class LauncherCallbacksRegisterPatch : AbstractPatch { protected override MethodBase GetTargetMethod() { return typeof(LauncherCallbacks).GetMethod(nameof(LauncherCallbacks.Register))!; } [PatchPrefix] public static bool Prefix(string url, RegisterData info, MongoId sessionID, ref ValueTask __result) { if (string.IsNullOrWhiteSpace(info.Username) || string.IsNullOrWhiteSpace(info.Password)) { __result = new ValueTask("FAILED"); return false; } if (!PersonalAuthMod.Instance!.DbManager.RegisterUser(info.Username, info.Password)) { Console.WriteLine($"[PersonalAuthMod] Register FAILED for user: {info.Username} (Already exists or DB error)"); __result = new ValueTask("FAILED"); return false; } Console.WriteLine($"[PersonalAuthMod] Register SUCCESS for user: {info.Username}"); return true; } }