From 6411fbea42bbfe4c170eba1e50456d86afbf9615 Mon Sep 17 00:00:00 2001 From: Ylian Saint-Hilaire Date: Fri, 25 Sep 2020 13:58:36 -0700 Subject: [PATCH] Fixed account password reset when used with 2FA accounts. --- db.js | 6 +-- translate/translate.json | 15 +++++-- views/login.handlebars | 72 ++++++++++++++++++++++++---------- views/login2.handlebars | 84 ++++++++++++++++++++++++++++++---------- webserver.js | 2 +- 5 files changed, 130 insertions(+), 49 deletions(-) diff --git a/db.js b/db.js index be600099..f823f39f 100644 --- a/db.js +++ b/db.js @@ -1012,8 +1012,8 @@ module.exports.CreateDB = function (parent, func) { }; obj.GetAllType = function (type, func) { obj.file.find({ type: type }).toArray(function (err, docs) { func(err, performTypedRecordDecrypt(docs)); }); }; obj.GetAllIdsOfType = function (ids, domain, type, func) { obj.file.find({ type: type, domain: domain, _id: { $in: ids } }).toArray(function (err, docs) { func(err, performTypedRecordDecrypt(docs)); }); }; - obj.GetUserWithEmail = function (domain, email, func) { obj.file.find({ type: 'user', domain: domain, email: email }).project({ type: 0 }).toArray(function (err, docs) { func(err, performTypedRecordDecrypt(docs)); }); }; - obj.GetUserWithVerifiedEmail = function (domain, email, func) { obj.file.find({ type: 'user', domain: domain, email: email, emailVerified: true }).project({ type: 0 }).toArray(function (err, docs) { func(err, performTypedRecordDecrypt(docs)); }); }; + obj.GetUserWithEmail = function (domain, email, func) { obj.file.find({ type: 'user', domain: domain, email: email }).toArray(function (err, docs) { func(err, performTypedRecordDecrypt(docs)); }); }; + obj.GetUserWithVerifiedEmail = function (domain, email, func) { obj.file.find({ type: 'user', domain: domain, email: email, emailVerified: true }).toArray(function (err, docs) { func(err, performTypedRecordDecrypt(docs)); }); }; obj.Remove = function (id, func) { obj.file.deleteOne({ _id: id }, func); }; obj.RemoveAll = function (func) { obj.file.deleteMany({}, { multi: true }, func); }; obj.RemoveAllOfType = function (type, func) { obj.file.deleteMany({ type: type }, { multi: true }, func); }; @@ -1166,7 +1166,7 @@ module.exports.CreateDB = function (parent, func) { obj.GetAllType = function (type, func) { obj.file.find({ type: type }, function (err, docs) { func(err, performTypedRecordDecrypt(docs)); }); }; obj.GetAllIdsOfType = function (ids, domain, type, func) { obj.file.find({ type: type, domain: domain, _id: { $in: ids } }, function (err, docs) { func(err, performTypedRecordDecrypt(docs)); }); }; obj.GetUserWithEmail = function (domain, email, func) { obj.file.find({ type: 'user', domain: domain, email: email }, { type: 0 }, function (err, docs) { func(err, performTypedRecordDecrypt(docs)); }); }; - obj.GetUserWithVerifiedEmail = function (domain, email, func) { obj.file.find({ type: 'user', domain: domain, email: email, emailVerified: true }, { type: 0 }, function (err, docs) { func(err, performTypedRecordDecrypt(docs)); }); }; + obj.GetUserWithVerifiedEmail = function (domain, email, func) { obj.file.find({ type: 'user', domain: domain, email: email, emailVerified: true }, function (err, docs) { func(err, performTypedRecordDecrypt(docs)); }); }; obj.Remove = function (id, func) { obj.file.remove({ _id: id }, func); }; obj.RemoveAll = function (func) { obj.file.remove({}, { multi: true }, func); }; obj.RemoveAllOfType = function (type, func) { obj.file.remove({ type: type }, { multi: true }, func); }; diff --git a/translate/translate.json b/translate/translate.json index 3d993f54..d6ef20ae 100644 --- a/translate/translate.json +++ b/translate/translate.json @@ -13546,11 +13546,13 @@ "login-mobile.handlebars->5->42", "login-mobile.handlebars->container->page_content->column_l->1->1->0->1->tokenpanel->1->7->1->4->1->3", "login.handlebars->5->43", + "login.handlebars->container->column_l->centralTable->1->0->logincell->resettokenpanel->1->5->1->2->1->3", "login.handlebars->container->column_l->centralTable->1->0->logincell->tokenpanel->1->7->1->4->1->3", "login2.handlebars->7->22", "login2.handlebars->7->44", "login2.handlebars->centralTable->1->0->logincell->resetpanel->1->7->1->0->1", "login2.handlebars->centralTable->1->0->logincell->resetpanel->1->7->1->0->1", + "login2.handlebars->centralTable->1->0->logincell->resettokenpanel->1->5->1->2farow2->1->3", "login2.handlebars->centralTable->1->0->logincell->tokenpanel->1->7->1->2farow->1->3" ] }, @@ -21235,6 +21237,7 @@ "login.handlebars->container->column_l->centralTable->1->0->logincell->loginpanel->1->5->1", "login.handlebars->container->column_l->centralTable->1->0->logincell->loginpanel->1->7->1->4->3", "login2.handlebars->centralTable->1->0->logincell->loginpanel->1->5->1->6->1", + "login2.handlebars->centralTable->1->0->logincell->resettokenpanel->1->5->1->2->1->1", "login2.handlebars->centralTable->1->0->logincell->tokenpanel->1->7->1->2->1->1" ] }, @@ -21300,8 +21303,7 @@ "login-mobile.handlebars->container->page_content->column_l->1->1->0->1->resettokenpanel->1->5->1->2->1->1", "login-mobile.handlebars->container->page_content->column_l->1->1->0->1->tokenpanel->1->7->1->4->1->1", "login.handlebars->container->column_l->centralTable->1->0->logincell->resettokenpanel->1->5->1->2->1->1", - "login.handlebars->container->column_l->centralTable->1->0->logincell->tokenpanel->1->7->1->4->1->1", - "login2.handlebars->centralTable->1->0->logincell->resettokenpanel->1->5->1->2->1->1" + "login.handlebars->container->column_l->centralTable->1->0->logincell->tokenpanel->1->7->1->4->1->1" ] }, { @@ -21388,8 +21390,7 @@ "login-mobile.handlebars->container->page_content->column_l->1->1->0->1->resettokenpanel->1->5->1->0->1", "login-mobile.handlebars->container->page_content->column_l->1->1->0->1->tokenpanel->1->7->1->0->1", "login.handlebars->container->column_l->centralTable->1->0->logincell->resettokenpanel->1->5->1->0->1", - "login.handlebars->container->column_l->centralTable->1->0->logincell->tokenpanel->1->7->1->0->1", - "login2.handlebars->centralTable->1->0->logincell->resettokenpanel->1->5->1->0->1" + "login.handlebars->container->column_l->centralTable->1->0->logincell->tokenpanel->1->7->1->0->1" ] }, { @@ -30335,7 +30336,9 @@ "default.handlebars->27->1905", "default.handlebars->27->1910", "login-mobile.handlebars->container->page_content->column_l->1->1->0->1->tokenpanel->1->7->1->4->1->3", + "login.handlebars->container->column_l->centralTable->1->0->logincell->resettokenpanel->1->5->1->2->1->3", "login.handlebars->container->column_l->centralTable->1->0->logincell->tokenpanel->1->7->1->4->1->3", + "login2.handlebars->centralTable->1->0->logincell->resettokenpanel->1->5->1->2farow2->1->3", "login2.handlebars->centralTable->1->0->logincell->tokenpanel->1->7->1->2farow->1->3" ] }, @@ -36193,6 +36196,8 @@ "zh-chs": "代币", "zh-cht": "代幣", "xloc": [ + "login2.handlebars->centralTable->1->0->logincell->resettokenpanel->1->5->1->0->1", + "login2.handlebars->centralTable->1->0->logincell->resettokenpanel->1->5->1->0->1", "login2.handlebars->centralTable->1->0->logincell->tokenpanel->1->7->1->0->1", "login2.handlebars->centralTable->1->0->logincell->tokenpanel->1->7->1->0->1" ] @@ -37429,7 +37434,9 @@ "zh-cht": "使用安全密鑰", "xloc": [ "login-mobile.handlebars->container->page_content->column_l->1->1->0->1->tokenpanel->1->7->1->4->1->3", + "login.handlebars->container->column_l->centralTable->1->0->logincell->resettokenpanel->1->5->1->2->1->3", "login.handlebars->container->column_l->centralTable->1->0->logincell->tokenpanel->1->7->1->4->1->3", + "login2.handlebars->centralTable->1->0->logincell->resettokenpanel->1->5->1->2farow2->1->3", "login2.handlebars->centralTable->1->0->logincell->tokenpanel->1->7->1->2farow->1->3" ] }, diff --git a/views/login.handlebars b/views/login.handlebars index 1fe08ad4..ffd15e02 100644 --- a/views/login.handlebars +++ b/views/login.handlebars @@ -161,7 +161,7 @@ Login token: -
+
@@ -174,9 +174,9 @@
- - - + + +
@@ -200,6 +200,11 @@
+
+ + + +
@@ -422,6 +427,14 @@ QV('smsKeyButton', otpsms && (messageid != 2) && (messageid != 4)); } + if (loginMode == '5') { + try { if (hardwareKeyChallenge.length > 0) { hardwareKeyChallenge = JSON.parse(hardwareKeyChallenge); } else { hardwareKeyChallenge = null; } } catch (ex) { hardwareKeyChallenge = null } + QV('securityKeyButton2', (hardwareKeyChallenge != null) && (hardwareKeyChallenge.type == 'webAuthn')); + QV('emailKeyButton2', otpemail && (messageid != 2) && (messageid != 4)); + QV('smsKeyButton2', otpsms && (messageid != 2) && (messageid != 4)); + } + + /* if (loginMode == '5') { try { if (hardwareKeyChallenge.length > 0) { hardwareKeyChallenge = JSON.parse(hardwareKeyChallenge); } else { hardwareKeyChallenge = null; } } catch (ex) { hardwareKeyChallenge = null } if ((hardwareKeyChallenge != null) && (hardwareKeyChallenge.type == 'webAuthn')) { @@ -452,13 +465,14 @@ ); } } + */ // Setup the user interface in the right mode userInterfaceSelectMenu(); } // Use a hardware security key - function useSecurityKey() { + function useSecurityKey(panelAction) { if ((hardwareKeyChallenge != null) && (hardwareKeyChallenge.type == 'webAuthn')) { if (typeof hardwareKeyChallenge.challenge == 'string') { hardwareKeyChallenge.challenge = Uint8Array.from(atob(hardwareKeyChallenge.challenge), function (c) { return c.charCodeAt(0) }).buffer; } @@ -479,35 +493,53 @@ signature: btoa(String.fromCharCode.apply(null, new Uint8Array(rawAssertion.response.signature))), authenticatorData: btoa(String.fromCharCode.apply(null, new Uint8Array(rawAssertion.response.authenticatorData))), }; - Q('hwtokenInput').value = JSON.stringify(assertion); - QE('tokenOkButton', true); - Q('tokenOkButton').click(); + if (panelAction == 1) { + Q('hwtokenInput').value = JSON.stringify(assertion); + QE('tokenOkButton', true); + Q('tokenOkButton').click(); + } else if (panelAction == 2) { + Q('resetHwtokenInput').value = JSON.stringify(assertion); + QE('resetTokenOkButton', true); + Q('resetTokenOkButton').click(); + } }, function (error) { console.log('credentials-get error', error); } ); } } - function useEmailToken() { + function useEmailToken(panelAction) { if (otpemail != true) return; - setDialogMode(1, "Secure Login", 3, useEmailKeyEx, "Send token to registered email address?"); + setDialogMode(1, "Secure Login", 3, useEmailKeyEx, "Send token to registered email address?", panelAction); } - function useEmailKeyEx() { - Q('hwtokenInput').value = '**email**'; - QE('tokenOkButton', true); - Q('tokenOkButton').click(); + function useEmailKeyEx(b, panelAction) { + if (panelAction == 1) { + Q('hwtokenInput').value = '**email**'; + QE('tokenOkButton', true); + Q('tokenOkButton').click(); + } else if (panelAction == 2) { + Q('resetHwtokenInput').value = '**email**'; + QE('resetTokenOkButton', true); + Q('resetTokenOkButton').click(); + } } - function useSMSToken() { + function useSMSToken(panelAction) { if (otpsms != true) return; - setDialogMode(1, "Secure Login", 3, useSMSTokenEx, "Send token to registered phone number?"); + setDialogMode(1, "Secure Login", 3, useSMSTokenEx, "Send token to registered phone number?", panelAction); } - function useSMSTokenEx() { - Q('hwtokenInput').value = '**sms**'; - QE('tokenOkButton', true); - Q('tokenOkButton').click(); + function useSMSTokenEx(b, panelAction) { + if (panelAction == 1) { + Q('hwtokenInput').value = '**sms**'; + QE('tokenOkButton', true); + Q('tokenOkButton').click(); + } else if (panelAction == 2) { + Q('resetHwtokenInput').value = '**sms**'; + QE('resetTokenOkButton', true); + Q('resetTokenOkButton').click(); + } } function showPassHint(e) { diff --git a/views/login2.handlebars b/views/login2.handlebars index b4790db3..37840efb 100644 --- a/views/login2.handlebars +++ b/views/login2.handlebars @@ -180,9 +180,9 @@
- - - + + +
@@ -195,17 +195,28 @@
- +
- + + +
Login token: - +
+
+ +
+
-
+
+
+ + + +
@@ -427,6 +438,18 @@ QV('2farow', twofakey || emailkey || smskey); } + if (loginMode == '5') { + try { if (hardwareKeyChallenge.length > 0) { hardwareKeyChallenge = JSON.parse(hardwareKeyChallenge); } else { hardwareKeyChallenge = null; } } catch (ex) { hardwareKeyChallenge = null } + var twofakey = (hardwareKeyChallenge != null) && (hardwareKeyChallenge.type == 'webAuthn'); + var emailkey = otpemail && (messageid != 2) && (messageid != 4); + var smskey = otpsms && (messageid != 2) && (messageid != 4); + QV('securityKeyButton2', twofakey); + QV('emailKeyButton2', emailkey); + QV('smsKeyButton2', smskey); + QV('2farow2', twofakey || emailkey || smskey); + } + + /* if (loginMode == '5') { try { if (hardwareKeyChallenge.length > 0) { hardwareKeyChallenge = JSON.parse(hardwareKeyChallenge); } else { hardwareKeyChallenge = null; } } catch (ex) { hardwareKeyChallenge = null } if ((hardwareKeyChallenge != null) && (hardwareKeyChallenge.type == 'webAuthn')) { @@ -457,10 +480,11 @@ ); } } + */ } // Use a hardware security key - function useSecurityKey() { + function useSecurityKey(panelAction) { if ((hardwareKeyChallenge != null) && (hardwareKeyChallenge.type == 'webAuthn')) { if (typeof hardwareKeyChallenge.challenge == 'string') { hardwareKeyChallenge.challenge = Uint8Array.from(atob(hardwareKeyChallenge.challenge), function (c) { return c.charCodeAt(0) }).buffer; } @@ -481,35 +505,53 @@ signature: btoa(String.fromCharCode.apply(null, new Uint8Array(rawAssertion.response.signature))), authenticatorData: btoa(String.fromCharCode.apply(null, new Uint8Array(rawAssertion.response.authenticatorData))), }; - Q('hwtokenInput').value = JSON.stringify(assertion); - QE('tokenOkButton', true); - Q('tokenOkButton').click(); + if (panelAction == 1) { + Q('hwtokenInput').value = JSON.stringify(assertion); + QE('tokenOkButton', true); + Q('tokenOkButton').click(); + } else if (panelAction == 2) { + Q('resetHwtokenInput').value = JSON.stringify(assertion); + QE('resetTokenOkButton', true); + Q('resetTokenOkButton').click(); + } }, function (error) { console.log('credentials-get error', error); } ); } } - function useEmailToken() { + function useEmailToken(panelAction) { if (otpemail != true) return; setDialogMode(1, "Secure Login", 3, useEmailKeyEx, "Send token to registered email address?"); } - function useEmailKeyEx() { - Q('hwtokenInput').value = '**email**'; - QE('tokenOkButton', true); - Q('tokenOkButton').click(); + function useEmailKeyEx(b, panelAction) { + if (panelAction == 1) { + Q('hwtokenInput').value = '**email**'; + QE('tokenOkButton', true); + Q('tokenOkButton').click(); + } else if (panelAction == 2) { + Q('resetHwtokenInput').value = '**email**'; + QE('resetTokenOkButton', true); + Q('resetTokenOkButton').click(); + } } - function useSMSToken() { + function useSMSToken(panelAction) { if (otpsms != true) return; setDialogMode(1, "Secure Login", 3, useSMSTokenEx, "Send token to registered phone number?"); } - function useSMSTokenEx() { - Q('hwtokenInput').value = '**sms**'; - QE('tokenOkButton', true); - Q('tokenOkButton').click(); + function useSMSTokenEx(b, panelAction) { + if (panelAction == 1) { + Q('hwtokenInput').value = '**sms**'; + QE('tokenOkButton', true); + Q('tokenOkButton').click(); + } else if (panelAction == 2) { + Q('resetHwtokenInput').value = '**sms**'; + QE('resetTokenOkButton', true); + Q('resetTokenOkButton').click(); + } } function showPassHint(e) { diff --git a/webserver.js b/webserver.js index c52c8f27..672e142d 100644 --- a/webserver.js +++ b/webserver.js @@ -2438,7 +2438,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) { if (hardwareKeyChallenge) { hwstate = obj.parent.encodeCookie({ u: req.session.tokenusername, p: req.session.tokenpassword, c: req.session.u2fchallenge }, obj.parent.loginCookieEncryptionKey) } // Check if we can use OTP tokens with email - var otpemail = (parent.mailserver != null) && (req.session != null) && (req.session.tokenemail == true); + var otpemail = (parent.mailserver != null) && (req.session != null) && ((req.session.tokenemail == true) || (typeof req.session.tokenemail == 'string')); if ((typeof domain.passwordrequirements == 'object') && (domain.passwordrequirements.email2factor == false)) { otpemail = false; } var otpsms = (parent.smsserver != null) && (req.session != null) && (req.session.tokensms == true); if ((typeof domain.passwordrequirements == 'object') && (domain.passwordrequirements.sms2factor == false)) { otpsms = false; }