mirror of
https://github.com/achiez/NebulaAuth-Steam-Desktop-Authenticator-by-Achies.git
synced 2026-07-28 23:59:35 +00:00
1.5.3 progress
- SessionHandler refactored. Now it used as global authorization API - Added semaphore to SessionHandler - WaitLogin dialog flow moved to SessionHandler - MaClient and PortableMaClient authorization logic now separeted and works correctly - Fixed App startup error was not localized and closed immediatly - Fixed code progress bar was freezing due to low Dispatcher priority - Fixed legacy serializer typo error in AccessToken field on write operation
This commit is contained in:
@@ -25,11 +25,12 @@ public partial class App
|
||||
var msg = ex.ToString();
|
||||
if (ex is CantAlignTimeException)
|
||||
{
|
||||
msg = Loc.Tr(LocManager.GetCodeBehind("CantAlignTimeError"));
|
||||
msg = LocManager.Get("CantAlignTimeError");
|
||||
}
|
||||
|
||||
MessageBox.Show(msg);
|
||||
MessageBox.Show(msg, "Error", MessageBoxButton.OK, MessageBoxImage.Stop, MessageBoxResult.OK, MessageBoxOptions.DefaultDesktopOnly);
|
||||
throw;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@ using System.Net.Http;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using AchiesUtilities.Web.Models;
|
||||
|
||||
namespace NebulaAuth.Model.MAAC;
|
||||
|
||||
@@ -80,7 +81,7 @@ public partial class PortableMaClient : ObservableObject, IDisposable
|
||||
}
|
||||
catch (ApplicationException ex)
|
||||
{
|
||||
Shell.Logger.Warn(ex, "Error GetConf in timer.");
|
||||
Shell.Logger.Warn(ex, "Timer {accountName}: Error GetConf in timer.", Mafile.AccountName);
|
||||
return 0;
|
||||
}
|
||||
var toConfirm = new List<Confirmation>();
|
||||
@@ -98,13 +99,14 @@ public partial class PortableMaClient : ObservableObject, IDisposable
|
||||
if (toConfirm.Count == 0) return 0;
|
||||
try
|
||||
{
|
||||
Shell.Logger.Debug("Sending confirmations. Count: {count}", toConfirm.Count);
|
||||
await HandleTimerRequest(() => SendConfirmations(toConfirm));
|
||||
Shell.Logger.Debug("Timer {accountName}: Sending confirmations. Count: {count}", Mafile.AccountName, toConfirm.Count);
|
||||
var success = await HandleTimerRequest(() => SendConfirmations(toConfirm));
|
||||
Shell.Logger.Debug("Timer {accountName}: Confirmation sent: {confirmResult}", Mafile.AccountName, success);
|
||||
return toConfirm.Count;
|
||||
}
|
||||
catch (ApplicationException ex)
|
||||
{
|
||||
Shell.Logger.Warn(ex, "MultiConf error in Timer.");
|
||||
Shell.Logger.Warn(ex, "Timer {accountName}: MultiConf error in Timer.", Mafile.AccountName);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@@ -125,7 +127,7 @@ public partial class PortableMaClient : ObservableObject, IDisposable
|
||||
Exception innerException;
|
||||
try
|
||||
{
|
||||
return await SessionHandler.Handle(func, Mafile, GetTimerPrefix());
|
||||
return await SessionHandler.Handle(func, Mafile, Chp(), GetTimerPrefix());
|
||||
}
|
||||
catch (OperationCanceledException ex)
|
||||
{
|
||||
@@ -144,6 +146,8 @@ public partial class PortableMaClient : ObservableObject, IDisposable
|
||||
throw new ApplicationException("Swallowed", innerException);
|
||||
}
|
||||
|
||||
private HttpClientHandlerPair Chp() => new(Client, ClientHandler);
|
||||
|
||||
private static string GetLocalization(string key)
|
||||
{
|
||||
return LocManager.GetCodeBehindOrDefault(key, LOC_PATH, key);
|
||||
|
||||
@@ -1,15 +1,11 @@
|
||||
using AchiesUtilities.Web.Proxy;
|
||||
using AchiesUtilities.Web.Models;
|
||||
using AchiesUtilities.Web.Proxy;
|
||||
using NebulaAuth.Model.Entities;
|
||||
using SteamLib.Account;
|
||||
using SteamLib.Api;
|
||||
using SteamLib.Api.Mobile;
|
||||
using SteamLib.Authentication;
|
||||
using SteamLib.Authentication.LoginV2;
|
||||
using SteamLib.Core.Enums;
|
||||
using SteamLib.Core.Interfaces;
|
||||
using SteamLib.Exceptions;
|
||||
using SteamLib.ProtoCore.Services;
|
||||
using SteamLib.SteamMobile;
|
||||
using SteamLib.SteamMobile.Confirmations;
|
||||
using SteamLib.Web;
|
||||
using System;
|
||||
@@ -30,6 +26,7 @@ public static class MaClient
|
||||
private static DynamicProxy Proxy { get; }
|
||||
|
||||
public static ProxyData? DefaultProxy { get; set; }
|
||||
public static HttpClientHandlerPair Chp => new(Client, ClientHandler);
|
||||
|
||||
static MaClient()
|
||||
{
|
||||
@@ -66,50 +63,18 @@ public static class MaClient
|
||||
return SteamMobileConfirmationsApi.GetConfirmations(Client, mafile, mafile.SessionData!.SteamId);
|
||||
}
|
||||
|
||||
public static async Task LoginAgain(Mafile mafile, string password, bool savePassword, ICaptchaResolver? resolver)
|
||||
public static Task LoginAgain(Mafile mafile, string password, bool savePassword, ICaptchaResolver? resolver)
|
||||
{
|
||||
SetProxy(mafile);
|
||||
var sgGenerator = new SteamGuardCodeGenerator(mafile.SharedSecret);
|
||||
var options = new LoginV2ExecutorOptions(LoginV2Executor.NullConsumer, Client)
|
||||
{
|
||||
Logger = Shell.ExtensionsLogger,
|
||||
SteamGuardProvider = sgGenerator,
|
||||
DeviceDetails = LoginV2ExecutorOptions.GetMobileDefaultDevice(),
|
||||
WebsiteId = "Mobile"
|
||||
};
|
||||
ClientHandler.CookieContainer.ClearMobileSessionCookies();
|
||||
var result = await LoginV2Executor.DoLogin(options, mafile.AccountName, password);
|
||||
AdmissionHelper.TransferCommunityCookies(ClientHandler.CookieContainer);
|
||||
mafile.SetSessionData((MobileSessionData)result);
|
||||
if (PHandler.IsPasswordSet)
|
||||
mafile.Password = (savePassword ? PHandler.Encrypt(password) : null);
|
||||
Storage.UpdateMafile(mafile);
|
||||
return SessionHandler.LoginAgain(Chp, mafile, password, savePassword);
|
||||
}
|
||||
|
||||
|
||||
public static async Task RefreshSession(Mafile mafile)
|
||||
public static Task RefreshSession(Mafile mafile)
|
||||
{
|
||||
ValidateMafile(mafile, true);
|
||||
SetProxy(mafile);
|
||||
var token = mafile.SessionData!.GetMobileToken();
|
||||
if (token == null || token.Value.IsExpired)
|
||||
{
|
||||
var sessionToken = await SteamMobileApi.RefreshJwt(Client, mafile.SessionData!.RefreshToken.Token, mafile.SessionData.SteamId.Steam64);
|
||||
var newToken = SteamTokenHelper.Parse(sessionToken);
|
||||
mafile.SessionData.SetMobileToken(newToken);
|
||||
}
|
||||
|
||||
//RETHINK: Do we need this? Mobile token is enough
|
||||
var communityToken = mafile.SessionData!.GetToken(SteamDomain.Community);
|
||||
if (communityToken == null || communityToken.Value.IsExpired)
|
||||
{
|
||||
var communityTokenString = await SteamGlobalApi.RefreshJwt(Client, SteamDomain.Community);
|
||||
var newToken = SteamTokenHelper.Parse(communityTokenString);
|
||||
mafile.SessionData.SetToken(SteamDomain.Community, newToken);
|
||||
}
|
||||
|
||||
Storage.UpdateMafile(mafile);
|
||||
ClientHandler.CookieContainer.SetSteamMobileCookiesWithMobileToken(mafile.SessionData);
|
||||
return SessionHandler.RefreshMobileToken(Chp, mafile);
|
||||
}
|
||||
|
||||
public static Task<bool> SendConfirmation(Mafile mafile, Confirmation confirmation, bool confirm)
|
||||
@@ -122,7 +87,7 @@ public static class MaClient
|
||||
public static Task<bool> SendMultipleConfirmation(Mafile mafile, IEnumerable<Confirmation> confirmations, bool confirm)
|
||||
{
|
||||
var enumerable = confirmations.ToList();
|
||||
if (!enumerable.Any())
|
||||
if (enumerable.Count == 0)
|
||||
{
|
||||
return Task.FromResult(result: false);
|
||||
}
|
||||
|
||||
@@ -1,24 +1,122 @@
|
||||
using NebulaAuth.Core;
|
||||
using AchiesUtilities.Web.Models;
|
||||
using MaterialDesignThemes.Wpf;
|
||||
using NebulaAuth.Core;
|
||||
using NebulaAuth.Model.Entities;
|
||||
using SteamLib.Exceptions;
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using NebulaAuth.View.Dialogs;
|
||||
|
||||
namespace NebulaAuth.Model;
|
||||
|
||||
public static class SessionHandler
|
||||
public static partial class SessionHandler
|
||||
{
|
||||
public static event EventHandler? LoginStarted;
|
||||
public static event EventHandler? LoginCompleted;
|
||||
|
||||
public static async Task<T> Handle<T>(Func<Task<T>> func, Mafile mafile, string? snackbarPrefix = null)
|
||||
private static readonly SemaphoreSlim Semaphore = new(1, 1);
|
||||
|
||||
public static async Task<T> Handle<T>(Func<Task<T>> func, Mafile mafile,
|
||||
HttpClientHandlerPair? chp = null, string? snackbarPrefix = null)
|
||||
{
|
||||
chp ??= MaClient.Chp;
|
||||
await Semaphore.WaitAsync();
|
||||
try
|
||||
{
|
||||
return await HandleInternal(func, chp.Value, mafile, snackbarPrefix);
|
||||
}
|
||||
finally
|
||||
{
|
||||
Semaphore.Release();
|
||||
}
|
||||
}
|
||||
|
||||
private static async Task<T> HandleInternal<T>(Func<Task<T>> func, HttpClientHandlerPair chp, Mafile mafile,
|
||||
string? snackbarPrefix = null)
|
||||
{
|
||||
using var scope = Shell.Logger.PushScopeProperty("Scope", "SessionHandler");
|
||||
var mobileTokenExpired = MobileTokenExpired(mafile);
|
||||
var refreshTokenExpired = RefreshTokenExpired(mafile);
|
||||
var password = GetPassword(mafile);
|
||||
|
||||
if (!mobileTokenExpired)
|
||||
{
|
||||
try
|
||||
{
|
||||
return await func();
|
||||
}
|
||||
catch (SessionInvalidException ex)
|
||||
when (refreshTokenExpired == false || password != null)
|
||||
{
|
||||
if (ex is SessionPermanentlyExpiredException)
|
||||
{
|
||||
Shell.Logger.Debug(ex, "RefreshToken on mafile {name} {steamid} is expired", mafile.AccountName, mafile.SessionData?.SteamId);
|
||||
refreshTokenExpired = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
Shell.Logger.Debug(ex, "MobileToken on mafile {name} {steamid} is expired", mafile.AccountName, mafile.SessionData?.SteamId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//State: mobileToken invalid/expired, refreshToken maybe not expired
|
||||
if (!refreshTokenExpired)
|
||||
{
|
||||
var refreshed = await RefreshInternal(chp, mafile);
|
||||
if (refreshed)
|
||||
{
|
||||
SnackbarController.SendSnackbar(snackbarPrefix + LocManager.GetCodeBehindOrDefault("SessionWasRefreshedAutomatically", "SessionHandler", "SessionWasRefreshedAutomatically"));
|
||||
try
|
||||
{
|
||||
return await func();
|
||||
}
|
||||
catch (SessionInvalidException)
|
||||
when (password != null)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Shell.Logger.Debug("Session on mafile {name} {steamid} is invalid/expired", mafile.AccountName, mafile.SessionData?.SteamId);
|
||||
|
||||
//State: mobileToken invalid/expired, refreshToken invalid/expired
|
||||
if (password != null)
|
||||
{
|
||||
var logged = await LoginAgainInternal(chp, mafile, password, true);
|
||||
if (logged)
|
||||
{
|
||||
Shell.Logger.Debug("Mafile {name} {steamid} was succesfully auto-relogined", mafile.AccountName, mafile.SessionData?.SteamId);
|
||||
return await func();
|
||||
}
|
||||
}
|
||||
|
||||
//Nothing to do more, everything is expired
|
||||
throw new SessionPermanentlyExpiredException(SessionPermanentlyExpiredException.SESSION_EXPIRED_MSG);
|
||||
}
|
||||
|
||||
|
||||
|
||||
private static bool MobileTokenExpired(Mafile mafile)
|
||||
{
|
||||
var mobileToken = mafile.SessionData?.GetMobileToken();
|
||||
return mobileToken == null || mobileToken.Value.IsExpired;
|
||||
}
|
||||
|
||||
private static bool RefreshTokenExpired(Mafile mafile)
|
||||
{
|
||||
return mafile.SessionData?.RefreshToken.IsExpired != false;
|
||||
}
|
||||
|
||||
private static string? GetPassword(Mafile mafile)
|
||||
{
|
||||
string? password = null;
|
||||
try
|
||||
{
|
||||
if (PHandler.IsPasswordSet && !string.IsNullOrWhiteSpace(mafile.Password))
|
||||
{
|
||||
password = PHandler.Decrypt(mafile.Password);
|
||||
return PHandler.Decrypt(mafile.Password);
|
||||
}
|
||||
}
|
||||
catch
|
||||
@@ -26,70 +124,74 @@ public static class SessionHandler
|
||||
// ignored
|
||||
}
|
||||
|
||||
var refreshed = false;
|
||||
try
|
||||
{
|
||||
return await func();
|
||||
}
|
||||
catch (SessionInvalidException) when (mafile.SessionData is { RefreshToken.IsExpired: false})
|
||||
{
|
||||
Shell.Logger.Debug("Token on mafile {name} {steamid} expired. Trying to refresh", mafile.AccountName, mafile.SessionData?.SteamId);
|
||||
refreshed = await TryRefresh(mafile);
|
||||
}
|
||||
catch (SessionInvalidException)
|
||||
when (password != null)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
if (refreshed)
|
||||
{
|
||||
Shell.Logger.Debug("Token on mafile {name} {steamid} refreshed", mafile.AccountName,
|
||||
mafile.SessionData?.SteamId);
|
||||
try
|
||||
{
|
||||
return await func();
|
||||
}
|
||||
catch (Exception ex3)
|
||||
when (password != null && ex3 is SessionPermanentlyExpiredException or SessionInvalidException)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (password == null)
|
||||
{
|
||||
throw new SessionInvalidException();
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
LoginStarted?.Invoke(null, EventArgs.Empty);
|
||||
await MaClient.LoginAgain(mafile, password, savePassword: true, null);
|
||||
Shell.Logger.Debug("Mafile {name} {steamid} succesfully auto-relogined", mafile.AccountName,
|
||||
mafile.SessionData?.SteamId);
|
||||
}
|
||||
finally
|
||||
{
|
||||
LoginCompleted?.Invoke(null, EventArgs.Empty);
|
||||
}
|
||||
|
||||
return await func();
|
||||
return null;
|
||||
}
|
||||
|
||||
private static async Task<bool> TryRefresh(Mafile mafile, string? snackbarPrefix = null)
|
||||
|
||||
private static async Task<bool> RefreshInternal(HttpClientHandlerPair chp, Mafile mafile)
|
||||
{
|
||||
try
|
||||
{
|
||||
await MaClient.RefreshSession(mafile);
|
||||
SnackbarController.SendSnackbar(snackbarPrefix + LocManager.GetCodeBehindOrDefault("SessionWasRefreshedAutomatically", "SessionHandler", "SessionWasRefreshedAutomatically"));
|
||||
await RefreshMobileToken(chp, mafile);
|
||||
return true;
|
||||
}
|
||||
catch (SessionInvalidException)
|
||||
catch(Exception ex)
|
||||
{
|
||||
Shell.Logger.Debug("Token on mafile {name} {steamid} not refreshed", mafile.AccountName, mafile.SessionData?.SteamId);
|
||||
Shell.Logger.Debug(ex, "Failed to refresh session on mafile {name} {steamid}", mafile.AccountName, mafile.SessionData?.SteamId);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static async Task<bool> LoginAgainInternal(HttpClientHandlerPair chp, Mafile mafile, string password,
|
||||
bool savePassword)
|
||||
{
|
||||
var t = Task.Run(OnLoginStarted);
|
||||
try
|
||||
{
|
||||
|
||||
await LoginAgain(chp, mafile, password, savePassword);
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Shell.Logger.Debug(ex, "Failed to relogin mafile {name} {steamid}", mafile.AccountName,
|
||||
mafile.SessionData?.SteamId);
|
||||
return false;
|
||||
}
|
||||
finally
|
||||
{
|
||||
OnLoginCompleted();
|
||||
await t;
|
||||
}
|
||||
}
|
||||
|
||||
private static async Task OnLoginStarted()
|
||||
{
|
||||
if (DialogHost.IsDialogOpen(null)) return;
|
||||
await Application.Current.Dispatcher.BeginInvoke(async () =>
|
||||
{
|
||||
await DialogHost.Show(new WaitLoginDialog());
|
||||
});
|
||||
}
|
||||
|
||||
private static void OnLoginCompleted()
|
||||
{
|
||||
var currentSession = DialogHost.GetDialogSession(null);
|
||||
Application.Current.Dispatcher.BeginInvoke(() =>
|
||||
{
|
||||
if (currentSession is { Content: WaitLoginDialog, IsEnded: false })
|
||||
{
|
||||
try
|
||||
{
|
||||
currentSession.Close();
|
||||
}
|
||||
catch
|
||||
{
|
||||
//Ignored
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
using AchiesUtilities.Web.Models;
|
||||
using NebulaAuth.Model.Entities;
|
||||
using SteamLib.Account;
|
||||
using SteamLib.Api.Mobile;
|
||||
using SteamLib.Authentication;
|
||||
using SteamLib.Authentication.LoginV2;
|
||||
using SteamLib.Exceptions;
|
||||
using SteamLib.SteamMobile;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace NebulaAuth.Model;
|
||||
|
||||
public partial class SessionHandler //API
|
||||
{
|
||||
public static async Task RefreshMobileToken(HttpClientHandlerPair chp, Mafile mafile)
|
||||
{
|
||||
if (mafile.SessionData is not { RefreshToken.IsExpired: false })
|
||||
throw new SessionPermanentlyExpiredException(SessionInvalidException.SESSION_NULL_MSG);
|
||||
|
||||
var mobileToken = await SteamMobileApi.RefreshJwt(chp.Client, mafile.SessionData.RefreshToken.Token, mafile.SessionData.SteamId);
|
||||
Shell.Logger.Info("MobileToken on {name} {steamid} successfully refreshed", mafile.AccountName, mafile.SessionData.SteamId);
|
||||
|
||||
var newToken = SteamTokenHelper.Parse(mobileToken);
|
||||
mafile.SessionData.SetMobileToken(newToken);
|
||||
mafile.SetSessionData(mafile.SessionData); //Trigger event
|
||||
Storage.UpdateMafile(mafile);
|
||||
chp.Handler.CookieContainer.SetSteamMobileCookiesWithMobileToken(mafile.SessionData);
|
||||
}
|
||||
|
||||
public static async Task LoginAgain(HttpClientHandlerPair chp, Mafile mafile, string password, bool savePassword)
|
||||
{
|
||||
var sgGenerator = new SteamGuardCodeGenerator(mafile.SharedSecret);
|
||||
var options = new LoginV2ExecutorOptions(LoginV2Executor.NullConsumer, chp.Client)
|
||||
{
|
||||
Logger = Shell.ExtensionsLogger,
|
||||
SteamGuardProvider = sgGenerator,
|
||||
DeviceDetails = LoginV2ExecutorOptions.GetMobileDefaultDevice(),
|
||||
WebsiteId = "Mobile",
|
||||
};
|
||||
chp.Handler.CookieContainer.ClearMobileSessionCookies();
|
||||
var result = await LoginV2Executor.DoLogin(options, mafile.AccountName, password);
|
||||
Shell.Logger.Info("Logged in again on {name} {steamid}", mafile.AccountName, result.SteamId);
|
||||
AdmissionHelper.TransferCommunityCookies(chp.Handler.CookieContainer);
|
||||
mafile.SetSessionData((MobileSessionData)result);
|
||||
if (PHandler.IsPasswordSet)
|
||||
mafile.Password = (savePassword ? PHandler.Encrypt(password) : null);
|
||||
Storage.UpdateMafile(mafile);
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using CommunityToolkit.Mvvm.Input;
|
||||
using JetBrains.Annotations;
|
||||
using MaterialDesignThemes.Wpf;
|
||||
using NebulaAuth.Core;
|
||||
using NebulaAuth.Model;
|
||||
@@ -12,7 +13,6 @@ using System;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Collections.Specialized;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace NebulaAuth.ViewModel;
|
||||
@@ -21,8 +21,9 @@ public partial class MainVM : ObservableObject
|
||||
{
|
||||
[ObservableProperty]
|
||||
private ObservableCollection<Mafile> _maFiles = Storage.MaFiles;
|
||||
public SnackbarMessageQueue MessageQueue => SnackbarController.MessageQueue;
|
||||
|
||||
[UsedImplicitly]
|
||||
public SnackbarMessageQueue MessageQueue => SnackbarController.MessageQueue;
|
||||
|
||||
|
||||
public Mafile? SelectedMafile
|
||||
@@ -42,8 +43,6 @@ public partial class MainVM : ObservableObject
|
||||
new MaProxy(kvp.Key, kvp.Value)));
|
||||
Storage.MaFiles.CollectionChanged += MaFilesOnCollectionChanged;
|
||||
QueryGroups();
|
||||
SessionHandler.LoginStarted += SessionHandlerOnLoginStarted;
|
||||
SessionHandler.LoginCompleted += SessionHandlerOnLoginCompleted;
|
||||
UpdateManager.CheckForUpdates();
|
||||
if (Storage.DuplicateFound > 0)
|
||||
{
|
||||
@@ -200,4 +199,11 @@ public partial class MainVM : ObservableObject
|
||||
Shell.Logger.Error(ex);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static string GetLocalization(string key)
|
||||
{
|
||||
const string LOC_PATH = "MainVM";
|
||||
return LocManager.GetCodeBehindOrDefault(key, LOC_PATH, key);
|
||||
}
|
||||
}
|
||||
@@ -30,7 +30,7 @@ public partial class MainVM
|
||||
private void UpdateCode(object? state = null)
|
||||
{
|
||||
var currentTime = TimeAligner.GetSteamTime();
|
||||
var untilChange = currentTime - currentTime / 30L * 30L;
|
||||
var untilChange = currentTime % 30;
|
||||
var codeProgress = untilChange / 30D * 100;
|
||||
|
||||
string? code = null;
|
||||
@@ -41,12 +41,12 @@ public partial class MainVM
|
||||
|
||||
|
||||
if (Application.Current == null) return;
|
||||
Application.Current.Dispatcher.Invoke((string? c) =>
|
||||
Application.Current.Dispatcher.BeginInvoke((string? c) =>
|
||||
{
|
||||
if (Application.Current.MainWindow?.WindowState == WindowState.Minimized) return;
|
||||
CodeProgress = codeProgress;
|
||||
if (c != null) Code = c;
|
||||
}, DispatcherPriority.Background, code);
|
||||
}, DispatcherPriority.DataBind, code);
|
||||
}
|
||||
|
||||
[RelayCommand(AllowConcurrentExecutions = false)]
|
||||
|
||||
@@ -1,47 +0,0 @@
|
||||
using MaterialDesignThemes.Wpf;
|
||||
using System;
|
||||
using System.Windows;
|
||||
using NebulaAuth.View.Dialogs;
|
||||
using NebulaAuth.Core;
|
||||
|
||||
namespace NebulaAuth.ViewModel;
|
||||
|
||||
public partial class MainVM //Other
|
||||
{
|
||||
|
||||
private const string LOC_PATH = "MainVM";
|
||||
|
||||
private static string GetLocalization(string key)
|
||||
{
|
||||
return LocManager.GetCodeBehindOrDefault(key, LOC_PATH, key);
|
||||
}
|
||||
|
||||
|
||||
private void SessionHandlerOnLoginCompleted(object? sender, EventArgs e)
|
||||
{
|
||||
var currentSession = DialogHost.GetDialogSession(null);
|
||||
Application.Current.Dispatcher.BeginInvoke(() =>
|
||||
{
|
||||
if (currentSession is { Content: WaitLoginDialog, IsEnded: false })
|
||||
{
|
||||
try
|
||||
{
|
||||
currentSession.Close();
|
||||
}
|
||||
catch
|
||||
{
|
||||
//Ignored
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private async void SessionHandlerOnLoginStarted(object? sender, EventArgs e)
|
||||
{
|
||||
if (DialogHost.IsDialogOpen(null)) return;
|
||||
await Application.Current.Dispatcher.BeginInvoke(async () =>
|
||||
{
|
||||
await DialogHost.Show(new WaitLoginDialog());
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -7,6 +7,7 @@ using SteamLib.ProtoCore.Enums;
|
||||
using SteamLib.ProtoCore.Exceptions;
|
||||
using SteamLib.ProtoCore.Services;
|
||||
using System.Net;
|
||||
using SteamLib.Account;
|
||||
|
||||
namespace SteamLib.Api.Mobile;
|
||||
|
||||
@@ -28,12 +29,12 @@ public static class SteamMobileApi
|
||||
/// <param name="cancellationToken"></param>
|
||||
/// <returns>Refreshed AccessToken</returns>
|
||||
/// <exception cref="SessionInvalidException"></exception>
|
||||
public static async Task<string> RefreshJwt(HttpClient client, string refreshToken, long steamId, CancellationToken cancellationToken = default)
|
||||
public static async Task<string> RefreshJwt(HttpClient client, string refreshToken, SteamId steamId, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var req = new GenerateAccessTokenForApp_Request
|
||||
{
|
||||
RefreshToken = refreshToken,
|
||||
SteamId = steamId,
|
||||
SteamId = steamId.Steam64,
|
||||
TokenRenewalType = true
|
||||
};
|
||||
|
||||
|
||||
@@ -28,12 +28,12 @@ public static class SteamMobileConfirmationsApi
|
||||
var reqMsg = new HttpRequestMessage(HttpMethod.Get, req);
|
||||
var resp = await client.SendAsync(reqMsg, cancellationToken);
|
||||
|
||||
var respStr = await resp.Content.ReadAsStringAsync(cancellationToken);
|
||||
|
||||
if (resp.StatusCode == HttpStatusCode.Redirect)
|
||||
{
|
||||
throw new SessionPermanentlyExpiredException("Mobile session expired");
|
||||
throw new SessionInvalidException("Mobile session expired");
|
||||
}
|
||||
|
||||
var respStr = await resp.Content.ReadAsStringAsync(cancellationToken);
|
||||
resp.EnsureSuccessStatusCode();
|
||||
|
||||
try
|
||||
@@ -41,7 +41,7 @@ public static class SteamMobileConfirmationsApi
|
||||
return MobileConfirmationScrapper.Scrap(respStr);
|
||||
}
|
||||
catch (Exception ex)
|
||||
when (ex is not (SessionPermanentlyExpiredException or CantLoadConfirmationsException))
|
||||
when (ex is not (SessionInvalidException or CantLoadConfirmationsException))
|
||||
{
|
||||
SteamLibErrorMonitor.LogErrorResponse(respStr, ex);
|
||||
throw;
|
||||
|
||||
@@ -79,7 +79,7 @@ public static class AdmissionHelper
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clear and set new session. Not recommended. Uses <see cref="IMobileSessionData.GetMobileToken()"/> for domain <see cref="SteamDomain.Community"/> instead of its own cookie. It's okay to use it only for confirmations. But Market, Trading and other pages won't be authenticated
|
||||
/// Clear and set new session. Not recommended. Uses <see cref="IMobileSessionData.GetMobileToken()"/> for domain <see cref="SteamDomain.Community"/> instead of its own cookie. It's okay to use it only for confirmations. But Market, Trading and other pages won't be authorized
|
||||
/// </summary>
|
||||
public static void SetSteamMobileCookiesWithMobileToken(this CookieContainer container, IMobileSessionData mobileSession,
|
||||
string setLanguage = "english")
|
||||
|
||||
@@ -52,7 +52,7 @@ public class SteamAuthenticatorLinker
|
||||
if (accessToken.Value.Type != SteamAccessTokenType.Mobile)
|
||||
Logger?.LogWarning("Provided access token is not of type Mobile. Actual type: {actualType}", accessToken.Value.Type);
|
||||
|
||||
var refreshed = await SteamMobileApi.RefreshJwt(Options.HttpClient, data.RefreshToken.Token, data.RefreshToken.SteamId.Steam64);
|
||||
var refreshed = await SteamMobileApi.RefreshJwt(Options.HttpClient, data.RefreshToken.Token, data.RefreshToken.SteamId);
|
||||
accessToken = SteamTokenHelper.Parse(refreshed);
|
||||
data.SetMobileToken(accessToken.Value);
|
||||
}
|
||||
|
||||
@@ -63,7 +63,7 @@ public static partial class MafileSerializer //Write
|
||||
? null
|
||||
: new
|
||||
{
|
||||
AccesToken = ext.SessionData?.MobileToken?.Token,
|
||||
AccessToken = ext.SessionData?.MobileToken?.Token,
|
||||
steamLoginSecure = ext.SessionData?.MobileToken?.SignedToken,
|
||||
RefreshToken = ext.SessionData?.RefreshToken.Token,
|
||||
SteamID = ext.SessionData?.SteamId.Steam64.Id,
|
||||
|
||||
@@ -14,6 +14,14 @@ public static class MobileConfirmationScrapper
|
||||
{"You are not set up to receive mobile confirmations", LoadConfirmationsError.NotSetupToReceiveConfirmations}
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="response"></param>
|
||||
/// <returns></returns>
|
||||
/// <exception cref="UnsupportedResponseException"></exception>
|
||||
/// <exception cref="SessionInvalidException"></exception>
|
||||
/// <exception cref="CantLoadConfirmationsException"></exception>
|
||||
public static List<Confirmation> Scrap(string response)
|
||||
{
|
||||
ConfirmationsJson conf;
|
||||
@@ -28,7 +36,7 @@ public static class MobileConfirmationScrapper
|
||||
|
||||
if (conf.NeedAuth)
|
||||
{
|
||||
throw new SessionPermanentlyExpiredException();
|
||||
throw new SessionInvalidException();
|
||||
}
|
||||
|
||||
if (conf.Success == false)
|
||||
|
||||
@@ -74,10 +74,13 @@
|
||||
- UPDATE: Added hyperlink to troubleshooting guide in "Linking" window <br />
|
||||
- UPDATE: Added socks4, socks5 proxy support (not tested) <br />
|
||||
- FIX: Now errors in receiving confirmations are localized and displayed correctly. <br/>
|
||||
- FIX: Fixed typo in "AccesToken" when serializing in legacy mode <br/>
|
||||
- FIX: Fixed application startup error was not shown correctly and had empty message when app wasn't able to align time <br/>
|
||||
- UI: Added "Copy RCode" button after linking account <br />
|
||||
- UI: Auto-confirm timers now use icons instead of text-chips <br />
|
||||
- UI: Minor design and localization improvements <br />
|
||||
- UI: Hyperlink "by achies" now leads to repository instead of "achiez/" user page<br />
|
||||
- UI: Fixed code progress bar was "lagging" <br/>
|
||||
- DEV: Big code clean-ups and refactoring <br />
|
||||
- DEV: Updated dependency and SteamLib libraries <br />
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user