Added support for 2FA messaging (#4650)

Ylian Saint-Hilaire committed Oct 22, 2022 at 12:59 UTC e2cf723c6a95f6c472d7eb38a50e19c677e5b643
8 files changed +139 -13
meshmessaging.js
+1 -1
@@ -69,7 +69,7 @@ module.exports.CreateServer = function (parent) {
69 async function sendTelegramMessage(to, msg, func) {
70 if (obj.telegramClient == null) return;
71 parent.debug('email', 'Sending Telegram message to: ' + to.substring(9) + ': ' + msg);
72 - try { await obj.telegramClient.sendMessage(to.substring(9), { message: msg }); func(true); } catch (ex) { func(false, ex); }
72 + try { await obj.telegramClient.sendMessage(to.substring(9), { message: msg }); if (func != null) { func(true); } } catch (ex) { if (func != null) { func(false, ex); } }
73 }
74 sendTelegramMessage(to, msg, func);
75 } else {
meshuser.js
+2
@@ -7941,10 +7941,12 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
7941 function count2factoraAuths() {
7942 var email2fa = (((typeof domain.passwordrequirements != 'object') || (domain.passwordrequirements.email2factor != false)) && (domain.mailserver != null));
7943 var sms2fa = ((parent.parent.smsserver != null) && ((typeof domain.passwordrequirements != 'object') || (domain.passwordrequirements.sms2factor != false)));
7944 + var msg2fa = ((parent.parent.msgserver != null) && (parent.parent.msgserver.providers != 0) && ((typeof domain.passwordrequirements != 'object') || (domain.passwordrequirements.msg2factor != false)));
7945 var authFactorCount = 0;
7946 if (typeof user.otpsecret == 'string') { authFactorCount++; } // Authenticator time factor
7947 if (email2fa && (user.otpekey != null)) { authFactorCount++; } // EMail factor
7948 if (sms2fa && (user.phone != null)) { authFactorCount++; } // SMS factor
7949 + if (msg2fa && (user.msghandle != null)) { authFactorCount++; } // Messaging factor
7950 if (user.otphkeys != null) { authFactorCount += user.otphkeys.length; } // FIDO hardware factor
7951 if ((authFactorCount > 0) && (user.otpkeys != null)) { authFactorCount++; } // Backup keys
7952 return authFactorCount;
public/images/login/2fa-messaging-48.png
Binary files /dev/null and b/public/images/login/2fa-messaging-48.png differ
public/images/login/2fa-messaging-96.png
Binary files /dev/null and b/public/images/login/2fa-messaging-96.png differ
views/default.handlebars
+2
@@ -2230,6 +2230,7 @@
2230 if (userinfo.otphkeys > 0) { authFactorCount += userinfo.otphkeys; } // FIDO hardware factor
2231 if ((features & 0x00800000) && (userinfo.otpekey == 1)) { authFactorCount++; } // EMail factor
2232 if ((features & 0x02000000) && (features & 0x04000000) && (userinfo.phone != null)) { authFactorCount++; } // SMS factor
2233 + if ((features2 & 0x02000000) && (features2 & 0x04000000) && (userinfo.msghandle != null)) { authFactorCount++; } // Messaging factor
2234 if ((authFactorCount > 0) && (userinfo.otpkeys > 0) && ((features2 & 0x40000) == 0)) { authFactorCount++; } // Backup keys
2235 return authFactorCount;
2236 }
@@ -16738,6 +16739,7 @@
16739 if (v == 'fido') { return "FIDO key"; }
16740 if (v == 'sms') { return "SMS message"; }
16741 if (v == 'hwotp') { return "Hardware OTP"; }
16742 + if (v == 'messenger') { return "Messenging"; }
16743 if (v == 'push') { return "Push Notification"; }
16744 if (v == 'otp') { return "One-Time Password"; }
16745 if (v == 'cookie') { return "Remember Device"; }
views/login.handlebars
+23 -1
@@ -190,6 +190,7 @@
190 <input style="display:none;float:right" id=securityKeyButton type=button value="Use Security Key" onclick="useSecurityKey(1)" />
191 <input style="display:none;float:right" id=emailKeyButton type=button value="Email" onclick="useEmailToken(1)" />
192 <input style="display:none;float:right" id=smsKeyButton type=button value="SMS" onclick="useSMSToken(1)" />
193 + <input style="display:none;float:right" id=msgKeyButton type=button value="Messaging" onclick="useMsgToken(1)" />
194 </div>
195 </td>
196 </tr>
@@ -217,6 +218,7 @@
218 <input style="display:none;float:right" id=securityKeyButton2 type=button value="Use Security Key" onclick="useSecurityKey(2)" />
219 <input style="display:none;float:right" id=emailKeyButton2 type=button value="Email" onclick="useEmailToken(2)" />
220 <input style="display:none;float:right" id=smsKeyButton2 type=button value="SMS" onclick="useSMSToken(2)" />
221 + <input style="display:none;float:right" id=msgKeyButton2 type=button value="Messaging" onclick="useMsgToken(2)" />
222 </div>
223 </td>
224 </tr>
@@ -335,6 +337,7 @@
337 var publicKeyCredentialRequestOptions = null;
338 var otpemail = (decodeURIComponent('{{{otpemail}}}') === 'true');
339 var otpsms = (decodeURIComponent('{{{otpsms}}}') === 'true');
340 + var otpmsg = (decodeURIComponent('{{{otpmsg}}}') === 'true');
341 var autofido = (decodeURIComponent('{{{autofido}}}') === 'true');
342 var twoFactorCookieDays = parseInt('{{{twoFactorCookieDays}}}');
343 var authStrategies = '{{{authStrategies}}}'.split(',');
@@ -344,7 +347,7 @@
347 // Display the right server message
348 var i;
349 var messageid = parseInt('{{{messageid}}}');
347 - var okmessages = ['', "If valid, reset mail sent.", "Email sent.", "Email verification required, check your mailbox and click the confirmation link.", "SMS sent."];
350 + var okmessages = ['', "If valid, reset mail sent.", "Email sent.", "Email verification required, check your mailbox and click the confirmation link.", "SMS sent.", "Sending notification...", "Message sent."];
351 var failmessages = ["Unable to create account.", "Account limit reached.", "Existing account with this email address.", "Invalid account creation token.", "Username already exists.", "Password rejected, use a different one.", "Invalid email.", "Account not found.", "Invalid token, try again.", "Unable to sent email.", "Account locked.", "Access denied.", "Login failed, check username and password.", "Password change requested.", "IP address blocked, try again later.", "Server under maintenance.", "Unable to send device notification.", "Invalid security check."];
352 if (messageid > 0) {
353 var msg = '';
@@ -455,6 +458,7 @@
458 QV('securityKeyButton', twofakey);
459 QV('emailKeyButton', otpemail && (messageid != 2) && (messageid != 4));
460 QV('smsKeyButton', otpsms && (messageid != 2) && (messageid != 4));
461 + QV('msgKeyButton', otpmsg && (messageid != 2) && (messageid != 4));
462
463 // If hardware key is an option, trigger it now
464 if (autofido && twofakey) { setTimeout(function () { useSecurityKey(1); }, 300); }
@@ -467,6 +471,7 @@
471 QV('securityKeyButton2', twofakey);
472 QV('emailKeyButton2', otpemail && (messageid != 2) && (messageid != 4));
473 QV('smsKeyButton2', otpsms && (messageid != 2) && (messageid != 4));
474 + QV('msgKeyButton2', otpmsg && (messageid != 2) && (messageid != 4));
475
476 // If hardware key is an option, trigger it now
477 if (autofido && twofakey) { setTimeout(function () { useSecurityKey(2); }, 300); }
@@ -580,6 +585,23 @@
585 }
586 }
587
588 + function useMsgToken(panelAction) {
589 + if (otpmsg != true) return;
590 + setDialogMode(1, "Secure Login", 3, useMsgTokenEx, "Send token to messaging application?", panelAction);
591 + }
592 +
593 + function useMsgTokenEx(b, panelAction) {
594 + if (panelAction == 1) {
595 + Q('hwtokenInput').value = '**msg**';
596 + QE('tokenOkButton', true);
597 + Q('tokenOkButton').click();
598 + } else if (panelAction == 2) {
599 + Q('resetHwtokenInput').value = '**msg**';
600 + QE('resetTokenOkButton', true);
601 + Q('resetTokenOkButton').click();
602 + }
603 + }
604 +
605 function showPassHint(e) {
606 messagebox("Password Hint", passhint);
607 haltEvent(e);
views/login2.handlebars
+27 -3
@@ -217,6 +217,7 @@
217 <div>
218 <img id=securityKeyButton src="images/login/2fa-key-48.png" srcset="images/login/2fa-key-96.png 2x" title="Use Security Key" loading="lazy" width="48" height="48" style="display:none;margin-left:3px;margin-right:3px;border-radius:3px;box-shadow:2px 2px 5px black;cursor:pointer;background-color:#FFF" onclick="useSecurityKey(1)" />
219 <img id=smsKeyButton src="images/login/2fa-sms-48.png" srcset="images/login/2fa-sms-96.png 2x" title="SMS" loading="lazy" width="48" height="48" style="display:none;margin-left:3px;margin-right:3px;border-radius:3px;box-shadow:2px 2px 5px black;cursor:pointer;background-color:#FFF" onclick="useSMSToken(1)" />
220 + <img id=msgKeyButton src="images/login/2fa-messaging-48.png" srcset="images/login/2fa-messaging-96.png 2x" title="Messaging" loading="lazy" width="48" height="48" style="display:none;margin-left:3px;margin-right:3px;border-radius:3px;box-shadow:2px 2px 5px black;cursor:pointer;background-color:#FFF" onclick="useMsgToken(1)" />
221 <img id=emailKeyButton src="images/login/2fa-mail-48.png" srcset="images/login/2fa-mail-96.png 2x" title="Email" loading="lazy" width="48" height="48" style="display:none;margin-left:3px;margin-right:3px;border-radius:3px;box-shadow:2px 2px 5px black;cursor:pointer;background-color:#FFF" onclick="useEmailToken(1)" />
222 <img id=pushKeyButton src="images/login/2fa-push-48.png" srcset="images/login/2fa-push-96.png 2x" title="Device Authentication" loading="lazy" width="48" height="48" style="display:none;margin-left:3px;margin-right:3px;border-radius:3px;box-shadow:2px 2px 5px black;cursor:pointer;background-color:#FFF" onclick="usePushToken(1)" />
223 </div>
@@ -251,6 +252,7 @@
252 <div>
253 <img id=securityKeyButton2 src="images/login/2fa-key-48.png" srcset="images/login/2fa-key-96.png 2x" title="Use Security Key" loading="lazy" width="48" height="48" style="display:none;margin-left:3px;margin-right:3px;border-radius:3px;box-shadow:2px 2px 5px black;cursor:pointer;background-color:#FFF" onclick="useSecurityKey(2)" />
254 <img id=smsKeyButton2 src="images/login/2fa-sms-48.png" srcset="images/login/2fa-sms-96.png 2x" title="SMS" loading="lazy" width="48" height="48" style="display:none;margin-left:3px;margin-right:3px;border-radius:3px;box-shadow:2px 2px 5px black;cursor:pointer;background-color:#FFF" onclick="useSMSToken(2)" />
255 + <img id=msgKeyButton2 src="images/login/2fa-msg-48.png" srcset="images/login/2fa-msg-96.png 2x" title="SMS" loading="lazy" width="48" height="48" style="display:none;margin-left:3px;margin-right:3px;border-radius:3px;box-shadow:2px 2px 5px black;cursor:pointer;background-color:#FFF" onclick="useMsgToken(2)" />
256 <img id=emailKeyButton2 src="images/login/2fa-mail-48.png" srcset="images/login/2fa-mail-96.png 2x" title="Email" loading="lazy" width="48" height="48" style="display:none;margin-left:3px;margin-right:3px;border-radius:3px;box-shadow:2px 2px 5px black;cursor:pointer;background-color:#FFF" onclick="useEmailToken(2)" />
257 <img id=pushKeyButton2 src="images/login/2fa-push-48.png" srcset="images/login/2fa-push-96.png 2x" title="Device Authentication" loading="lazy" width="48" height="48" style="display:none;margin-left:3px;margin-right:3px;border-radius:3px;box-shadow:2px 2px 5px black;cursor:pointer;background-color:#FFF" onclick="usePushToken(2)" />
258 </div>
@@ -389,6 +391,7 @@
391 var publicKeyCredentialRequestOptions = null;
392 var otpemail = (decodeURIComponent('{{{otpemail}}}') === 'true');
393 var otpsms = (decodeURIComponent('{{{otpsms}}}') === 'true');
394 + var otpmsg = (decodeURIComponent('{{{otpmsg}}}') === 'true');
395 var otppush = (decodeURIComponent('{{{otppush}}}') === 'true');
396 var autofido = (decodeURIComponent('{{{autofido}}}') === 'true');
397 var twoFactorCookieDays = parseInt('{{{twoFactorCookieDays}}}');
@@ -414,7 +417,7 @@
417 // Display the right server message
418 var i;
419 var messageid = parseInt('{{{messageid}}}');
417 - var okmessages = ['', "If valid, reset mail sent.", "Email sent.", "Email verification required, check your mailbox and click the confirmation link.", "SMS sent.", "Sending notification..."];
420 + var okmessages = ['', "If valid, reset mail sent.", "Email sent.", "Email verification required, check your mailbox and click the confirmation link.", "SMS sent.", "Sending notification...", "Message sent."];
421 var failmessages = ["Unable to create account.", "Account limit reached.", "Existing account with this email address.", "Invalid account creation token.", "Username already exists.", "Password rejected, use a different one.", "Invalid email.", "Account not found.", "Invalid token, try again.", "Unable to sent email.", "Account locked.", "Access denied.", "Login failed, check username and password.", "Password change requested.", "IP address blocked, try again later.", "Server under maintenance.", "Unable to send device notification.", "Invalid security check."];
422 if (messageid > 0) {
423 var msg = '';
@@ -508,12 +511,14 @@
511 var twofakey = (hardwareKeyChallenge != null) && (hardwareKeyChallenge.type == 'webAuthn');
512 var emailkey = otpemail && (messageid != 2) && (messageid != 4);
513 var smskey = otpsms && (messageid != 2) && (messageid != 4);
514 + var msgkey = otpmsg && (messageid != 2) && (messageid != 4);
515 var pushkey = otppush && (messageid != 2) && (messageid != 4);
516 QV('securityKeyButton', twofakey);
517 QV('emailKeyButton', emailkey);
518 QV('smsKeyButton', smskey);
519 + QV('msgKeyButton', msgkey);
520 QV('pushKeyButton', pushkey);
516 - QV('2farow', twofakey || emailkey || smskey || pushkey);
521 + QV('2farow', twofakey || emailkey || smskey || msgkey || pushkey);
522
523 // If hardware key is an option, trigger it now
524 if (autofido && twofakey) { setTimeout(function () { useSecurityKey(1); }, 300); }
@@ -525,12 +530,14 @@
530 var twofakey = (hardwareKeyChallenge != null) && (hardwareKeyChallenge.type == 'webAuthn');
531 var emailkey = otpemail && (messageid != 2) && (messageid != 4);
532 var smskey = otpsms && (messageid != 2) && (messageid != 4);
533 + var msgkey = otpmsg && (messageid != 2) && (messageid != 4);
534 var pushkey = otppush && (messageid != 2) && (messageid != 4);
535 QV('securityKeyButton2', twofakey);
536 QV('emailKeyButton2', emailkey);
537 QV('smsKeyButton2', smskey);
538 + QV('msgKeyButton2', msgkey);
539 QV('pushKeyButton', pushkey);
533 - QV('2farow2', twofakey || emailkey || smskey || pushkey);
540 + QV('2farow2', twofakey || emailkey || smskey || msgkey || pushkey);
541
542 // If hardware key is an option, trigger it now
543 if (autofido && twofakey) { setTimeout(function () { useSecurityKey(2); }, 300); }
@@ -645,6 +652,23 @@
652 }
653 }
654
655 + function useMsgToken(panelAction) {
656 + if (otpmsg != true) return;
657 + setDialogMode(1, "Secure Login", 3, useMsgTokenEx, "Send token to messaging application?", panelAction);
658 + }
659 +
660 + function useMsgTokenEx(b, panelAction) {
661 + if (panelAction == 1) {
662 + Q('hwtokenInput').value = '**msg**';
663 + QE('tokenOkButton', true);
664 + Q('tokenOkButton').click();
665 + } else if (panelAction == 2) {
666 + Q('resetHwtokenInput').value = '**msg**';
667 + QE('resetTokenOkButton', true);
668 + Q('resetTokenOkButton').click();
669 + }
670 + }
671 +
672 function usePushToken(panelAction) {
673 if (panelAction == 1) {
674 Q('hwtokenInput').value = '**push**';
webserver.js
+84 -8
@@ -892,8 +892,11 @@ module.exports.CreateWebServer = function (parent, db, args, certificates, doneF
892 // See if SMS 2FA is available
893 var sms2fa = (((typeof domain.passwordrequirements != 'object') || (domain.passwordrequirements.sms2factor != false)) && (parent.smsserver != null) && (user.phone != null));
894
895 + // See if Messenger 2FA is available
896 + var msg2fa = (((typeof domain.passwordrequirements != 'object') || (domain.passwordrequirements.msg2factor != false)) && (parent.msgserver != null) && (parent.msgserver.providers != 0) && (user.msghandle != null));
897 +
898 // Check if a 2nd factor is present
896 - return ((parent.config.settings.no2factorauth !== true) && (sms2fa || (user.otpsecret != null) || ((user.email != null) && (user.emailVerified == true) && (domain.mailserver != null) && (user.otpekey != null)) || ((user.otphkeys != null) && (user.otphkeys.length > 0))));
899 + return ((parent.config.settings.no2factorauth !== true) && (msg2fa || sms2fa || (user.otpsecret != null) || ((user.email != null) && (user.emailVerified == true) && (domain.mailserver != null) && (user.otpekey != null)) || ((user.otphkeys != null) && (user.otphkeys.length > 0))));
900 }
901
902 // Check the 2-step auth token
@@ -907,6 +910,8 @@ module.exports.CreateWebServer = function (parent, db, args, certificates, doneF
910 if ((typeof domain.passwordrequirements == 'object') && (domain.passwordrequirements.email2factor == false)) { otpemail = false; }
911 var otpsms = (parent.smsserver != null);
912 if ((typeof domain.passwordrequirements == 'object') && (domain.passwordrequirements.sms2factor == false)) { otpsms = false; }
913 + var otpmsg = ((parent.msgserver != null) && (parent.msgserver.providers != 0));
914 + if ((typeof domain.passwordrequirements == 'object') && (domain.passwordrequirements.msg2factor == false)) { otpmsg = false; }
915
916 // Check 2FA login cookie
917 if ((token != null) && (token.startsWith('cookie='))) {
@@ -926,7 +931,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates, doneF
931 }
932 }
933
929 - // Check sms key
934 + // Check SMS key
935 if ((otpsms) && (user.phone != null) && (user.otpsms != null) && (user.otpsms.d != null) && (user.otpsms.k === token)) {
936 var deltaTime = (Date.now() - user.otpsms.d);
937 if ((deltaTime > 0) && (deltaTime < 300000)) { // Allow 5 minutes to use the SMS token (10000 * 60 * 5).
@@ -938,6 +943,18 @@ module.exports.CreateWebServer = function (parent, db, args, certificates, doneF
943 }
944 }
945
946 + // Check messenger key
947 + if ((otpmsg) && (user.msghandle != null) && (user.otpmsg != null) && (user.otpmsg.d != null) && (user.otpmsg.k === token)) {
948 + var deltaTime = (Date.now() - user.otpmsg.d);
949 + if ((deltaTime > 0) && (deltaTime < 300000)) { // Allow 5 minutes to use the Messenger token (10000 * 60 * 5).
950 + delete user.otpmsg;
951 + obj.db.SetUser(user);
952 + parent.debug('web', 'checkUserOneTimePassword: success (Messenger).');
953 + func(true, { twoFactorType: 'messenger' });
954 + return;
955 + }
956 + }
957 +
958 // Check hardware key
959 if (user.otphkeys && (user.otphkeys.length > 0) && (typeof (hwtoken) == 'string') && (hwtoken.length > 0)) {
960 var authResponse = null;
@@ -1121,6 +1138,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates, doneF
1138
1139 var email2fa = (((typeof domain.passwordrequirements != 'object') || (domain.passwordrequirements.email2factor != false)) && (domain.mailserver != null) && (user.email != null) && (user.emailVerified == true) && (user.otpekey != null));
1140 var sms2fa = (((typeof domain.passwordrequirements != 'object') || (domain.passwordrequirements.sms2factor != false)) && (parent.smsserver != null) && (user.phone != null));
1141 + var msg2fa = (((typeof domain.passwordrequirements != 'object') || (domain.passwordrequirements.msg2factor != false)) && (parent.msgserver != null) && (parent.msgserver.providers != 0) && (user.msghandle != null));
1142 var push2fa = ((parent.firebase != null) && (user.otpdev != null));
1143
1144 // Check if two factor can be skipped
@@ -1158,6 +1176,19 @@ module.exports.CreateWebServer = function (parent, db, args, certificates, doneF
1176 return;
1177 }
1178
1179 + if ((req.body.hwtoken == '**msg**') && msg2fa) {
1180 + // Cause a token to be sent to the user's messenger account
1181 + user.otpmsg = { k: obj.common.zeroPad(getRandomSixDigitInteger(), 6), d: Date.now() };
1182 + obj.db.SetUser(user);
1183 + parent.debug('web', 'Sending 2FA message to: ' + user.msghandle);
1184 + parent.msgserver.sendToken(domain, user.msghandle, user.otpmsg.k, obj.getLanguageCodes(req));
1185 + // Ask for a login token & confirm message was sent
1186 + req.session.messageid = 6; // "Message sent" message
1187 + req.session.loginmode = 4;
1188 + if (direct === true) { handleRootRequestEx(req, res, domain); } else { res.redirect(domain.url + getQueryPortion(req)); }
1189 + return;
1190 + }
1191 +
1192 // Handle device push notification 2FA request
1193 // We create a browser cookie, send it back and when the browser connects it's web socket, it will trigger the push notification.
1194 if ((req.body.hwtoken == '**push**') && push2fa && ((domain.passwordrequirements == null) || (domain.passwordrequirements.push2factor != false))) {
@@ -1219,6 +1250,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates, doneF
1250 req.session.loginmode = 4;
1251 if ((user.email != null) && (user.emailVerified == true) && (domain.mailserver != null) && (user.otpekey != null)) { req.session.temail = 1; }
1252 if ((user.phone != null) && (parent.smsserver != null)) { req.session.tsms = 1; }
1253 + if ((user.msghandle != null) && (parent.msgserver != null) && (parent.msgserver.providers != 0)) { req.session.tmsg = 1; }
1254 if ((user.otpdev != null) && (parent.firebase != null)) { req.session.tpush = 1; }
1255 req.session.e = parent.encryptSessionData({ tuserid: userid, tuser: xusername, tpass: xpassword });
1256 if (direct === true) { handleRootRequestEx(req, res, domain); } else { res.redirect(domain.url + getQueryPortion(req)); }
@@ -1358,6 +1390,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates, doneF
1390 delete req.session.tpass;
1391 delete req.session.temail;
1392 delete req.session.tsms;
1393 + delete req.session.tmsg;
1394 delete req.session.tpush;
1395 delete req.session.messageid;
1396 delete req.session.passhint;
@@ -1591,6 +1624,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates, doneF
1624 delete req.session.tpass;
1625 delete req.session.temail;
1626 delete req.session.tsms;
1627 + delete req.session.tmsg;
1628 delete req.session.tpush;
1629 delete req.session.messageid;
1630 delete req.session.passhint;
@@ -1673,6 +1707,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates, doneF
1707 delete req.session.tpass;
1708 delete req.session.temail;
1709 delete req.session.tsms;
1710 + delete req.session.tmsg;
1711 delete req.session.tpush;
1712 delete req.session.messageid;
1713 delete req.session.passhint;
@@ -1751,6 +1786,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates, doneF
1786 parent.debug('web', 'handleResetAccountRequest: Invalid 2FA token, try again');
1787 if ((req.body.token != null) || (req.body.hwtoken != null)) {
1788 var sms2fa = (((typeof domain.passwordrequirements != 'object') || (domain.passwordrequirements.sms2factor != false)) && (parent.smsserver != null) && (user.phone != null));
1789 + var msg2fa = (((typeof domain.passwordrequirements != 'object') || (domain.passwordrequirements.msg2factor != false)) && (parent.msgserver != null) && (parent.msgserver.providers != 0) && (user.msghandle != null));
1790 if ((req.body.hwtoken == '**sms**') && sms2fa) {
1791 // Cause a token to be sent to the user's phone number
1792 user.otpsms = { k: obj.common.zeroPad(getRandomSixDigitInteger(), 6), d: Date.now() };
@@ -1758,6 +1794,13 @@ module.exports.CreateWebServer = function (parent, db, args, certificates, doneF
1794 parent.debug('web', 'Sending 2FA SMS for password recovery to: ' + user.phone);
1795 parent.smsserver.sendToken(domain, user.phone, user.otpsms.k, obj.getLanguageCodes(req));
1796 req.session.messageid = 4; // SMS sent.
1797 + } else if ((req.body.hwtoken == '**msg**') && msg2fa) {
1798 + // Cause a token to be sent to the user's messager account
1799 + user.otpmsg = { k: obj.common.zeroPad(getRandomSixDigitInteger(), 6), d: Date.now() };
1800 + obj.db.SetUser(user);
1801 + parent.debug('web', 'Sending 2FA message for password recovery to: ' + user.msghandle);
1802 + parent.msgserver.sendToken(domain, user.msghandle, user.otpmsg.k, obj.getLanguageCodes(req));
1803 + req.session.messageid = 6; // Message sent.
1804 } else {
1805 req.session.messageid = 108; // Invalid token, try again.
1806 const ua = obj.getUserAgentInfo(req);
@@ -3204,6 +3247,8 @@ module.exports.CreateWebServer = function (parent, db, args, certificates, doneF
3247 if ((typeof domain.passwordrequirements == 'object') && (domain.passwordrequirements.email2factor == false)) { otpemail = false; }
3248 var otpsms = (parent.smsserver != null) && (req.session != null) && (req.session.tsms === 1);
3249 if ((typeof domain.passwordrequirements == 'object') && (domain.passwordrequirements.sms2factor == false)) { otpsms = false; }
3250 + var otpmsg = (parent.msgserver != null) && (req.session != null) && (req.session.tmsg === 1);
3251 + if ((typeof domain.passwordrequirements == 'object') && (domain.passwordrequirements.msg2factor == false)) { otpmsg = false; }
3252 var otppush = (parent.firebase != null) && (req.session != null) && (req.session.tpush === 1);
3253 if ((typeof domain.passwordrequirements == 'object') && (domain.passwordrequirements.push2factor == false)) { otppush = false; }
3254 const autofido = ((typeof domain.passwordrequirements == 'object') && (domain.passwordrequirements.autofido2fa == true)); // See if FIDO should be automatically prompted if user account has it.
@@ -3270,6 +3315,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates, doneF
3315 hwstate: hwstate,
3316 otpemail: otpemail,
3317 otpsms: otpsms,
3318 + otpmsg: otpmsg,
3319 otppush: otppush,
3320 autofido: autofido,
3321 twoFactorCookieDays: twoFactorCookieDays,
@@ -7250,6 +7296,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates, doneF
7296 // Figure out if email 2FA is allowed
7297 var email2fa = (((typeof domain.passwordrequirements != 'object') || (domain.passwordrequirements.email2factor != false)) && (domain.mailserver != null) && (user.otpekey != null));
7298 var sms2fa = (((typeof domain.passwordrequirements != 'object') || (domain.passwordrequirements.sms2factor != false)) && (parent.smsserver != null) && (user.phone != null));
7299 + var msg2fa = (((typeof domain.passwordrequirements != 'object') || (domain.passwordrequirements.msg2factor != false)) && (parent.msgserver != null) && (parent.msgserver.providers != 0) && (user.msghandle != null));
7300 //var push2fa = ((parent.firebase != null) && (user.otpdev != null));
7301 if ((typeof command.token != 'string') || (command.token == '**email**') || (command.token == '**sms**')/* || (command.token == '**push**')*/) {
7302 if ((command.token == '**email**') && (email2fa == true)) {
@@ -7259,7 +7306,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates, doneF
7306 parent.debug('web', 'Sending 2FA email to: ' + user.email);
7307 domain.mailserver.sendAccountLoginMail(domain, user.email, user.otpekey.k, obj.getLanguageCodes(req), req.query.key);
7308 // Ask for a login token & confirm email was sent
7262 - try { ws.send(JSON.stringify({ action: 'close', cause: 'noauth', msg: 'tokenrequired', email2fa: email2fa, sms2fa: sms2fa, email2fasent: true, twoFactorCookieDays: twoFactorCookieDays })); ws.close(); } catch (e) { }
7309 + try { ws.send(JSON.stringify({ action: 'close', cause: 'noauth', msg: 'tokenrequired', email2fa: email2fa, sms2fa: sms2fa, msg2fa: msg2fa, email2fasent: true, twoFactorCookieDays: twoFactorCookieDays })); ws.close(); } catch (e) { }
7310 } else if ((command.token == '**sms**') && (sms2fa == true)) {
7311 // Cause a token to be sent to the user's phone number
7312 user.otpsms = { k: obj.common.zeroPad(getRandomSixDigitInteger(), 6), d: Date.now() };
@@ -7267,7 +7314,15 @@ module.exports.CreateWebServer = function (parent, db, args, certificates, doneF
7314 parent.debug('web', 'Sending 2FA SMS to: ' + user.phone);
7315 parent.smsserver.sendToken(domain, user.phone, user.otpsms.k, obj.getLanguageCodes(req));
7316 // Ask for a login token & confirm sms was sent
7270 - try { ws.send(JSON.stringify({ action: 'close', cause: 'noauth', msg: 'tokenrequired', email2fa: email2fa, sms2fa: sms2fa, sms2fasent: true, twoFactorCookieDays: twoFactorCookieDays })); ws.close(); } catch (e) { }
7317 + try { ws.send(JSON.stringify({ action: 'close', cause: 'noauth', msg: 'tokenrequired', email2fa: email2fa, sms2fa: sms2fa, msg2fa: msg2fa, sms2fasent: true, twoFactorCookieDays: twoFactorCookieDays })); ws.close(); } catch (e) { }
7318 + } else if ((command.token == '**msg**') && (msg2fa == true)) {
7319 + // Cause a token to be sent to the user's messenger account
7320 + user.otpmsg = { k: obj.common.zeroPad(getRandomSixDigitInteger(), 6), d: Date.now() };
7321 + obj.db.SetUser(user);
7322 + parent.debug('web', 'Sending 2FA message to: ' + user.phone);
7323 + parent.msgserver.sendToken(domain, user.msghandle, user.otpmsg.k, obj.getLanguageCodes(req));
7324 + // Ask for a login token & confirm sms was sent
7325 + try { ws.send(JSON.stringify({ action: 'close', cause: 'noauth', msg: 'tokenrequired', email2fa: email2fa, sms2fa: sms2fa, msg2fa: msg2fa, msg2fasent: true, twoFactorCookieDays: twoFactorCookieDays })); ws.close(); } catch (e) { }
7326 /*
7327 } else if ((command.token == '**push**') && (push2fa == true)) {
7328 // Cause push notification to device
@@ -7311,7 +7366,8 @@ module.exports.CreateWebServer = function (parent, db, args, certificates, doneF
7366 parent.debug('web', 'Invalid login, asking for email validation');
7367 var email2fa = (((typeof domain.passwordrequirements != 'object') || (domain.passwordrequirements.email2factor != false)) && (domain.mailserver != null) && (user.otpekey != null));
7368 var sms2fa = (((typeof domain.passwordrequirements != 'object') || (domain.passwordrequirements.sms2factor != false)) && (parent.smsserver != null) && (user.phone != null));
7314 - try { ws.send(JSON.stringify({ action: 'close', cause: 'emailvalidation', msg: 'emailvalidationrequired', email2fa: email2fa, sms2fa: sms2fa, email2fasent: true })); ws.close(); } catch (e) { }
7369 + var msg2fa = (((typeof domain.passwordrequirements != 'object') || (domain.passwordrequirements.msg2factor != false)) && (parent.msgserver != null) && (parent.msgserver.providers != 0) && (user.msghandle != null));
7370 + try { ws.send(JSON.stringify({ action: 'close', cause: 'emailvalidation', msg: 'emailvalidationrequired', email2fa: email2fa, sms2fa: sms2fa, msg2fa: msg2fa, email2fasent: true })); ws.close(); } catch (e) { }
7371 } else {
7372 // We are authenticated
7373 ws._socket.pause();
@@ -7396,6 +7452,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates, doneF
7452 // Figure out if email 2FA is allowed
7453 var email2fa = (((typeof domain.passwordrequirements != 'object') || (domain.passwordrequirements.email2factor != false)) && (domain.mailserver != null) && (user.otpekey != null));
7454 var sms2fa = (((typeof domain.passwordrequirements != 'object') || (domain.passwordrequirements.sms2factor != false)) && (parent.smsserver != null) && (user.phone != null));
7455 + var msg2fa = (((typeof domain.passwordrequirements != 'object') || (domain.passwordrequirements.msg2factor != false)) && (parent.msgserver != null) && (parent.msgserver.providers != 0) && (user.msghandle != null));
7456 //var push2fa = ((parent.firebase != null) && (user.otpdev != null));
7457 if ((typeof req.query.token != 'string') || (req.query.token == '**email**') || (req.query.token == '**sms**')/* || (req.query.token == '**push**')*/) {
7458 if ((req.query.token == '**email**') && (email2fa == true)) {
@@ -7405,7 +7462,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates, doneF
7462 parent.debug('web', 'Sending 2FA email to: ' + user.email);
7463 domain.mailserver.sendAccountLoginMail(domain, user.email, user.otpekey.k, obj.getLanguageCodes(req), req.query.key);
7464 // Ask for a login token & confirm email was sent
7408 - try { ws.send(JSON.stringify({ action: 'close', cause: 'noauth', msg: 'tokenrequired', email2fa: email2fa, sms2fa: sms2fa, email2fasent: true, twoFactorCookieDays: twoFactorCookieDays })); ws.close(); } catch (e) { }
7465 + try { ws.send(JSON.stringify({ action: 'close', cause: 'noauth', msg: 'tokenrequired', email2fa: email2fa, sms2fa: sms2fa, msg2fa: msg2fa, email2fasent: true, twoFactorCookieDays: twoFactorCookieDays })); ws.close(); } catch (e) { }
7466 } else if ((req.query.token == '**sms**') && (sms2fa == true)) {
7467 // Cause a token to be sent to the user's phone number
7468 user.otpsms = { k: obj.common.zeroPad(getRandomSixDigitInteger(), 6), d: Date.now() };
@@ -7413,7 +7470,15 @@ module.exports.CreateWebServer = function (parent, db, args, certificates, doneF
7470 parent.debug('web', 'Sending 2FA SMS to: ' + user.phone);
7471 parent.smsserver.sendToken(domain, user.phone, user.otpsms.k, obj.getLanguageCodes(req));
7472 // Ask for a login token & confirm sms was sent
7416 - try { ws.send(JSON.stringify({ action: 'close', cause: 'noauth', msg: 'tokenrequired', email2fa: email2fa, sms2fa: sms2fa, sms2fasent: true, twoFactorCookieDays: twoFactorCookieDays })); ws.close(); } catch (e) { }
7473 + try { ws.send(JSON.stringify({ action: 'close', cause: 'noauth', msg: 'tokenrequired', email2fa: email2fa, sms2fa: sms2fa, msg2fa: msg2fa, sms2fasent: true, twoFactorCookieDays: twoFactorCookieDays })); ws.close(); } catch (e) { }
7474 + } else if ((req.query.token == '**msg**') && (msg2fa == true)) {
7475 + // Cause a token to be sent to the user's messenger account
7476 + user.otpmsg = { k: obj.common.zeroPad(getRandomSixDigitInteger(), 6), d: Date.now() };
7477 + obj.db.SetUser(user);
7478 + parent.debug('web', 'Sending 2FA message to: ' + user.msghandle);
7479 + parent.msgserver.sendToken(domain, user.msghandle, user.otpmsg.k, obj.getLanguageCodes(req));
7480 + // Ask for a login token & confirm message was sent
7481 + try { ws.send(JSON.stringify({ action: 'close', cause: 'noauth', msg: 'tokenrequired', email2fa: email2fa, sms2fa: sms2fa, msg2fa: msg2fa, msg2fasent: true, twoFactorCookieDays: twoFactorCookieDays })); ws.close(); } catch (e) { }
7482 /*
7483 } else if ((command.token == '**push**') && (push2fa == true)) {
7484 // Cause push notification to device
@@ -7454,7 +7519,8 @@ module.exports.CreateWebServer = function (parent, db, args, certificates, doneF
7519 parent.debug('web', 'Invalid login, asking for email validation');
7520 var email2fa = (((typeof domain.passwordrequirements != 'object') || (domain.passwordrequirements.email2factor != false)) && (domain.mailserver != null) && (user.otpekey != null));
7521 var sms2fa = (((typeof domain.passwordrequirements != 'object') || (domain.passwordrequirements.sms2factor != false)) && (parent.smsserver != null) && (user.phone != null));
7457 - try { ws.send(JSON.stringify({ action: 'close', cause: 'emailvalidation', msg: 'emailvalidationrequired', email2fa: email2fa, sms2fa: sms2fa, email2fasent: true })); ws.close(); } catch (e) { }
7522 + var msg2fa = (((typeof domain.passwordrequirements != 'object') || (domain.passwordrequirements.msg2factor != false)) && (parent.msgserver != null) && (parent.msgserver.providers != 0) && (user.msghandle != null));
7523 + try { ws.send(JSON.stringify({ action: 'close', cause: 'emailvalidation', msg: 'emailvalidationrequired', email2fa: email2fa, sms2fa: sms2fa, msg2fa: msg2fa, email2fasent: true })); ws.close(); } catch (e) { }
7524 } else {
7525 // We are authenticated
7526 func(ws, req, domain, user);
@@ -7533,6 +7599,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates, doneF
7599 // Figure out if email 2FA is allowed
7600 var email2fa = (((typeof domain.passwordrequirements != 'object') || (domain.passwordrequirements.email2factor != false)) && (domain.mailserver != null) && (user.otpekey != null));
7601 var sms2fa = (((typeof domain.passwordrequirements != 'object') || (domain.passwordrequirements.sms2factor != false)) && (parent.smsserver != null) && (user.phone != null));
7602 + var msg2fa = (((typeof domain.passwordrequirements != 'object') || (domain.passwordrequirements.msg2factor != false)) && (parent.msgserver != null) && (parent.msgserver.providers != 0) && (user.msghandle != null));
7603 if (s.length != 3) {
7604 try { ws.send(JSON.stringify({ action: 'close', cause: 'noauth', msg: 'tokenrequired', email2fa: email2fa, sms2fa: sms2fa, twoFactorCookieDays: twoFactorCookieDays })); ws.close(); } catch (e) { }
7605 } else {
@@ -7554,6 +7621,14 @@ module.exports.CreateWebServer = function (parent, db, args, certificates, doneF
7621 parent.smsserver.sendToken(domain, user.phone, user.otpsms.k, obj.getLanguageCodes(req));
7622 // Ask for a login token & confirm sms was sent
7623 try { ws.send(JSON.stringify({ action: 'close', cause: 'noauth', msg: 'tokenrequired', sms2fa: sms2fa, sms2fasent: true, twoFactorCookieDays: twoFactorCookieDays })); ws.close(); } catch (e) { }
7624 + } else if ((s[2] == '**msg**') && (msg2fa == true)) {
7625 + // Cause a token to be sent to the user's phone number
7626 + user.otpmsg = { k: obj.common.zeroPad(getRandomSixDigitInteger(), 6), d: Date.now() };
7627 + obj.db.SetUser(user);
7628 + parent.debug('web', 'Sending 2FA message to: ' + user.msghandle);
7629 + parent.msgserver.sendToken(domain, user.msghandle, user.otpmsg.k, obj.getLanguageCodes(req));
7630 + // Ask for a login token & confirm sms was sent
7631 + try { ws.send(JSON.stringify({ action: 'close', cause: 'noauth', msg: 'tokenrequired', msg2fa: msg2fa, msg2fasent: true, twoFactorCookieDays: twoFactorCookieDays })); ws.close(); } catch (e) { }
7632 } else {
7633 // Ask for a login token
7634 try { ws.send(JSON.stringify({ action: 'close', cause: 'noauth', msg: 'tokenrequired', email2fa: email2fa, twoFactorCookieDays: twoFactorCookieDays })); ws.close(); } catch (e) { }
@@ -8173,6 +8248,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates, doneF
8248 delete user2.subscriptions;
8249 delete user2.passtype;
8250 delete user2.otpsms;
8251 + delete user2.otpmsg;
8252 if ((typeof user2.otpekey == 'object') && (user2.otpekey != null)) { user2.otpekey = 1; } // Indicates that email 2FA is enabled.
8253 if ((typeof user2.otpsecret == 'string') && (user2.otpsecret != null)) { user2.otpsecret = 1; } // Indicates a time secret is present.
8254 if ((typeof user2.otpkeys == 'object') && (user2.otpkeys != null)) { user2.otpkeys = 0; if (user.otpkeys != null) { for (var i = 0; i < user.otpkeys.keys.length; i++) { if (user.otpkeys.keys[i].u == true) { user2.otpkeys = 1; } } } } // Indicates the number of one time backup codes that are active.