Files
Radicade/Server/Auth/QuestRememberedSession.cs
2026-10-16 23:33:13 -07:00

52 lines
2.3 KiB
C#

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<long?> 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 ?? "")));
}