mirror of
https://github.com/JogmansonReal/Radicade.git
synced 2026-10-26 07:29:00 -07:00
125 lines
5.5 KiB
C#
125 lines
5.5 KiB
C#
using System;
|
|
using System.Text.Json;
|
|
using BestHTTP;
|
|
using Il2CppInterop.Runtime;
|
|
|
|
internal static class HubSocketAuth
|
|
{
|
|
internal static string Token { get; private set; }
|
|
|
|
internal static string LastLoginRefusalCode { get; private set; }
|
|
|
|
private static readonly string[] AuthorizationHeaders = { "Authorization", "authorization", "AUTHORIZATION" };
|
|
|
|
private static readonly System.Collections.Generic.HashSet<string> Stated = new();
|
|
|
|
internal static bool IsServer(string url)
|
|
{
|
|
if (!Uri.TryCreate(url, UriKind.Absolute, out var request) ||
|
|
!Uri.TryCreate(RadicadeEacBypassPlugin.ServerUrl.Value, UriKind.Absolute, out var server)) return false;
|
|
return request.Host.Equals(server.Host, StringComparison.OrdinalIgnoreCase) && request.Port == server.Port &&
|
|
(request.Scheme == server.Scheme || request.Scheme == (server.Scheme == "https" ? "wss" : "ws"));
|
|
}
|
|
internal static void Capture(HTTPRequest request)
|
|
{
|
|
if (request?.Uri == null || !IsServer(request.Uri.ToString())) return;
|
|
foreach (var name in AuthorizationHeaders)
|
|
{
|
|
var values = request.GetHeaderValues(name);
|
|
if (values == null || values.Count == 0) continue;
|
|
var value = values[0];
|
|
if (value != null && value.StartsWith("Bearer ", StringComparison.OrdinalIgnoreCase))
|
|
Token = value.Substring(7).Trim();
|
|
}
|
|
}
|
|
internal static void CaptureFromLoginResponse(HTTPRequest request, string url)
|
|
{
|
|
if (!IsServer(url) || !new Uri(url).AbsolutePath.EndsWith("/connect/token", StringComparison.OrdinalIgnoreCase)) return;
|
|
Token = null;
|
|
var original = request.Callback;
|
|
request.Callback = DelegateSupport.ConvertDelegate<OnRequestFinishedDelegate>(
|
|
new Action<HTTPRequest, HTTPResponse>((req, response) =>
|
|
{
|
|
try
|
|
{
|
|
if (response != null && response.StatusCode == 200)
|
|
{
|
|
using var document = JsonDocument.Parse(response.DataAsText);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (document.RootElement.TryGetProperty("access_token", out var token) &&
|
|
token.ValueKind == JsonValueKind.String &&
|
|
!string.IsNullOrWhiteSpace(token.GetString()))
|
|
{
|
|
Token = token.GetString();
|
|
LastLoginRefusalCode = null;
|
|
Diag.Trace("[Realtime] Login token captured (value omitted)");
|
|
}
|
|
else
|
|
{
|
|
Token = null;
|
|
var error = document.RootElement.TryGetProperty("error", out var errorElement) ? errorElement.GetString() : null;
|
|
var description = document.RootElement.TryGetProperty("error_description", out var descriptionElement) ? descriptionElement.GetString() : null;
|
|
LastLoginRefusalCode = error;
|
|
RadicadeEacBypassPlugin.LogSource.LogWarning(
|
|
"[Realtime] connect/token answered HTTP 200 without an access_token - that is a login REFUSAL, not a login. " +
|
|
"error='" + (error ?? "-") + "', error_description='" + (description ?? "-") + "'.");
|
|
}
|
|
}
|
|
}
|
|
catch (Exception ex) { RadicadeEacBypassPlugin.LogSource.LogWarning("[Realtime] Token parse failed: " + ex.GetType().Name); }
|
|
finally { original?.Invoke(req, response); }
|
|
}));
|
|
}
|
|
internal static void AuthenticateHubRequest(HTTPRequest request, string url)
|
|
{
|
|
if (!IsServer(url) || !NameserverRedirect.IsHubUrl(url)) return;
|
|
if (!string.IsNullOrWhiteSpace(Token))
|
|
{
|
|
request.Uri = new Il2CppSystem.Uri(NameserverRedirect.EnsureHubToken(url, Token));
|
|
request.SetHeader("Authorization", "Bearer " + Token);
|
|
}
|
|
Diag.Trace("[Realtime] Hub request " + new Uri(url).AbsolutePath +
|
|
"; token=" + (NameserverRedirect.HasHubToken(request.Uri.ToString()) ? "present" : "missing"));
|
|
}
|
|
|
|
internal static void AuthenticateServerRequest(HTTPRequest request, string url)
|
|
{
|
|
if (request == null || !IsServer(url) || string.IsNullOrWhiteSpace(Token)) return;
|
|
try
|
|
{
|
|
if (IsAnonymousPath(new Uri(url).AbsolutePath)) return;
|
|
|
|
foreach (var name in AuthorizationHeaders)
|
|
{
|
|
var values = request.GetHeaderValues(name);
|
|
if (values != null && values.Count > 0) return;
|
|
}
|
|
|
|
request.SetHeader("Authorization", "Bearer " + Token);
|
|
var path = new Uri(url).AbsolutePath;
|
|
if (Stated.Add(path))
|
|
Diag.Trace("[Auth] stated the session token on " + path + " (the client sent none)");
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Diag.Warn("[Auth] could not state the token on " + url + ": " + ex.GetType().Name);
|
|
}
|
|
}
|
|
|
|
private static bool IsAnonymousPath(string path) =>
|
|
path.StartsWith("/auth/connect/token", StringComparison.OrdinalIgnoreCase) ||
|
|
path.StartsWith("/auth/cachedlogin", StringComparison.OrdinalIgnoreCase) ||
|
|
path.StartsWith("/auth/eac/", StringComparison.OrdinalIgnoreCase) ||
|
|
path.StartsWith("/images/", StringComparison.OrdinalIgnoreCase) ||
|
|
path.StartsWith("/cdn/", StringComparison.OrdinalIgnoreCase);
|
|
}
|