Files
Nebula-Auth/SteamLibForked/Api/Mobile/SteamAuthenticatorLinkerApi.cs
T
Давид Чернопятов 1e65cd4a06 1.5.3 progress. Added multi-confirmation and code refactorings
NebulaAuth:
- Added MAAC and PortableMaClient for multi-confirmations
- Code clean-ups, removed unused classes, refactoring of old files
- SteamLib updated
- Localization and UI updates
- Dependcies updated to the last version

LegacyConverter:
 - Added mode to decrypt mafiles
2024-10-16 16:31:53 +03:00

305 lines
11 KiB
C#

using JetBrains.Annotations;
using Newtonsoft.Json.Linq;
using SteamLib.Authentication;
using SteamLib.Core;
using SteamLib.Core.Enums;
using SteamLib.Core.Interfaces;
using SteamLib.Core.StatusCodes;
using SteamLib.Exceptions;
using SteamLib.Exceptions.Mobile;
using SteamLib.ProtoCore;
using SteamLib.ProtoCore.Enums;
using SteamLib.ProtoCore.Exceptions;
using SteamLib.ProtoCore.Services;
using SteamLib.SteamMobile;
using SteamLib.SteamMobile.AuthenticatorLinker;
namespace SteamLib.Api.Mobile;
[PublicAPI]
public static class SteamAuthenticatorLinkerApi
{
private const string PHONE_REQ = SteamConstants.STEAM_COMMUNITY + "steamguard/phoneajax";
#region Global
public static Task<AccountPhoneStatus_Response> HasPhone(HttpClient client, string accessToken)
{
const string uri = SteamConstants.STEAM_API + "IPhoneService/AccountPhoneStatus/v1";
var reqUri = AddAccessToken(uri, accessToken);
return client.PostProto<AccountPhoneStatus_Response>(reqUri, request: null);
}
/// <summary>
///
/// </summary>
/// <param name="client"></param>
/// <param name="data"></param>
/// <param name="deviceId"></param>
/// <returns></returns>
/// <exception cref="AuthenticatorLinkerException"></exception>
/// <exception cref="EResultException"></exception>
public static async Task<AddAuthenticator_Response> LinkRequest(HttpClient client, ISessionData data, string deviceId)
{
const string uri = SteamConstants.STEAM_API + "ITwoFactorService/AddAuthenticator/v1";
data.EnsureValidated();
var reqUri = AddAccessToken(uri, data.GetAccessToken());
var req = new AddAuthenticator_Request
{
SteamId = (ulong)data.SteamId.Steam64.Id,
AuthenticatorType = 1,
DeviceIdentifier = deviceId,
Version = 2
};
var resp = await client.PostProtoMsg<AddAuthenticator_Response>(reqUri, req);
if (resp is { Result: EResult.InvalidState, ResponseMsg.Status: 2 })
{
throw new AuthenticatorLinkerException(AuthenticatorLinkerError.InvalidStateWithStatus2);
}
return resp.GetResponseEnsureSuccess();
}
public static async Task<LinkResult> FinalizeLink(HttpClient client, string confirmationCode, ISessionData data,
byte[] sharedSecret, bool validateSmsCode)
{
const string uri = SteamConstants.STEAM_API + "ITwoFactorService/FinalizeAddAuthenticator/v1";
data.EnsureValidated();
var reqUri = AddAccessToken(uri, data.GetAccessToken());
var time = await TimeAligner.GetSteamTimeAsync();
if (validateSmsCode)
{
var validateSmsReq = new FinalizeAddAuthenticator_Request
{
SteamId = data.SteamId.Steam64.ToUlong(),
AuthenticatorTime = (ulong)time,
ConfirmationCode = confirmationCode,
ValidateConfirmationCode = true
};
var validateResp = await client.PostProto<FinalizeAddAuthenticator_Response>(reqUri, validateSmsReq);
if (validateResp.Success == false || validateResp.Status != 2)
{
if (validateResp.Status == 89)
throw new AuthenticatorLinkerException(AuthenticatorLinkerError.BadConfirmationCode);
throw new AuthenticatorLinkerException("Can't accept sms code. Status: " + validateResp.Status);
}
}
var tries = 0;
while (tries < 30)
{
tries++;
time = await TimeAligner.GetSteamTimeAsync();
var code = SteamGuardCodeGenerator.GenerateCode(sharedSecret, time);
var req = new FinalizeAddAuthenticator_Request
{
SteamId = data.SteamId.Steam64.ToUlong(),
AuthenticatorCode = code,
AuthenticatorTime = (ulong)time,
ConfirmationCode = confirmationCode,
};
var resp = await client.PostProto<FinalizeAddAuthenticator_Response>(reqUri, req);
if (resp.Success && resp.WantMore == false)
{
return new LinkResult();
}
if (resp.Status == 89)
{
return new LinkResult(LinkError.BadConfirmationCode);
}
if (resp.Status == 88 && tries >= 30)
{
return new LinkResult(LinkError.UnableToGenerateCorrectCodes);
}
}
return new LinkResult(LinkError.GeneralFailure);
}
public static async Task<bool> IsValidPhoneNumber(HttpClient client, long phoneNumber, string sessionId)
{
return await CheckPhoneNumber(client, phoneNumber, sessionId) == CheckPhoneResult.Valid;
}
public static async Task<CheckPhoneResult> CheckPhoneNumber(HttpClient client, long phoneNumber, string sessionId)
{
var phone = '+' + phoneNumber.ToString();
var data = new Dictionary<string, string>
{
{"phoneNumber", phone},
{"sessionID", sessionId}
};
var reqContent = new FormUrlEncodedContent(data);
var req = new HttpRequestMessage(HttpMethod.Post, SteamConstants.STEAM_STORE + "phone/validate");
req.Content = reqContent;
req.Headers.Referrer = new Uri("https://store.steampowered.com/phone/add");
var resp = await client.SendAsync(req);
var content = await resp.EnsureSuccessStatusCode().Content.ReadAsStringAsync();
var j = JObject.Parse(content);
if (j["success"]!.Value<bool>() == false)
return CheckPhoneResult.GeneralFailure;
if (j["is_voip"]!.Value<bool>())
return CheckPhoneResult.VoIp;
return j["is_valid"]!.Value<bool>() ? CheckPhoneResult.Valid : CheckPhoneResult.NotValid;
}
public static async Task<bool> AttachPhone(HttpClient client, long phoneNumber, string accessToken)
{
const string uri = SteamConstants.STEAM_API + "IPhoneService/SetAccountPhoneNumber/v1";
var phone = '+' + phoneNumber.ToString();
var req = new SetAccountPhoneNumber_Request
{
PhoneNumber = phone
};
var reqUri = AddAccessToken(uri, accessToken);
var resp =
await client.PostProtoMsg<SetAccountPhoneNumber_Response>(reqUri, req);
return resp.Result == EResult.Pending;
}
public static async Task<bool> CheckEmailConfirmation(HttpClient client, string accessToken)
{
const string uri = SteamConstants.STEAM_API + "IPhoneService/IsAccountWaitingForEmailConfirmation/v1";
var reqUri = AddAccessToken(uri, accessToken);
var i = 0;
while (i < 5)
{
i++;
var resp = await client.PostProto<IsAccountWaitingForEmailConfirmation_Response>(reqUri, new EmptyMessage());
if (resp.IsWaiting == false) return true;
await Task.Delay(resp.SecondsToWait * 1000);
}
return false;
}
public static Task<EResult> SendSmsCode(HttpClient client, string accessToken)
{
const string uri = SteamConstants.STEAM_API + "IPhoneService/SendPhoneVerificationCode/v1";
var reqUri = AddAccessToken(uri, accessToken);
return client.PostProto(reqUri, new SendPhoneVerificationCode_Request());
}
public static async Task<bool> CheckSmsCode(HttpClient client, int smsCode, string sessionId)
{
var data = new Dictionary<string, string>
{
{"op", "check_sms_code"},
{"arg", smsCode.ToString()},
{"checkfortos", "0"},
{"skipvoip", "1"},
{"sessionid", sessionId}
};
var content = new FormUrlEncodedContent(data);
var resp = await client.PostAsync(PHONE_REQ, content);
var respContent = await resp.EnsureSuccessStatusCode().Content.ReadAsStringAsync();
var success = SteamStatusCode.Translate<SteamStatusCode>(respContent, out _).Equals(SteamStatusCode.Ok);
if (success) return true;
await Task.Delay(5000);
var hasPhone = await HasPhone(client, sessionId);
return hasPhone.HasPhone;
}
private static FormUrlEncodedContent CreateAjaxContent(string op, string arg, string sessionId)
{
var data = new Dictionary<string, string>
{
{"op",op},
{"arg", arg},
{"sessionid", sessionId}
};
return new FormUrlEncodedContent(data);
}
public static string GenerateDeviceId() => "android:" + Guid.NewGuid();
private static string AddAccessToken(string uri, string accessToken) => uri + "?access_token=" + accessToken;
#endregion
#region ForLinker
internal static Task<AddAuthenticator_Response> LinkRequest(this SteamAuthenticatorLinker linker)
{
if (linker.SessionData == null)
throw new InvalidOperationException("SessionData is null");
return LinkRequest(linker.Client, linker.SessionData, linker.DeviceId ??= GenerateDeviceId());
}
internal static Task<bool> IsValidPhoneNumber(this SteamAuthenticatorLinker linker, long phoneNumber)
{
if (linker.SessionData == null)
throw new InvalidOperationException("SessionData is null");
return IsValidPhoneNumber(linker.Client, phoneNumber, linker.SessionData.SessionId);
}
internal static Task<AccountPhoneStatus_Response> HasPhone(this SteamAuthenticatorLinker linker)
{
if (linker.SessionData == null)
throw new InvalidOperationException("SessionData is null");
return HasPhone(linker.Client, linker.SessionData.GetAccessToken());
}
internal static Task<bool> AttachPhone(this SteamAuthenticatorLinker linker, long phoneNumber)
{
if (linker.SessionData == null)
throw new InvalidOperationException("SessionData is null");
return AttachPhone(linker.Client, phoneNumber, linker.SessionData.GetAccessToken());
}
internal static Task<bool> CheckEmailConfirmation(this SteamAuthenticatorLinker linker)
{
if (linker.SessionData == null)
throw new InvalidOperationException("SessionData is null");
return CheckEmailConfirmation(linker.Client, linker.SessionData.GetAccessToken());
}
internal static Task<EResult> SendSmsCode(this SteamAuthenticatorLinker linker)
{
if (linker.SessionData == null)
throw new InvalidOperationException("SessionData is null");
return SendSmsCode(linker.Client, linker.SessionData.GetAccessToken());
}
internal static Task<bool> CheckSmsCode(this SteamAuthenticatorLinker linker, int smsCode)
{
if (linker.SessionData == null)
throw new InvalidOperationException("SessionData is null");
return CheckSmsCode(linker.Client, smsCode, linker.SessionData.SessionId);
}
internal static Task<LinkResult> FinalizeLink(this SteamAuthenticatorLinker linker, string confirmationCode, byte[] sharedSecret, bool validateSmsCode)
{
if (linker.SessionData == null)
throw new InvalidOperationException("SessionData is null");
return FinalizeLink(linker.Client, confirmationCode, linker.SessionData, sharedSecret, validateSmsCode);
}
#endregion
private static string GetAccessToken(this ISessionData s) => s.GetToken(SteamDomain.Community)?.Token ?? throw new SessionInvalidException("Access token was null. MobileEndpoints requires valid AccessToken");
}