mirror of
https://github.com/DorkSquadRR/DorkNet.git
synced 2026-10-25 15:08:47 -07:00
Park Photon CustomAuth injector
- Photon dashboard has CustomAuth disabled on the AppId now, so the watch never needs to attach userid/LoginLock to its Authenticate ops. Removed the harmony hook on NetworkingPeer.CallAuthenticate, the AuthValuesInjector_Prefix method, the GetCurrentLockToken helper, and the InjectAuthValues Cfg field + config load. Bumped the [config] log line to drop the InjectAuthValues field. - Both dorknet-clientmod.json.template copies (source + local-releases) lose the "InjectAuthValues" key. - Code moved verbatim to DorkNet.ClientMod/attic/AuthValuesInjector.cs.attic with a header explaining why it was parked and how to restore it. The .cs.attic suffix keeps the SDK's auto-globber from compiling it.
This commit is contained in:
+12
-82
@@ -68,8 +68,9 @@ public class Mod : MelonMod
|
||||
// any /goto has returned. Default matches the server's
|
||||
// appsettings.json Photon:CloudRegion ("eu").
|
||||
public static string PhotonCloudRegion = "eu";
|
||||
public static bool InjectAuthValues = true;
|
||||
public static bool EnableTlsTrustBypass = true;
|
||||
// Photon Custom Auth injector was parked 2026-05-28; see
|
||||
// attic/AuthValuesInjector.cs.attic for the code + restore notes.
|
||||
}
|
||||
|
||||
public override void OnInitializeMelon()
|
||||
@@ -88,8 +89,8 @@ public class Mod : MelonMod
|
||||
RegisterNetworkPatches();
|
||||
TryPatchByName("PhotonNetwork", "ConnectUsingSettings",
|
||||
prefix: nameof(PhotonPatches.PhotonAppIdOverride_Prefix));
|
||||
TryPatchByName("NetworkingPeer", "CallAuthenticate",
|
||||
prefix: nameof(PhotonPatches.AuthValuesInjector_Prefix));
|
||||
// NetworkingPeer.CallAuthenticate hook (Photon Custom Auth
|
||||
// injector) parked — see attic/AuthValuesInjector.cs.attic.
|
||||
TryPatchByName("BestHTTP.SecureProtocol.Org.BouncyCastle.Crypto.Tls.ServerOnlyTlsAuthentication",
|
||||
"NotifyServerCertificate",
|
||||
prefix: nameof(TlsPatches.NotifyServerCertificate_Prefix));
|
||||
@@ -286,10 +287,11 @@ public class Mod : MelonMod
|
||||
if (r.TryGetProperty("PhotonAppId", out v)) Cfg.PhotonAppId = v.GetString() ?? Cfg.PhotonAppId;
|
||||
if (r.TryGetProperty("PhotonVoiceAppId", out v)) Cfg.PhotonVoiceAppId = v.GetString() ?? Cfg.PhotonVoiceAppId;
|
||||
if (r.TryGetProperty("PhotonCloudRegion", out v)) Cfg.PhotonCloudRegion = v.GetString() ?? Cfg.PhotonCloudRegion;
|
||||
if (r.TryGetProperty("InjectAuthValues", out v)) Cfg.InjectAuthValues = v.GetBoolean();
|
||||
if (r.TryGetProperty("EnableTlsTrustBypass", out v)) Cfg.EnableTlsTrustBypass = v.GetBoolean();
|
||||
// "InjectAuthValues" key in the template is now ignored —
|
||||
// see attic/AuthValuesInjector.cs.attic.
|
||||
Log.Msg($"[config] loaded: ServerHost={Cfg.ServerHost}, SingleOrigin={(string.IsNullOrEmpty(Cfg.SingleOriginBaseUrl) ? "<off>" : Cfg.SingleOriginBaseUrl)}, PhotonAppId={(string.IsNullOrEmpty(Cfg.PhotonAppId) ? "<unset>" : "<set>")}, " +
|
||||
$"InjectAuthValues={Cfg.InjectAuthValues}, EnableTlsTrustBypass={Cfg.EnableTlsTrustBypass}");
|
||||
$"EnableTlsTrustBypass={Cfg.EnableTlsTrustBypass}");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -745,83 +747,11 @@ internal static class PhotonPatches
|
||||
catch { return null; }
|
||||
}
|
||||
|
||||
public static void AuthValuesInjector_Prefix(object __instance)
|
||||
{
|
||||
if (!Mod.Cfg.InjectAuthValues) return;
|
||||
try
|
||||
{
|
||||
// Photon's AuthValues live on PhotonNetwork.AuthValues. The 2020
|
||||
// watch never sets them itself, so we set them right before
|
||||
// NetworkingPeer.CallAuthenticate forwards to the wire op. The
|
||||
// userid is RecNet.Core.LocalAccountId, the LoginLock comes
|
||||
// from PlayerPrefs key LoginLockTokenV2.
|
||||
// RecNet.Core.LocalAccountId is declared `int` in the game
|
||||
// build (dump.cs:573622 → 0x30 backing field). Convert via
|
||||
// Convert.ToInt64 so boxing as int still gives us the long we
|
||||
// need below; a direct (long) cast on boxed int would throw.
|
||||
var coreType = FindGameType("RecNet.Core");
|
||||
var accountIdObj = coreType?.GetProperty("LocalAccountId", BindingFlags.Public | BindingFlags.Static)?.GetValue(null);
|
||||
var accountId = accountIdObj is null ? 0L : Convert.ToInt64(accountIdObj);
|
||||
if (accountId <= 0)
|
||||
{
|
||||
Mod.Log.Msg("[auth-injector] skip — no LocalAccountId yet (pre-login Photon ping)");
|
||||
return;
|
||||
}
|
||||
|
||||
var photonNetwork = FindGameType("PhotonNetwork");
|
||||
var avProp = photonNetwork?.GetProperty("AuthValues", BindingFlags.Public | BindingFlags.Static);
|
||||
var existing = avProp?.GetValue(null);
|
||||
if (existing is not null)
|
||||
{
|
||||
var userIdProp = existing.GetType().GetProperty("UserId");
|
||||
var existingUserId = userIdProp?.GetValue(existing) as string;
|
||||
if (!string.IsNullOrEmpty(existingUserId)) return;
|
||||
}
|
||||
|
||||
var token = GetCurrentLockToken();
|
||||
|
||||
// Build a new AuthenticationValues. The type lives in the
|
||||
// ExitGames.Client.Photon.LoadBalancing namespace (game-side).
|
||||
// CustomAuthenticationType.Custom = 0 in this build.
|
||||
var authValuesType = FindGameType("ExitGames.Client.Photon.LoadBalancing.AuthenticationValues");
|
||||
if (authValuesType is null) { Mod.Log.Warning("[auth-injector] AuthenticationValues type not found"); return; }
|
||||
var av = Activator.CreateInstance(authValuesType);
|
||||
var authTypeProp = authValuesType.GetProperty("AuthType");
|
||||
if (authTypeProp is not null)
|
||||
{
|
||||
var customAuthValue = authTypeProp.PropertyType.IsEnum
|
||||
? Enum.ToObject(authTypeProp.PropertyType, 0)
|
||||
: Convert.ChangeType(0, authTypeProp.PropertyType);
|
||||
authTypeProp.SetValue(av, customAuthValue);
|
||||
}
|
||||
authValuesType.GetProperty("UserId")?.SetValue(av, accountId.ToString());
|
||||
var addParam = authValuesType.GetMethod("AddAuthParameter", new[] { typeof(string), typeof(string) });
|
||||
addParam?.Invoke(av, new object[] { "userid", accountId.ToString() });
|
||||
addParam?.Invoke(av, new object[] { "LoginLock", token ?? string.Empty });
|
||||
|
||||
avProp?.SetValue(null, av);
|
||||
Mod.Log.Msg($"[auth-injector] set AuthValues userid={accountId} LoginLock={(string.IsNullOrEmpty(token) ? "<missing>" : "<set>")}");
|
||||
}
|
||||
catch (Exception ex) { Mod.Log.Warning($"[auth-injector] failed: {ex.Message}"); }
|
||||
}
|
||||
|
||||
private static string? GetCurrentLockToken()
|
||||
{
|
||||
try
|
||||
{
|
||||
var playerPrefs = FindGameType("UnityEngine.PlayerPrefs");
|
||||
var getString = playerPrefs?.GetMethod("GetString", new[] { typeof(string), typeof(string) });
|
||||
var raw = (string?)getString?.Invoke(null, new object[] { "LoginLockTokenV2", string.Empty });
|
||||
if (string.IsNullOrEmpty(raw)) return null;
|
||||
const string key = "\"LockToken\":\"";
|
||||
var ix = raw.IndexOf(key, StringComparison.Ordinal);
|
||||
if (ix < 0) return null;
|
||||
ix += key.Length;
|
||||
var end = raw.IndexOf('"', ix);
|
||||
return end < 0 ? null : raw[ix..end];
|
||||
}
|
||||
catch { return null; }
|
||||
}
|
||||
// AuthValuesInjector_Prefix + GetCurrentLockToken parked 2026-05-28
|
||||
// — see attic/AuthValuesInjector.cs.attic. Photon Custom Auth is
|
||||
// now disabled at the AppId level in the Photon dashboard, so the
|
||||
// watch no longer needs to attach userid+LoginLock to Photon
|
||||
// Authenticate ops.
|
||||
|
||||
private static Type? FindGameType(string name)
|
||||
{
|
||||
|
||||
@@ -0,0 +1,164 @@
|
||||
// ─────────────────────────────────────────────────────────────────────
|
||||
// AuthValuesInjector — PARKED 2026-05-28
|
||||
// ─────────────────────────────────────────────────────────────────────
|
||||
//
|
||||
// Filename suffix is .cs.attic on purpose so the .NET SDK's globber
|
||||
// doesn't pick it up. Don't rename to .cs unless you're putting the
|
||||
// feature back.
|
||||
//
|
||||
// What this used to do
|
||||
// --------------------
|
||||
// Prefix-patched NetworkingPeer.CallAuthenticate so the 2020 watch
|
||||
// would attach a Photon Custom-Auth payload (`userid` +
|
||||
// `LoginLock`) to every Photon Authenticate op. The server's
|
||||
// /photon/customauth controller (PhotonCustomAuthController.cs) read
|
||||
// those parameters and validated them against the player's
|
||||
// /player/login lock.
|
||||
//
|
||||
// Why it's parked
|
||||
// ---------------
|
||||
// Photon Cloud's Custom Auth probe was getting 403'd at the WAF in
|
||||
// front of `auth.<apex>/photon/customauth` for hosts behind
|
||||
// Cloudflare or Tunnelto's proxy. The Photon NameServer then returned
|
||||
// `CustomAuthenticationFailed (ReturnCode 32755)` and the watch
|
||||
// bounced back to the dorm before a session could start.
|
||||
//
|
||||
// The cleaner fix turned out to be:
|
||||
// 1. Turn off Custom Authentication on the Photon AppId in the
|
||||
// dashboard (or flip "Anonymous connect" to Allow), so Photon
|
||||
// never calls our URL.
|
||||
// 2. Stop sending custom-auth params from the client — that's the
|
||||
// InjectAuthValues=false flag we shipped first. With the
|
||||
// dashboard side off too, the injector code below has nothing
|
||||
// to gate on, so it's removed from the live patch.
|
||||
//
|
||||
// How to put it back
|
||||
// ------------------
|
||||
// 1. Rename this file to AuthValuesInjector.cs (drop the .attic
|
||||
// suffix). It compiles as part of DorkNet.ClientMod.
|
||||
// 2. Reinstate the `InjectAuthValues` field on Mod.Cfg + the
|
||||
// config-load line in LoadConfig.
|
||||
// 3. Re-add the harmony patch hook in OnLateInitializeMelon:
|
||||
// TryPatchByName("NetworkingPeer", "CallAuthenticate",
|
||||
// prefix: nameof(PhotonPatches.AuthValuesInjector_Prefix));
|
||||
// 4. Flip `"InjectAuthValues": true` in
|
||||
// DorkNet.ClientMod/dorknet-clientmod.json.template (and the
|
||||
// copy under local-releases/).
|
||||
// 5. If you also want the server to enforce the lock, set
|
||||
// Photon:RequireCustomAuth=true on the server (env
|
||||
// Photon__RequireCustomAuth=true).
|
||||
//
|
||||
// The body below is verbatim what lived in Mod.cs's PhotonPatches
|
||||
// class. `FindGameType` is the same helper still in Mod.cs.
|
||||
|
||||
using System.Reflection;
|
||||
|
||||
namespace DorkNet.ClientMod.Attic;
|
||||
|
||||
internal static class AuthValuesInjector
|
||||
{
|
||||
public static void AuthValuesInjector_Prefix(object __instance)
|
||||
{
|
||||
if (!Mod.Cfg.InjectAuthValues) return;
|
||||
try
|
||||
{
|
||||
// Photon's AuthValues live on PhotonNetwork.AuthValues. The 2020
|
||||
// watch never sets them itself, so we set them right before
|
||||
// NetworkingPeer.CallAuthenticate forwards to the wire op. The
|
||||
// userid is RecNet.Core.LocalAccountId, the LoginLock comes
|
||||
// from PlayerPrefs key LoginLockTokenV2.
|
||||
// RecNet.Core.LocalAccountId is declared `int` in the game
|
||||
// build (dump.cs:573622 → 0x30 backing field). Convert via
|
||||
// Convert.ToInt64 so boxing as int still gives us the long we
|
||||
// need below; a direct (long) cast on boxed int would throw.
|
||||
var coreType = FindGameType("RecNet.Core");
|
||||
var accountIdObj = coreType?.GetProperty("LocalAccountId", BindingFlags.Public | BindingFlags.Static)?.GetValue(null);
|
||||
var accountId = accountIdObj is null ? 0L : Convert.ToInt64(accountIdObj);
|
||||
if (accountId <= 0)
|
||||
{
|
||||
Mod.Log.Msg("[auth-injector] skip — no LocalAccountId yet (pre-login Photon ping)");
|
||||
return;
|
||||
}
|
||||
|
||||
var photonNetwork = FindGameType("PhotonNetwork");
|
||||
var avProp = photonNetwork?.GetProperty("AuthValues", BindingFlags.Public | BindingFlags.Static);
|
||||
var existing = avProp?.GetValue(null);
|
||||
if (existing is not null)
|
||||
{
|
||||
var userIdProp = existing.GetType().GetProperty("UserId");
|
||||
var existingUserId = userIdProp?.GetValue(existing) as string;
|
||||
if (!string.IsNullOrEmpty(existingUserId)) return;
|
||||
}
|
||||
|
||||
var token = GetCurrentLockToken();
|
||||
|
||||
// Build a new AuthenticationValues. The type lives in the
|
||||
// ExitGames.Client.Photon.LoadBalancing namespace (game-side).
|
||||
// CustomAuthenticationType.Custom = 0 in this build.
|
||||
var authValuesType = FindGameType("ExitGames.Client.Photon.LoadBalancing.AuthenticationValues");
|
||||
if (authValuesType is null) { Mod.Log.Warning("[auth-injector] AuthenticationValues type not found"); return; }
|
||||
var av = Activator.CreateInstance(authValuesType);
|
||||
var authTypeProp = authValuesType.GetProperty("AuthType");
|
||||
if (authTypeProp is not null)
|
||||
{
|
||||
var customAuthValue = authTypeProp.PropertyType.IsEnum
|
||||
? Enum.ToObject(authTypeProp.PropertyType, 0)
|
||||
: Convert.ChangeType(0, authTypeProp.PropertyType);
|
||||
authTypeProp.SetValue(av, customAuthValue);
|
||||
}
|
||||
authValuesType.GetProperty("UserId")?.SetValue(av, accountId.ToString());
|
||||
var addParam = authValuesType.GetMethod("AddAuthParameter", new[] { typeof(string), typeof(string) });
|
||||
addParam?.Invoke(av, new object[] { "userid", accountId.ToString() });
|
||||
addParam?.Invoke(av, new object[] { "LoginLock", token ?? string.Empty });
|
||||
|
||||
avProp?.SetValue(null, av);
|
||||
Mod.Log.Msg($"[auth-injector] set AuthValues userid={accountId} LoginLock={(string.IsNullOrEmpty(token) ? "<missing>" : "<set>")}");
|
||||
}
|
||||
catch (Exception ex) { Mod.Log.Warning($"[auth-injector] failed: {ex.Message}"); }
|
||||
}
|
||||
|
||||
private static string? GetCurrentLockToken()
|
||||
{
|
||||
try
|
||||
{
|
||||
var playerPrefs = FindGameType("UnityEngine.PlayerPrefs");
|
||||
var getString = playerPrefs?.GetMethod("GetString", new[] { typeof(string), typeof(string) });
|
||||
var raw = (string?)getString?.Invoke(null, new object[] { "LoginLockTokenV2", string.Empty });
|
||||
if (string.IsNullOrEmpty(raw)) return null;
|
||||
const string key = "\"LockToken\":\"";
|
||||
var ix = raw.IndexOf(key, StringComparison.Ordinal);
|
||||
if (ix < 0) return null;
|
||||
ix += key.Length;
|
||||
var end = raw.IndexOf('"', ix);
|
||||
return end < 0 ? null : raw[ix..end];
|
||||
}
|
||||
catch { return null; }
|
||||
}
|
||||
|
||||
// FindGameType is already defined inside Mod.cs's PhotonPatches
|
||||
// class (private static) — when re-instating, either drop this
|
||||
// private copy and inline the lookup, or move the helper to a
|
||||
// shared internal class. The current shape preserves the original
|
||||
// behaviour for an isolated paste-back.
|
||||
private static Type? FindGameType(string name)
|
||||
{
|
||||
foreach (var asm in AppDomain.CurrentDomain.GetAssemblies())
|
||||
{
|
||||
try
|
||||
{
|
||||
var t = asm.GetType(name, throwOnError: false);
|
||||
if (t is not null) return t;
|
||||
var prefixed = asm.GetType("Il2Cpp" + name, throwOnError: false);
|
||||
if (prefixed is not null) return prefixed;
|
||||
var dot = name.IndexOf('.');
|
||||
if (dot > 0)
|
||||
{
|
||||
var nsPrefixed = asm.GetType("Il2Cpp" + name.Substring(0, dot) + name.Substring(dot), throwOnError: false);
|
||||
if (nsPrefixed is not null) return nsPrefixed;
|
||||
}
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,5 @@
|
||||
"PhotonAppId": "{PHOTON_APPID}",
|
||||
"PhotonVoiceAppId": "{PHOTON_VOICE_APPID}",
|
||||
"PhotonCloudRegion": "{PHOTON_REGION}",
|
||||
"InjectAuthValues": false,
|
||||
"EnableTlsTrustBypass": true
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user