Files
Nebula-Auth/SteamLibForked/Authentication/SteamTokenHelper.cs
T
2024-02-01 01:21:56 +02:00

184 lines
6.4 KiB
C#

using AchiesUtilities.Models;
using Microsoft.IdentityModel.JsonWebTokens;
using SteamLib.Account;
using SteamLib.Core.Enums;
using System.Collections.ObjectModel;
using System.Net.Http.Headers;
using System.Text.RegularExpressions;
namespace SteamLib.Authentication;
public static class SteamTokenHelper
{
public static readonly Regex SteamTokenRegex =
new(@"^(?<steamIdSignature>(?<steamId>7\d{16,})(?:%7C%7C))?(?<jwt>.+)$", RegexOptions.Compiled);
public static readonly IReadOnlyDictionary<string, SteamDomain> AudDomains =
new ReadOnlyDictionary<string, SteamDomain>(new Dictionary<string, SteamDomain>
{
{"web:community", SteamDomain.Community},
{"web:store", SteamDomain.Store},
{"web:help", SteamDomain.Help},
{"web:steamtv", SteamDomain.TV},
{"web:checkout", SteamDomain.Checkout},
});
public static bool TryParse(string input, out SteamAuthToken result)
{
return Parse(input, out result, true);
}
public static SteamAuthToken Parse(string input)
{
if (Parse(input, out var result, false))
{
return result;
}
else
{
throw new ArgumentException("Provided Token is not valid JWT token", nameof(input));
}
}
private static bool Parse(string input, out SteamAuthToken result, bool trying)
{
result = default;
var match = SteamTokenRegex.Match(input);
if (match.Success == false)
{
if (trying) return false;
throw new ArgumentException("Provided Token is not valid SteamLoginSecure or SteamAccess token");
}
var jwtStr = match.Groups["jwt"].Value;
JsonWebToken jwt;
try
{
jwt = new JsonWebToken(jwtStr);
}
catch (Exception e)
{
if (trying) return false;
throw new ArgumentException("Provided Token is not valid JWT token", nameof(input), e);
}
var steamIdStr = jwt.Subject;
if (steamIdStr == null || long.TryParse(steamIdStr, out var steamId) == false || steamId < SteamId64.SEED)
{
if (trying) return false;
throw new ArgumentException(
$"Provided Token is not valid JWT token. Subject claim is null or not valid SteamId '{steamIdStr}'",
nameof(input));
}
SteamId id;
try
{
id = SteamId.FromSteam64(steamId);
}
catch (Exception ex)
{
if (trying) return false;
throw new ArgumentException(
$"Provided Token is not valid JWT token. Subject is claim not valid SteamId '{steamIdStr}'",
nameof(input), ex);
}
var exp = jwt.ValidTo;
if (exp == DateTime.MinValue)
{
if (trying)
return false;
throw new ArgumentException("Provided Token has no 'exp' claim.", nameof(input));
}
var expires = UnixTimeStamp.FromDateTime(exp);
var domain = SteamDomain.Undefined;
var audiences = jwt.Audiences.ToList();
var tokenType = SteamAccessTokenType.Unknown;
if (jwt.Audiences.ToList().Count > 0)
{
var aud = audiences[0];
if (AudDomains.TryGetValue(aud, out domain) == false && aud != "web")
{
if (trying)
{
result = new SteamAuthToken(jwtStr, id, expires, domain, tokenType);
return false;
}
throw new ArgumentException($"Provided Token has invalid 'aud' claim. Value: {aud}", nameof(input));
}
tokenType = SteamAccessTokenType.AccessToken;
if (aud == "web")
{
var isMobile = audiences.Any(a => a.Equals("mobile"));
var isRefresh = audiences.Any(a => a.Equals("renew"));
if (isMobile ^ isRefresh)
{
tokenType = isMobile ? SteamAccessTokenType.Mobile : SteamAccessTokenType.Refresh;
}
else if (isMobile && isRefresh)
{
tokenType = SteamAccessTokenType.MobileRefresh;
}
else if (audiences.Count == 1)
{
tokenType = SteamAccessTokenType.Web;
}
else
{
if (trying)
{
result = new SteamAuthToken(jwtStr, id, expires, domain, SteamAccessTokenType.Unknown);
return false;
}
var auds = string.Join(", ", audiences);
throw new ArgumentException($"Provided Token has invalid 'aud' claim combination. Values: {auds}", nameof(input));
}
}
}
result = new SteamAuthToken(jwtStr, id, expires, domain, tokenType);
return true;
}
public static string ExtractJwtFromSetCookiesHeader(HttpHeaders headers)
{
var setCookies = headers.GetValues("Set-Cookie");
var steamLoginSecureHeader = setCookies.FirstOrDefault(x => x.StartsWith("steamLoginSecure="));
if (steamLoginSecureHeader == null) throw new ArgumentException("Can't find steamLoginSecure cookie in Set-Cookie header");
var firstCookiePart = steamLoginSecureHeader
.ToCharArray()
.SkipWhile(ch => !ch.Equals('='))
.Skip(1)
.TakeWhile(ch => !ch.Equals(';'))
.ToArray();
var steamLoginSecure = new string(firstCookiePart);
return ExtractJwtToken(steamLoginSecure);
}
public static string CombineJwtWithSteamId(long steamId, string loginValue) => steamId + "%7C%7C" + loginValue;
internal static bool CheckIfProperLoginValue(string value) => value.Contains("%7C%7C");
internal static string CombineLoginValueIfNeeded(long steamId, string loginValue)
{
return CheckIfProperLoginValue(loginValue) == false ? CombineJwtWithSteamId(steamId, loginValue) : loginValue;
}
public static string ExtractJwtToken(string steamLoginSecure)
{
var match = SteamTokenRegex.Match(steamLoginSecure);
if (match.Success == false) throw new ArgumentException("Can't extract JWT Access Token from steamLoginSecure");
return match.Groups["jwt"].Value;
}
}