From fa70ed65cc4f8183f22a435cb0e241ddea779a53 Mon Sep 17 00:00:00 2001 From: Patrick Fic Date: Mon, 18 Jan 2021 11:25:24 -0800 Subject: [PATCH] Added automatic login using auth tokens IO-403 --- BodyshopUploader/App.config | 6 ++ .../Properties/Settings.Designer.cs | 26 ++++++++- BodyshopUploader/Properties/Settings.settings | 6 ++ BodyshopUploader/Utils/Auth.cs | 58 +++++++++++++++++-- BodyshopUploader/Utils/GraphQL.cs | 6 +- BodyshopUploader/Utils/JobProcessingQueue.cs | 2 +- BodyshopUploader/Utils/UiConverters.cs | 14 +++++ BodyshopUploader/ViewModels/LoginViewModel.cs | 31 +++++++++- .../ViewModels/MainViewModel.commands.cs | 1 + BodyshopUploader/Views/Login.xaml | 3 +- BodyshopUploader/Views/Login.xaml.cs | 5 -- 11 files changed, 140 insertions(+), 18 deletions(-) diff --git a/BodyshopUploader/App.config b/BodyshopUploader/App.config index 290b8b4..5434933 100644 --- a/BodyshopUploader/App.config +++ b/BodyshopUploader/App.config @@ -57,6 +57,12 @@ + + + + + + diff --git a/BodyshopUploader/Properties/Settings.Designer.cs b/BodyshopUploader/Properties/Settings.Designer.cs index ec865ae..33d57f1 100644 --- a/BodyshopUploader/Properties/Settings.Designer.cs +++ b/BodyshopUploader/Properties/Settings.Designer.cs @@ -12,7 +12,7 @@ namespace BodyshopPartner.Properties { [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "16.7.0.0")] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "16.8.1.0")] internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); @@ -104,5 +104,29 @@ namespace BodyshopPartner.Properties { this["QuickBooksFilePath"] = value; } } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("")] + public string AuthToken { + get { + return ((string)(this["AuthToken"])); + } + set { + this["AuthToken"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("")] + public string RefreshToken { + get { + return ((string)(this["RefreshToken"])); + } + set { + this["RefreshToken"] = value; + } + } } } diff --git a/BodyshopUploader/Properties/Settings.settings b/BodyshopUploader/Properties/Settings.settings index 4bc4dda..5913e1a 100644 --- a/BodyshopUploader/Properties/Settings.settings +++ b/BodyshopUploader/Properties/Settings.settings @@ -23,5 +23,11 @@ + + + + + + \ No newline at end of file diff --git a/BodyshopUploader/Utils/Auth.cs b/BodyshopUploader/Utils/Auth.cs index b75f9ca..8d3d684 100644 --- a/BodyshopUploader/Utils/Auth.cs +++ b/BodyshopUploader/Utils/Auth.cs @@ -12,15 +12,57 @@ namespace BodyshopPartner.Utils { private static NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger(); public static FirebaseAuthLink authlink; - static FirebaseAuthProvider ap = new FirebaseAuthProvider(new FirebaseConfig(Utils.AppMetaData.FirebaseAPIKey)); + static FirebaseAuthProvider ap = new FirebaseAuthProvider(new FirebaseConfig(Utils.AppMetaData.FirebaseAPIKey)); private static Timer tokenTimer = new Timer(); private static string U; private static string P; - + public async static Task AuthTest() { - await ap.RefreshAuthAsync( new FirebaseAuth() { }); + await ap.RefreshAuthAsync(new FirebaseAuth() { }); + } + + public async static Task<(bool, string)> AutoLogin(string AuthToken, string RefreshToken) + { + try + { + authlink = await new FirebaseAuthLink(ap, new FirebaseAuth() { FirebaseToken = AuthToken, RefreshToken = RefreshToken, }).GetFreshAuthAsync(); + await authlink.RefreshUserDetails(); + authlink.FirebaseAuthRefreshed += Authlink_FirebaseAuthRefreshed; + + logger.Trace("Firebase Auth Token {0}.", authlink.FirebaseToken); + logger.Trace("Firebase Auth Token expires in {0} seconds.", authlink.ExpiresIn); + tokenTimer.Interval = (authlink.ExpiresIn - 600) * 1000; //Set the token to refresh 10 minutes before it has to. + logger.Trace("Refresh timer interval set to {0}ms", (authlink.ExpiresIn - 600) * 1000); + tokenTimer.Elapsed += TokenTimer_Tick; + tokenTimer.Start(); + return (true, null); + } + catch (FirebaseAuthException Ex) + { + switch (Ex.Reason) + { + case AuthErrorReason.WrongPassword: + logger.Error("Incorrect password provided for user."); + return (false, Properties.Resources.Error_Login); + + case AuthErrorReason.UnknownEmailAddress: + logger.Error("User does not exist."); + return (false, Properties.Resources.Error_Login); + case AuthErrorReason.TooManyAttemptsTryLater: + logger.Error("Too many attempts logging in."); + return (false, Properties.Resources.Error_Login_Attempts); + default: + logger.Error(Ex, "Unknown error occured while logging in. {0}", Ex.Reason); + return (false, Properties.Resources.Error_Login_Unknown); + } + } + catch (Exception Ex) + { + logger.Error(Ex, "Unknown error encountered while obtaining auth token."); + return (false, Properties.Resources.Error_Generic); + } } public async static Task<(bool, string)> LoginAsync(string Username, string Password) @@ -31,8 +73,12 @@ namespace BodyshopPartner.Utils { authlink = await ap.SignInWithEmailAndPasswordAsync(Username, Password); authlink.FirebaseAuthRefreshed += Authlink_FirebaseAuthRefreshed; - - + + Properties.Settings.Default.AuthToken = authlink.FirebaseToken; + Properties.Settings.Default.RefreshToken = authlink.RefreshToken; + Properties.Settings.Default.Save(); + + logger.Trace("Firebase Auth Token {0}.", authlink.FirebaseToken); logger.Trace("Firebase Auth Token expires in {0} seconds.", authlink.ExpiresIn); @@ -73,7 +119,7 @@ namespace BodyshopPartner.Utils { logger.Trace("Old Token {0}", authlink.RefreshToken); await authlink.GetFreshAuthAsync(); - + authlink = await ap.RefreshAuthAsync(authlink); logger.Trace("new Token {0}", authlink.FirebaseToken); } diff --git a/BodyshopUploader/Utils/GraphQL.cs b/BodyshopUploader/Utils/GraphQL.cs index 29a6e77..674ccbe 100644 --- a/BodyshopUploader/Utils/GraphQL.cs +++ b/BodyshopUploader/Utils/GraphQL.cs @@ -13,7 +13,7 @@ namespace BodyshopPartner.Utils public static class GraphQL { private static NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger(); - + public static GraphQLHttpClient CreateGQLClient() { var graphQLClient = new GraphQLHttpClient(AppMetaData.graphQlEndpoint, new NewtonsoftJsonSerializer()); @@ -31,7 +31,7 @@ namespace BodyshopPartner.Utils var graphQLResponse = await g.SendQueryAsync(r); if (graphQLResponse.Errors == null) { - + //logger.Trace("GQL Response: {0}", graphQLResponse.Data); return graphQLResponse.Data; } @@ -42,7 +42,7 @@ namespace BodyshopPartner.Utils logger.Error("Error executing query."); Array.ForEach(graphQLResponse.Errors, x => { - logger.Error(x.Message); + logger.Error("Graphql Error: " + x.Message); if (x.Message.Contains("JWTExpired")) { jwtExpired = true; diff --git a/BodyshopUploader/Utils/JobProcessingQueue.cs b/BodyshopUploader/Utils/JobProcessingQueue.cs index a5ecb79..0848fbe 100644 --- a/BodyshopUploader/Utils/JobProcessingQueue.cs +++ b/BodyshopUploader/Utils/JobProcessingQueue.cs @@ -184,7 +184,7 @@ namespace BodyshopPartner.Utils catch (Exception Ex) { - logger.Error("Job insert failed. Show notification"); + logger.Error("Job insert failed. Show notification " + Ex.ToString()); //Succesful upsert App.Current.Dispatcher.Invoke(() => { diff --git a/BodyshopUploader/Utils/UiConverters.cs b/BodyshopUploader/Utils/UiConverters.cs index e390171..7e74dda 100644 --- a/BodyshopUploader/Utils/UiConverters.cs +++ b/BodyshopUploader/Utils/UiConverters.cs @@ -25,6 +25,20 @@ namespace BodyshopPartner.Utils } } + public class InvertBoolConverter : IValueConverter + { + public object Convert(object value, Type targetType, object parameter, CultureInfo culture) + { + return !((bool)value); + + } + + public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) + { + return ((bool)value); + } + } + public class NullVisibilityConverter : IValueConverter { public object Convert(object value, Type targetType, object parameter, CultureInfo culture) diff --git a/BodyshopUploader/ViewModels/LoginViewModel.cs b/BodyshopUploader/ViewModels/LoginViewModel.cs index e16bd11..a56df03 100644 --- a/BodyshopUploader/ViewModels/LoginViewModel.cs +++ b/BodyshopUploader/ViewModels/LoginViewModel.cs @@ -6,6 +6,7 @@ using System.Text; using System.Threading.Tasks; using System.Windows; + namespace BodyshopPartner.ViewModels { public partial class LoginViewModel : BaseViewModel @@ -15,6 +16,34 @@ namespace BodyshopPartner.ViewModels public LoginViewModel() { logger.Trace("Login VM Created."); + + AutoLogin(); + + + } + + private async Task AutoLogin() + { + Loading = true; + string AuthToken = Properties.Settings.Default.AuthToken; + string RefreshToken = Properties.Settings.Default.RefreshToken; + + + if (!String.IsNullOrEmpty(AuthToken) && !String.IsNullOrEmpty(RefreshToken)) + { + //Attempt auto login. + logger.Debug("Good to attempt auto login."); + (Error, ErrorMsg) = await Utils.Auth.AutoLogin(AuthToken, RefreshToken); + if (ErrorMsg == null) + { + App.Current.MainWindow.Hide(); + Views.Main m = new Views.Main(); + m.Show(); + + } + + } + Loading = false; } private async Task LoginAsync(Window W) @@ -25,7 +54,7 @@ namespace BodyshopPartner.ViewModels logger.Trace("Attempting to login as user: {0}", UserName); (Error, ErrorMsg) = await Utils.Auth.LoginAsync(UserName, Utils.LoginHelpers.DecodePassword(UserPassword)); - if(ErrorMsg ==null) + if (ErrorMsg == null) { Views.Main m = new Views.Main(); m.Show(); diff --git a/BodyshopUploader/ViewModels/MainViewModel.commands.cs b/BodyshopUploader/ViewModels/MainViewModel.commands.cs index 2f3fb3a..3b632c8 100644 --- a/BodyshopUploader/ViewModels/MainViewModel.commands.cs +++ b/BodyshopUploader/ViewModels/MainViewModel.commands.cs @@ -148,6 +148,7 @@ namespace BodyshopPartner.ViewModels async p => { await InstallUpdates(); + UpdateAvailable = false; }); } return _installUpdatesCommand; diff --git a/BodyshopUploader/Views/Login.xaml b/BodyshopUploader/Views/Login.xaml index 90dd0c5..3d946ba 100644 --- a/BodyshopUploader/Views/Login.xaml +++ b/BodyshopUploader/Views/Login.xaml @@ -27,7 +27,7 @@ - +