using System.IdentityModel.Tokens.Jwt; using System.Security.Claims; using System.Text; using System.Security.Cryptography; using Microsoft.EntityFrameworkCore; using Microsoft.IdentityModel.Tokens; using RadicadeServer.DB; namespace RadicadeServer.Auth; internal static class QuestRememberedSession { internal const string Header = "X-Radicade-Quest-Session"; internal static async Task ValidateAsync(RecDBContext db, string token, string deviceId, long requestedAccount) { if (string.IsNullOrWhiteSpace(token) || token.Length > 8192) return null; try { var handler = new JwtSecurityTokenHandler { MapInboundClaims = false }; var principal = handler.ValidateToken(token, new TokenValidationParameters { ValidateIssuerSigningKey = true, IssuerSigningKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(DBConfig.JwtKey)), ValidAlgorithms = new[] { SecurityAlgorithms.HmacSha256 }, ValidateIssuer = true, ValidIssuer = DBConfig.TokenIssuer, ValidateAudience = false, ValidateLifetime = true, RequireExpirationTime = true, ClockSkew = TimeSpan.Zero }, out _); string? Claim(string name) => principal.FindFirst(name)?.Value; if (Claim("client_id") != "recroom" || Claim("rn.plat") != "7" || Claim("rn.platid") != "radicade-quest:" + deviceId || !long.TryParse(Claim("userId"), out var account) || account <= 0 || (requestedAccount > 0 && requestedAccount != account) || !int.TryParse(Claim("tver"), out var version)) return null; var player = await db.Players.AsNoTracking().FirstOrDefaultAsync(x => x.AccountId == account); return player != null && player.TokenVersion == version && Claim("q.pwd") == PasswordVersion(player.Hashword) ? account : null; } catch (Exception ex) when (ex is SecurityTokenException or ArgumentException) { return null; } } internal static string PasswordVersion(string? hash) => Convert.ToHexString(SHA256.HashData(Encoding.UTF8.GetBytes(hash ?? ""))); }