From 0bd530ab64b0818aad30be4642d137a76a4f1b85 Mon Sep 17 00:00:00 2001 From: smizrahi Date: Wed, 4 Jan 2023 17:37:46 +0100 Subject: [PATCH] feat(preferences): Improve TOTP - add validation code in preferences before saving to ensure user add qr code in totp application --- .../English.lproj/Localizable.strings | 2 + .../French.lproj/Localizable.strings | 2 + UI/PreferencesUI/UIxPreferences.m | 67 +++++++++++++++++++ UI/Templates/PreferencesUI/UIxPreferences.wox | 8 +++ .../js/Preferences/PreferencesController.js | 13 +++- 5 files changed, 91 insertions(+), 1 deletion(-) diff --git a/UI/PreferencesUI/English.lproj/Localizable.strings b/UI/PreferencesUI/English.lproj/Localizable.strings index 2b40ceb36..ac645b188 100644 --- a/UI/PreferencesUI/English.lproj/Localizable.strings +++ b/UI/PreferencesUI/English.lproj/Localizable.strings @@ -485,6 +485,8 @@ "Enable two-factor authentication using a TOTP application" = "Enable two-factor authentication using a TOTP application"; "You must enter this key into your TOTP application." = "You must enter this key into your TOTP application."; "If you do not and you log out you will not be able to login again." = "If you do not and you log out you will not be able to login again."; +"Enter TOTP verification code :" = "Enter TOTP verification code :"; +"Invalid TOTP verification code" = "Invalid TOTP verification code"; /* External Sieve scripts */ "An external Sieve script is active" = "An external Sieve script is active"; diff --git a/UI/PreferencesUI/French.lproj/Localizable.strings b/UI/PreferencesUI/French.lproj/Localizable.strings index 2e0c31915..48096027a 100644 --- a/UI/PreferencesUI/French.lproj/Localizable.strings +++ b/UI/PreferencesUI/French.lproj/Localizable.strings @@ -485,6 +485,8 @@ "Enable two-factor authentication using a TOTP application" = "Activer l'authentification à deux facteurs à l'aide d’une application TOTP"; "You must enter this key into your TOTP application." = "Vous devez saisir cette clé dans votre application TOTP."; "If you do not and you log out you will not be able to login again." = "À défaut de le faire, vous ne pourrez pas vous reconnecter."; +"Enter TOTP verification code :" = "Saisissez le code de vérification TOTP :"; +"Invalid TOTP verification code" = "Code de vérification TOTP invalide"; /* External Sieve scripts */ "An external Sieve script is active" = "Un script Sieve externe est actif"; diff --git a/UI/PreferencesUI/UIxPreferences.m b/UI/PreferencesUI/UIxPreferences.m index d48fd3b26..6de9ce09f 100644 --- a/UI/PreferencesUI/UIxPreferences.m +++ b/UI/PreferencesUI/UIxPreferences.m @@ -53,6 +53,10 @@ #import "UIxPreferences.h" +#if defined(MFA_CONFIG) +#include +#endif + static NSArray *reminderItems = nil; static NSArray *reminderValues = nil; @@ -1586,6 +1590,69 @@ static NSArray *reminderValues = nil; } } + +#if defined(MFA_CONFIG) + // Check TOTP token + NSString *verificationCode; + + if ([v objectForKey: @"SOGoTOTPEnabled"] + && 1 == [[v objectForKey: @"SOGoTOTPEnabled"] intValue] + && ![[user userDefaults] totpEnabled]) { + + verificationCode = [v objectForKey: @"totpVerificationCode"]; + + if ([verificationCode length] == 6 && [verificationCode unsignedIntValue] > 0) + { + unsigned int code; + const char *real_secret; + char *secret; + + size_t secret_len; + + const auto time_step = OATH_TOTP_DEFAULT_TIME_STEP_SIZE; + const auto digits = 6; + + real_secret = [[user totpKey] UTF8String]; + + auto result = oath_init(); + auto t = time(NULL); + auto left = time_step - (t % time_step); + + char otp[digits + 1]; + + oath_base32_decode (real_secret, + strlen(real_secret), + &secret, &secret_len); + + result = oath_totp_generate2(secret, + secret_len, + t, + time_step, + OATH_TOTP_DEFAULT_START_TIME, + digits, + 0, + otp); + + sscanf(otp, "%u", &code); + + oath_done(); + free(secret); + + if (code != [verificationCode unsignedIntValue]) + { + results = (id ) [self responseWithStatus: 485 + andJSONRepresentation: [NSDictionary dictionaryWithObjectsAndKeys: @"Invalid TOTP verification code", @"message", nil]]; + return results; + } + } else { + results = (id ) [self responseWithStatus: 485 + andJSONRepresentation: [NSDictionary dictionaryWithObjectsAndKeys: @"Invalid TOTP verification code", @"message", nil]]; + return results; + } + } + [v removeObjectForKey: @"totpVerificationCode"]; +#endif + [[[user userDefaults] source] setValues: v]; if ([[user userDefaults] synchronize] && [self userHasMailAccess]) diff --git a/UI/Templates/PreferencesUI/UIxPreferences.wox b/UI/Templates/PreferencesUI/UIxPreferences.wox index 92ee81dda..a243cb75a 100644 --- a/UI/Templates/PreferencesUI/UIxPreferences.wox +++ b/UI/Templates/PreferencesUI/UIxPreferences.wox @@ -258,7 +258,15 @@
+ + + +
+
+
+
+ diff --git a/UI/WebServerResources/js/Preferences/PreferencesController.js b/UI/WebServerResources/js/Preferences/PreferencesController.js index 746a8d613..b553916df 100644 --- a/UI/WebServerResources/js/Preferences/PreferencesController.js +++ b/UI/WebServerResources/js/Preferences/PreferencesController.js @@ -456,8 +456,10 @@ }); } - if (sendForm) + if (sendForm) { + var self = this; return this.preferences.$save().then(function(data) { + self.preferences.defaults.totpVerificationCode = '' if (!options || !options.quick) { $mdToast.show( $mdToast.simple() @@ -466,11 +468,20 @@ .hideDelay(2000)); form.$setPristine(); } + }).catch(function(e) { + if (485 == e.status) { + form.totpVerificationCode.$setValidity('invalidTotpCode', false); + } }); + } return $q.reject('Invalid form'); }; + this.resetTotpVerificationCode = function(form) { + form.totpVerificationCode.$setValidity('invalidTotpCode', true); + } + this.canChangePassword = function(form) { if (this.passwords.newPasswordConfirmation && this.passwords.newPasswordConfirmation.length && this.passwords.newPassword != this.passwords.newPasswordConfirmation) {