diff --git a/meshmessaging.js b/meshmessaging.js index 9f990a6b9c..e5580bf410 100644 --- a/meshmessaging.js +++ b/meshmessaging.js @@ -69,7 +69,7 @@ module.exports.CreateServer = function (parent) { async function sendTelegramMessage(to, msg, func) { if (obj.telegramClient == null) return; parent.debug('email', 'Sending Telegram message to: ' + to.substring(9) + ': ' + msg); - try { await obj.telegramClient.sendMessage(to.substring(9), { message: msg }); func(true); } catch (ex) { func(false, ex); } + try { await obj.telegramClient.sendMessage(to.substring(9), { message: msg }); if (func != null) { func(true); } } catch (ex) { if (func != null) { func(false, ex); } } } sendTelegramMessage(to, msg, func); } else { diff --git a/meshuser.js b/meshuser.js index e0f9666f9a..8e37992604 100644 --- a/meshuser.js +++ b/meshuser.js @@ -7941,10 +7941,12 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use function count2factoraAuths() { var email2fa = (((typeof domain.passwordrequirements != 'object') || (domain.passwordrequirements.email2factor != false)) && (domain.mailserver != null)); var sms2fa = ((parent.parent.smsserver != null) && ((typeof domain.passwordrequirements != 'object') || (domain.passwordrequirements.sms2factor != false))); + var msg2fa = ((parent.parent.msgserver != null) && (parent.parent.msgserver.providers != 0) && ((typeof domain.passwordrequirements != 'object') || (domain.passwordrequirements.msg2factor != false))); var authFactorCount = 0; if (typeof user.otpsecret == 'string') { authFactorCount++; } // Authenticator time factor if (email2fa && (user.otpekey != null)) { authFactorCount++; } // EMail factor if (sms2fa && (user.phone != null)) { authFactorCount++; } // SMS factor + if (msg2fa && (user.msghandle != null)) { authFactorCount++; } // Messaging factor if (user.otphkeys != null) { authFactorCount += user.otphkeys.length; } // FIDO hardware factor if ((authFactorCount > 0) && (user.otpkeys != null)) { authFactorCount++; } // Backup keys return authFactorCount; diff --git a/public/images/login/2fa-messaging-48.png b/public/images/login/2fa-messaging-48.png new file mode 100644 index 0000000000..8332b01b7c Binary files /dev/null and b/public/images/login/2fa-messaging-48.png differ diff --git a/public/images/login/2fa-messaging-96.png b/public/images/login/2fa-messaging-96.png new file mode 100644 index 0000000000..98d9cb0034 Binary files /dev/null and b/public/images/login/2fa-messaging-96.png differ diff --git a/views/default.handlebars b/views/default.handlebars index 9b145db556..8bfbcffbab 100644 --- a/views/default.handlebars +++ b/views/default.handlebars @@ -2230,6 +2230,7 @@ if (userinfo.otphkeys > 0) { authFactorCount += userinfo.otphkeys; } // FIDO hardware factor if ((features & 0x00800000) && (userinfo.otpekey == 1)) { authFactorCount++; } // EMail factor if ((features & 0x02000000) && (features & 0x04000000) && (userinfo.phone != null)) { authFactorCount++; } // SMS factor + if ((features2 & 0x02000000) && (features2 & 0x04000000) && (userinfo.msghandle != null)) { authFactorCount++; } // Messaging factor if ((authFactorCount > 0) && (userinfo.otpkeys > 0) && ((features2 & 0x40000) == 0)) { authFactorCount++; } // Backup keys return authFactorCount; } @@ -16738,6 +16739,7 @@ if (v == 'fido') { return "FIDO key"; } if (v == 'sms') { return "SMS message"; } if (v == 'hwotp') { return "Hardware OTP"; } + if (v == 'messenger') { return "Messenging"; } if (v == 'push') { return "Push Notification"; } if (v == 'otp') { return "One-Time Password"; } if (v == 'cookie') { return "Remember Device"; } diff --git a/views/login.handlebars b/views/login.handlebars index 9d44fead78..70296ed40e 100644 --- a/views/login.handlebars +++ b/views/login.handlebars @@ -190,6 +190,7 @@ + @@ -217,6 +218,7 @@ + @@ -335,6 +337,7 @@ var publicKeyCredentialRequestOptions = null; var otpemail = (decodeURIComponent('{{{otpemail}}}') === 'true'); var otpsms = (decodeURIComponent('{{{otpsms}}}') === 'true'); + var otpmsg = (decodeURIComponent('{{{otpmsg}}}') === 'true'); var autofido = (decodeURIComponent('{{{autofido}}}') === 'true'); var twoFactorCookieDays = parseInt('{{{twoFactorCookieDays}}}'); var authStrategies = '{{{authStrategies}}}'.split(','); @@ -344,7 +347,7 @@ // Display the right server message var i; var messageid = parseInt('{{{messageid}}}'); - var okmessages = ['', "If valid, reset mail sent.", "Email sent.", "Email verification required, check your mailbox and click the confirmation link.", "SMS sent."]; + 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."]; 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."]; if (messageid > 0) { var msg = ''; @@ -455,6 +458,7 @@ QV('securityKeyButton', twofakey); QV('emailKeyButton', otpemail && (messageid != 2) && (messageid != 4)); QV('smsKeyButton', otpsms && (messageid != 2) && (messageid != 4)); + QV('msgKeyButton', otpmsg && (messageid != 2) && (messageid != 4)); // If hardware key is an option, trigger it now if (autofido && twofakey) { setTimeout(function () { useSecurityKey(1); }, 300); } @@ -467,6 +471,7 @@ QV('securityKeyButton2', twofakey); QV('emailKeyButton2', otpemail && (messageid != 2) && (messageid != 4)); QV('smsKeyButton2', otpsms && (messageid != 2) && (messageid != 4)); + QV('msgKeyButton2', otpmsg && (messageid != 2) && (messageid != 4)); // If hardware key is an option, trigger it now if (autofido && twofakey) { setTimeout(function () { useSecurityKey(2); }, 300); } @@ -580,6 +585,23 @@ } } + function useMsgToken(panelAction) { + if (otpmsg != true) return; + setDialogMode(1, "Secure Login", 3, useMsgTokenEx, "Send token to messaging application?", panelAction); + } + + function useMsgTokenEx(b, panelAction) { + if (panelAction == 1) { + Q('hwtokenInput').value = '**msg**'; + QE('tokenOkButton', true); + Q('tokenOkButton').click(); + } else if (panelAction == 2) { + Q('resetHwtokenInput').value = '**msg**'; + QE('resetTokenOkButton', true); + Q('resetTokenOkButton').click(); + } + } + function showPassHint(e) { messagebox("Password Hint", passhint); haltEvent(e); diff --git a/views/login2.handlebars b/views/login2.handlebars index b267637dfa..a04325f12f 100644 --- a/views/login2.handlebars +++ b/views/login2.handlebars @@ -217,6 +217,7 @@
+
@@ -251,6 +252,7 @@
+
@@ -389,6 +391,7 @@ var publicKeyCredentialRequestOptions = null; var otpemail = (decodeURIComponent('{{{otpemail}}}') === 'true'); var otpsms = (decodeURIComponent('{{{otpsms}}}') === 'true'); + var otpmsg = (decodeURIComponent('{{{otpmsg}}}') === 'true'); var otppush = (decodeURIComponent('{{{otppush}}}') === 'true'); var autofido = (decodeURIComponent('{{{autofido}}}') === 'true'); var twoFactorCookieDays = parseInt('{{{twoFactorCookieDays}}}'); @@ -414,7 +417,7 @@ // Display the right server message var i; var messageid = parseInt('{{{messageid}}}'); - var okmessages = ['', "If valid, reset mail sent.", "Email sent.", "Email verification required, check your mailbox and click the confirmation link.", "SMS sent.", "Sending notification..."]; + 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."]; 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."]; if (messageid > 0) { var msg = ''; @@ -508,12 +511,14 @@ var twofakey = (hardwareKeyChallenge != null) && (hardwareKeyChallenge.type == 'webAuthn'); var emailkey = otpemail && (messageid != 2) && (messageid != 4); var smskey = otpsms && (messageid != 2) && (messageid != 4); + var msgkey = otpmsg && (messageid != 2) && (messageid != 4); var pushkey = otppush && (messageid != 2) && (messageid != 4); QV('securityKeyButton', twofakey); QV('emailKeyButton', emailkey); QV('smsKeyButton', smskey); + QV('msgKeyButton', msgkey); QV('pushKeyButton', pushkey); - QV('2farow', twofakey || emailkey || smskey || pushkey); + QV('2farow', twofakey || emailkey || smskey || msgkey || pushkey); // If hardware key is an option, trigger it now if (autofido && twofakey) { setTimeout(function () { useSecurityKey(1); }, 300); } @@ -525,12 +530,14 @@ var twofakey = (hardwareKeyChallenge != null) && (hardwareKeyChallenge.type == 'webAuthn'); var emailkey = otpemail && (messageid != 2) && (messageid != 4); var smskey = otpsms && (messageid != 2) && (messageid != 4); + var msgkey = otpmsg && (messageid != 2) && (messageid != 4); var pushkey = otppush && (messageid != 2) && (messageid != 4); QV('securityKeyButton2', twofakey); QV('emailKeyButton2', emailkey); QV('smsKeyButton2', smskey); + QV('msgKeyButton2', msgkey); QV('pushKeyButton', pushkey); - QV('2farow2', twofakey || emailkey || smskey || pushkey); + QV('2farow2', twofakey || emailkey || smskey || msgkey || pushkey); // If hardware key is an option, trigger it now if (autofido && twofakey) { setTimeout(function () { useSecurityKey(2); }, 300); } @@ -645,6 +652,23 @@ } } + function useMsgToken(panelAction) { + if (otpmsg != true) return; + setDialogMode(1, "Secure Login", 3, useMsgTokenEx, "Send token to messaging application?", panelAction); + } + + function useMsgTokenEx(b, panelAction) { + if (panelAction == 1) { + Q('hwtokenInput').value = '**msg**'; + QE('tokenOkButton', true); + Q('tokenOkButton').click(); + } else if (panelAction == 2) { + Q('resetHwtokenInput').value = '**msg**'; + QE('resetTokenOkButton', true); + Q('resetTokenOkButton').click(); + } + } + function usePushToken(panelAction) { if (panelAction == 1) { Q('hwtokenInput').value = '**push**'; diff --git a/webserver.js b/webserver.js index 928ecd7b98..540efd9fc0 100644 --- a/webserver.js +++ b/webserver.js @@ -892,8 +892,11 @@ module.exports.CreateWebServer = function (parent, db, args, certificates, doneF // See if SMS 2FA is available var sms2fa = (((typeof domain.passwordrequirements != 'object') || (domain.passwordrequirements.sms2factor != false)) && (parent.smsserver != null) && (user.phone != null)); + // See if Messenger 2FA is available + var msg2fa = (((typeof domain.passwordrequirements != 'object') || (domain.passwordrequirements.msg2factor != false)) && (parent.msgserver != null) && (parent.msgserver.providers != 0) && (user.msghandle != null)); + // Check if a 2nd factor is present - 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)))); + 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)))); } // Check the 2-step auth token @@ -907,6 +910,8 @@ module.exports.CreateWebServer = function (parent, db, args, certificates, doneF if ((typeof domain.passwordrequirements == 'object') && (domain.passwordrequirements.email2factor == false)) { otpemail = false; } var otpsms = (parent.smsserver != null); if ((typeof domain.passwordrequirements == 'object') && (domain.passwordrequirements.sms2factor == false)) { otpsms = false; } + var otpmsg = ((parent.msgserver != null) && (parent.msgserver.providers != 0)); + if ((typeof domain.passwordrequirements == 'object') && (domain.passwordrequirements.msg2factor == false)) { otpmsg = false; } // Check 2FA login cookie if ((token != null) && (token.startsWith('cookie='))) { @@ -926,7 +931,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates, doneF } } - // Check sms key + // Check SMS key if ((otpsms) && (user.phone != null) && (user.otpsms != null) && (user.otpsms.d != null) && (user.otpsms.k === token)) { var deltaTime = (Date.now() - user.otpsms.d); 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 } } + // Check messenger key + if ((otpmsg) && (user.msghandle != null) && (user.otpmsg != null) && (user.otpmsg.d != null) && (user.otpmsg.k === token)) { + var deltaTime = (Date.now() - user.otpmsg.d); + if ((deltaTime > 0) && (deltaTime < 300000)) { // Allow 5 minutes to use the Messenger token (10000 * 60 * 5). + delete user.otpmsg; + obj.db.SetUser(user); + parent.debug('web', 'checkUserOneTimePassword: success (Messenger).'); + func(true, { twoFactorType: 'messenger' }); + return; + } + } + // Check hardware key if (user.otphkeys && (user.otphkeys.length > 0) && (typeof (hwtoken) == 'string') && (hwtoken.length > 0)) { var authResponse = null; @@ -1121,6 +1138,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates, doneF var email2fa = (((typeof domain.passwordrequirements != 'object') || (domain.passwordrequirements.email2factor != false)) && (domain.mailserver != null) && (user.email != null) && (user.emailVerified == true) && (user.otpekey != null)); var sms2fa = (((typeof domain.passwordrequirements != 'object') || (domain.passwordrequirements.sms2factor != false)) && (parent.smsserver != null) && (user.phone != null)); + var msg2fa = (((typeof domain.passwordrequirements != 'object') || (domain.passwordrequirements.msg2factor != false)) && (parent.msgserver != null) && (parent.msgserver.providers != 0) && (user.msghandle != null)); var push2fa = ((parent.firebase != null) && (user.otpdev != null)); // Check if two factor can be skipped @@ -1158,6 +1176,19 @@ module.exports.CreateWebServer = function (parent, db, args, certificates, doneF return; } + if ((req.body.hwtoken == '**msg**') && msg2fa) { + // Cause a token to be sent to the user's messenger account + user.otpmsg = { k: obj.common.zeroPad(getRandomSixDigitInteger(), 6), d: Date.now() }; + obj.db.SetUser(user); + parent.debug('web', 'Sending 2FA message to: ' + user.msghandle); + parent.msgserver.sendToken(domain, user.msghandle, user.otpmsg.k, obj.getLanguageCodes(req)); + // Ask for a login token & confirm message was sent + req.session.messageid = 6; // "Message sent" message + req.session.loginmode = 4; + if (direct === true) { handleRootRequestEx(req, res, domain); } else { res.redirect(domain.url + getQueryPortion(req)); } + return; + } + // Handle device push notification 2FA request // We create a browser cookie, send it back and when the browser connects it's web socket, it will trigger the push notification. 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 req.session.loginmode = 4; if ((user.email != null) && (user.emailVerified == true) && (domain.mailserver != null) && (user.otpekey != null)) { req.session.temail = 1; } if ((user.phone != null) && (parent.smsserver != null)) { req.session.tsms = 1; } + if ((user.msghandle != null) && (parent.msgserver != null) && (parent.msgserver.providers != 0)) { req.session.tmsg = 1; } if ((user.otpdev != null) && (parent.firebase != null)) { req.session.tpush = 1; } req.session.e = parent.encryptSessionData({ tuserid: userid, tuser: xusername, tpass: xpassword }); 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 delete req.session.tpass; delete req.session.temail; delete req.session.tsms; + delete req.session.tmsg; delete req.session.tpush; delete req.session.messageid; delete req.session.passhint; @@ -1591,6 +1624,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates, doneF delete req.session.tpass; delete req.session.temail; delete req.session.tsms; + delete req.session.tmsg; delete req.session.tpush; delete req.session.messageid; delete req.session.passhint; @@ -1673,6 +1707,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates, doneF delete req.session.tpass; delete req.session.temail; delete req.session.tsms; + delete req.session.tmsg; delete req.session.tpush; delete req.session.messageid; delete req.session.passhint; @@ -1751,6 +1786,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates, doneF parent.debug('web', 'handleResetAccountRequest: Invalid 2FA token, try again'); if ((req.body.token != null) || (req.body.hwtoken != null)) { var sms2fa = (((typeof domain.passwordrequirements != 'object') || (domain.passwordrequirements.sms2factor != false)) && (parent.smsserver != null) && (user.phone != null)); + var msg2fa = (((typeof domain.passwordrequirements != 'object') || (domain.passwordrequirements.msg2factor != false)) && (parent.msgserver != null) && (parent.msgserver.providers != 0) && (user.msghandle != null)); if ((req.body.hwtoken == '**sms**') && sms2fa) { // Cause a token to be sent to the user's phone number user.otpsms = { k: obj.common.zeroPad(getRandomSixDigitInteger(), 6), d: Date.now() }; @@ -1758,6 +1794,13 @@ module.exports.CreateWebServer = function (parent, db, args, certificates, doneF parent.debug('web', 'Sending 2FA SMS for password recovery to: ' + user.phone); parent.smsserver.sendToken(domain, user.phone, user.otpsms.k, obj.getLanguageCodes(req)); req.session.messageid = 4; // SMS sent. + } else if ((req.body.hwtoken == '**msg**') && msg2fa) { + // Cause a token to be sent to the user's messager account + user.otpmsg = { k: obj.common.zeroPad(getRandomSixDigitInteger(), 6), d: Date.now() }; + obj.db.SetUser(user); + parent.debug('web', 'Sending 2FA message for password recovery to: ' + user.msghandle); + parent.msgserver.sendToken(domain, user.msghandle, user.otpmsg.k, obj.getLanguageCodes(req)); + req.session.messageid = 6; // Message sent. } else { req.session.messageid = 108; // Invalid token, try again. const ua = obj.getUserAgentInfo(req); @@ -3204,6 +3247,8 @@ module.exports.CreateWebServer = function (parent, db, args, certificates, doneF if ((typeof domain.passwordrequirements == 'object') && (domain.passwordrequirements.email2factor == false)) { otpemail = false; } var otpsms = (parent.smsserver != null) && (req.session != null) && (req.session.tsms === 1); if ((typeof domain.passwordrequirements == 'object') && (domain.passwordrequirements.sms2factor == false)) { otpsms = false; } + var otpmsg = (parent.msgserver != null) && (req.session != null) && (req.session.tmsg === 1); + if ((typeof domain.passwordrequirements == 'object') && (domain.passwordrequirements.msg2factor == false)) { otpmsg = false; } var otppush = (parent.firebase != null) && (req.session != null) && (req.session.tpush === 1); if ((typeof domain.passwordrequirements == 'object') && (domain.passwordrequirements.push2factor == false)) { otppush = false; } 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 hwstate: hwstate, otpemail: otpemail, otpsms: otpsms, + otpmsg: otpmsg, otppush: otppush, autofido: autofido, twoFactorCookieDays: twoFactorCookieDays, @@ -7250,6 +7296,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates, doneF // Figure out if email 2FA is allowed var email2fa = (((typeof domain.passwordrequirements != 'object') || (domain.passwordrequirements.email2factor != false)) && (domain.mailserver != null) && (user.otpekey != null)); var sms2fa = (((typeof domain.passwordrequirements != 'object') || (domain.passwordrequirements.sms2factor != false)) && (parent.smsserver != null) && (user.phone != null)); + var msg2fa = (((typeof domain.passwordrequirements != 'object') || (domain.passwordrequirements.msg2factor != false)) && (parent.msgserver != null) && (parent.msgserver.providers != 0) && (user.msghandle != null)); //var push2fa = ((parent.firebase != null) && (user.otpdev != null)); if ((typeof command.token != 'string') || (command.token == '**email**') || (command.token == '**sms**')/* || (command.token == '**push**')*/) { if ((command.token == '**email**') && (email2fa == true)) { @@ -7259,7 +7306,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates, doneF parent.debug('web', 'Sending 2FA email to: ' + user.email); domain.mailserver.sendAccountLoginMail(domain, user.email, user.otpekey.k, obj.getLanguageCodes(req), req.query.key); // Ask for a login token & confirm email was sent - try { ws.send(JSON.stringify({ action: 'close', cause: 'noauth', msg: 'tokenrequired', email2fa: email2fa, sms2fa: sms2fa, email2fasent: true, twoFactorCookieDays: twoFactorCookieDays })); ws.close(); } catch (e) { } + 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) { } } else if ((command.token == '**sms**') && (sms2fa == true)) { // Cause a token to be sent to the user's phone number user.otpsms = { k: obj.common.zeroPad(getRandomSixDigitInteger(), 6), d: Date.now() }; @@ -7267,7 +7314,15 @@ module.exports.CreateWebServer = function (parent, db, args, certificates, doneF parent.debug('web', 'Sending 2FA SMS to: ' + user.phone); parent.smsserver.sendToken(domain, user.phone, user.otpsms.k, obj.getLanguageCodes(req)); // Ask for a login token & confirm sms was sent - try { ws.send(JSON.stringify({ action: 'close', cause: 'noauth', msg: 'tokenrequired', email2fa: email2fa, sms2fa: sms2fa, sms2fasent: true, twoFactorCookieDays: twoFactorCookieDays })); ws.close(); } catch (e) { } + 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) { } + } else if ((command.token == '**msg**') && (msg2fa == true)) { + // Cause a token to be sent to the user's messenger account + user.otpmsg = { k: obj.common.zeroPad(getRandomSixDigitInteger(), 6), d: Date.now() }; + obj.db.SetUser(user); + parent.debug('web', 'Sending 2FA message to: ' + user.phone); + parent.msgserver.sendToken(domain, user.msghandle, user.otpmsg.k, obj.getLanguageCodes(req)); + // Ask for a login token & confirm sms was sent + 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) { } /* } else if ((command.token == '**push**') && (push2fa == true)) { // Cause push notification to device @@ -7311,7 +7366,8 @@ module.exports.CreateWebServer = function (parent, db, args, certificates, doneF parent.debug('web', 'Invalid login, asking for email validation'); var email2fa = (((typeof domain.passwordrequirements != 'object') || (domain.passwordrequirements.email2factor != false)) && (domain.mailserver != null) && (user.otpekey != null)); var sms2fa = (((typeof domain.passwordrequirements != 'object') || (domain.passwordrequirements.sms2factor != false)) && (parent.smsserver != null) && (user.phone != null)); - try { ws.send(JSON.stringify({ action: 'close', cause: 'emailvalidation', msg: 'emailvalidationrequired', email2fa: email2fa, sms2fa: sms2fa, email2fasent: true })); ws.close(); } catch (e) { } + var msg2fa = (((typeof domain.passwordrequirements != 'object') || (domain.passwordrequirements.msg2factor != false)) && (parent.msgserver != null) && (parent.msgserver.providers != 0) && (user.msghandle != null)); + try { ws.send(JSON.stringify({ action: 'close', cause: 'emailvalidation', msg: 'emailvalidationrequired', email2fa: email2fa, sms2fa: sms2fa, msg2fa: msg2fa, email2fasent: true })); ws.close(); } catch (e) { } } else { // We are authenticated ws._socket.pause(); @@ -7396,6 +7452,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates, doneF // Figure out if email 2FA is allowed var email2fa = (((typeof domain.passwordrequirements != 'object') || (domain.passwordrequirements.email2factor != false)) && (domain.mailserver != null) && (user.otpekey != null)); var sms2fa = (((typeof domain.passwordrequirements != 'object') || (domain.passwordrequirements.sms2factor != false)) && (parent.smsserver != null) && (user.phone != null)); + var msg2fa = (((typeof domain.passwordrequirements != 'object') || (domain.passwordrequirements.msg2factor != false)) && (parent.msgserver != null) && (parent.msgserver.providers != 0) && (user.msghandle != null)); //var push2fa = ((parent.firebase != null) && (user.otpdev != null)); if ((typeof req.query.token != 'string') || (req.query.token == '**email**') || (req.query.token == '**sms**')/* || (req.query.token == '**push**')*/) { if ((req.query.token == '**email**') && (email2fa == true)) { @@ -7405,7 +7462,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates, doneF parent.debug('web', 'Sending 2FA email to: ' + user.email); domain.mailserver.sendAccountLoginMail(domain, user.email, user.otpekey.k, obj.getLanguageCodes(req), req.query.key); // Ask for a login token & confirm email was sent - try { ws.send(JSON.stringify({ action: 'close', cause: 'noauth', msg: 'tokenrequired', email2fa: email2fa, sms2fa: sms2fa, email2fasent: true, twoFactorCookieDays: twoFactorCookieDays })); ws.close(); } catch (e) { } + 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) { } } else if ((req.query.token == '**sms**') && (sms2fa == true)) { // Cause a token to be sent to the user's phone number user.otpsms = { k: obj.common.zeroPad(getRandomSixDigitInteger(), 6), d: Date.now() }; @@ -7413,7 +7470,15 @@ module.exports.CreateWebServer = function (parent, db, args, certificates, doneF parent.debug('web', 'Sending 2FA SMS to: ' + user.phone); parent.smsserver.sendToken(domain, user.phone, user.otpsms.k, obj.getLanguageCodes(req)); // Ask for a login token & confirm sms was sent - try { ws.send(JSON.stringify({ action: 'close', cause: 'noauth', msg: 'tokenrequired', email2fa: email2fa, sms2fa: sms2fa, sms2fasent: true, twoFactorCookieDays: twoFactorCookieDays })); ws.close(); } catch (e) { } + 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) { } + } else if ((req.query.token == '**msg**') && (msg2fa == true)) { + // Cause a token to be sent to the user's messenger account + user.otpmsg = { k: obj.common.zeroPad(getRandomSixDigitInteger(), 6), d: Date.now() }; + obj.db.SetUser(user); + parent.debug('web', 'Sending 2FA message to: ' + user.msghandle); + parent.msgserver.sendToken(domain, user.msghandle, user.otpmsg.k, obj.getLanguageCodes(req)); + // Ask for a login token & confirm message was sent + 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) { } /* } else if ((command.token == '**push**') && (push2fa == true)) { // Cause push notification to device @@ -7454,7 +7519,8 @@ module.exports.CreateWebServer = function (parent, db, args, certificates, doneF parent.debug('web', 'Invalid login, asking for email validation'); var email2fa = (((typeof domain.passwordrequirements != 'object') || (domain.passwordrequirements.email2factor != false)) && (domain.mailserver != null) && (user.otpekey != null)); var sms2fa = (((typeof domain.passwordrequirements != 'object') || (domain.passwordrequirements.sms2factor != false)) && (parent.smsserver != null) && (user.phone != null)); - try { ws.send(JSON.stringify({ action: 'close', cause: 'emailvalidation', msg: 'emailvalidationrequired', email2fa: email2fa, sms2fa: sms2fa, email2fasent: true })); ws.close(); } catch (e) { } + var msg2fa = (((typeof domain.passwordrequirements != 'object') || (domain.passwordrequirements.msg2factor != false)) && (parent.msgserver != null) && (parent.msgserver.providers != 0) && (user.msghandle != null)); + try { ws.send(JSON.stringify({ action: 'close', cause: 'emailvalidation', msg: 'emailvalidationrequired', email2fa: email2fa, sms2fa: sms2fa, msg2fa: msg2fa, email2fasent: true })); ws.close(); } catch (e) { } } else { // We are authenticated func(ws, req, domain, user); @@ -7533,6 +7599,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates, doneF // Figure out if email 2FA is allowed var email2fa = (((typeof domain.passwordrequirements != 'object') || (domain.passwordrequirements.email2factor != false)) && (domain.mailserver != null) && (user.otpekey != null)); var sms2fa = (((typeof domain.passwordrequirements != 'object') || (domain.passwordrequirements.sms2factor != false)) && (parent.smsserver != null) && (user.phone != null)); + var msg2fa = (((typeof domain.passwordrequirements != 'object') || (domain.passwordrequirements.msg2factor != false)) && (parent.msgserver != null) && (parent.msgserver.providers != 0) && (user.msghandle != null)); if (s.length != 3) { try { ws.send(JSON.stringify({ action: 'close', cause: 'noauth', msg: 'tokenrequired', email2fa: email2fa, sms2fa: sms2fa, twoFactorCookieDays: twoFactorCookieDays })); ws.close(); } catch (e) { } } else { @@ -7554,6 +7621,14 @@ module.exports.CreateWebServer = function (parent, db, args, certificates, doneF parent.smsserver.sendToken(domain, user.phone, user.otpsms.k, obj.getLanguageCodes(req)); // Ask for a login token & confirm sms was sent try { ws.send(JSON.stringify({ action: 'close', cause: 'noauth', msg: 'tokenrequired', sms2fa: sms2fa, sms2fasent: true, twoFactorCookieDays: twoFactorCookieDays })); ws.close(); } catch (e) { } + } else if ((s[2] == '**msg**') && (msg2fa == true)) { + // Cause a token to be sent to the user's phone number + user.otpmsg = { k: obj.common.zeroPad(getRandomSixDigitInteger(), 6), d: Date.now() }; + obj.db.SetUser(user); + parent.debug('web', 'Sending 2FA message to: ' + user.msghandle); + parent.msgserver.sendToken(domain, user.msghandle, user.otpmsg.k, obj.getLanguageCodes(req)); + // Ask for a login token & confirm sms was sent + try { ws.send(JSON.stringify({ action: 'close', cause: 'noauth', msg: 'tokenrequired', msg2fa: msg2fa, msg2fasent: true, twoFactorCookieDays: twoFactorCookieDays })); ws.close(); } catch (e) { } } else { // Ask for a login token 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 delete user2.subscriptions; delete user2.passtype; delete user2.otpsms; + delete user2.otpmsg; if ((typeof user2.otpekey == 'object') && (user2.otpekey != null)) { user2.otpekey = 1; } // Indicates that email 2FA is enabled. if ((typeof user2.otpsecret == 'string') && (user2.otpsecret != null)) { user2.otpsecret = 1; } // Indicates a time secret is present. 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.