More work on mobile 2FA.

This commit is contained in:
Ylian Saint-Hilaire 2021-04-13 23:23:09 -07:00
parent da33f1dfb1
commit c74a207606
11 changed files with 133 additions and 10 deletions

View File

@ -99,6 +99,7 @@
<Compile Include="amt\amt.js" /> <Compile Include="amt\amt.js" />
<Compile Include="exeHandler.js" /> <Compile Include="exeHandler.js" />
<Compile Include="amtprovisioningserver.js" /> <Compile Include="amtprovisioningserver.js" />
<Compile Include="firebase.js" />
<Compile Include="letsencrypt.js" /> <Compile Include="letsencrypt.js" />
<Compile Include="mcrec.js" /> <Compile Include="mcrec.js" />
<Compile Include="meshaccelerator.js" /> <Compile Include="meshaccelerator.js" />

View File

@ -90,8 +90,16 @@ module.exports.CreateFirebase = function (parent, senderid, serverkey) {
//var payload = { notification: { title: command.title, body: command.msg }, data: { url: obj.msgurl } }; //var payload = { notification: { title: command.title, body: command.msg }, data: { url: obj.msgurl } };
//var options = { priority: 'High', timeToLive: 5 * 60 }; // TTL: 5 minutes, priority 'Normal' or 'High' //var options = { priority: 'High', timeToLive: 5 * 60 }; // TTL: 5 minutes, priority 'Normal' or 'High'
// Send an outbound push notification
obj.sendToDevice = function (node, payload, options, func) { obj.sendToDevice = function (node, payload, options, func) {
if (typeof node == 'string') {
parent.db.Get(node, function (err, docs) { if ((err == null) && (docs != null) && (docs.length == 1)) { obj.sendToDeviceEx(docs[0], payload, options, func); } else { func(0, 'error'); } })
} else {
obj.sendToDeviceEx(node, payload, options, func);
}
}
// Send an outbound push notification
obj.sendToDeviceEx = function (node, payload, options, func) {
parent.debug('email', 'Firebase-sendToDevice'); parent.debug('email', 'Firebase-sendToDevice');
if ((node == null) || (typeof node.pmt != 'string')) return; if ((node == null) || (typeof node.pmt != 'string')) return;
obj.log('sendToDevice, node:' + node._id + ', payload: ' + JSON.stringify(payload) + ', options: ' + JSON.stringify(options)); obj.log('sendToDevice, node:' + node._id + ', payload: ' + JSON.stringify(payload) + ', options: ' + JSON.stringify(options));
@ -270,6 +278,14 @@ module.exports.CreateFirebaseRelay = function (parent, url, key) {
} }
obj.sendToDevice = function (node, payload, options, func) { obj.sendToDevice = function (node, payload, options, func) {
if (typeof node == 'string') {
parent.db.Get(node, function (err, docs) { if ((err == null) && (docs != null) && (docs.length == 1)) { obj.sendToDeviceEx(docs[0], payload, options, func); } else { func(0, 'error'); } })
} else {
obj.sendToDeviceEx(node, payload, options, func);
}
}
obj.sendToDeviceEx = function (node, payload, options, func) {
parent.debug('email', 'Firebase-sendToDevice-webSocket'); parent.debug('email', 'Firebase-sendToDevice-webSocket');
if ((node == null) || (typeof node.pmt != 'string')) { func(0, 'error'); return; } if ((node == null) || (typeof node.pmt != 'string')) { func(0, 'error'); return; }
obj.log('sendToDevice, node:' + node._id + ', payload: ' + JSON.stringify(payload) + ', options: ' + JSON.stringify(options)); obj.log('sendToDevice, node:' + node._id + ', payload: ' + JSON.stringify(payload) + ', options: ' + JSON.stringify(options));
@ -298,7 +314,16 @@ module.exports.CreateFirebaseRelay = function (parent, url, key) {
} else if (relayUrl.protocol == 'https:') { } else if (relayUrl.protocol == 'https:') {
// Send an outbound push notification using an HTTPS POST // Send an outbound push notification using an HTTPS POST
obj.pushOnly = true; obj.pushOnly = true;
obj.sendToDevice = function (node, payload, options, func) { obj.sendToDevice = function (node, payload, options, func) {
if (typeof node == 'string') {
parent.db.Get(node, function (err, docs) { if ((err == null) && (docs != null) && (docs.length == 1)) { obj.sendToDeviceEx(docs[0], payload, options, func); } else { func(0, 'error'); } })
} else {
obj.sendToDeviceEx(node, payload, options, func);
}
}
obj.sendToDeviceEx = function (node, payload, options, func) {
parent.debug('email', 'Firebase-sendToDevice-httpPost'); parent.debug('email', 'Firebase-sendToDevice-httpPost');
if ((node == null) || (typeof node.pmt != 'string')) return; if ((node == null) || (typeof node.pmt != 'string')) return;

View File

@ -1556,6 +1556,7 @@ module.exports.CreateMeshAgent = function (parent, db, ws, req, args, domain) {
// Complete 2FA checking // Complete 2FA checking
if (authCookie.a == 'checkAuth') { if (authCookie.a == 'checkAuth') {
// TODO // TODO
console.log(authCookie);
} }
break; break;

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 100 KiB

View File

@ -394,7 +394,7 @@ body {
color: blue; color: blue;
} }
#loginpanel, #createpanel, #resetpanel, #tokenpanel, #resettokenpanel, #resetpasswordpanel, #resetpasswordpanel, #checkemailpanel { #loginpanel, #createpanel, #resetpanel, #tokenpanel, #resettokenpanel, #resetpasswordpanel, #resetpasswordpanel, #checkemailpanel, #waitpushpanel {
display: inline-block; display: inline-block;
margin: 0; margin: 0;
background-color: #979797; background-color: #979797;

View File

@ -2032,6 +2032,7 @@
QV('authKeySetupCheck', userinfo.otphkeys > 0); QV('authKeySetupCheck', userinfo.otphkeys > 0);
QV('authPushAuthDevCheck', (userinfo.otpdev > 0) && ((features2 & 2) != 0)); QV('authPushAuthDevCheck', (userinfo.otpdev > 0) && ((features2 & 2) != 0));
QV('authCodesSetupCheck', userinfo.otpkeys > 0); QV('authCodesSetupCheck', userinfo.otpkeys > 0);
QV('managePushAuthDev', (features2 & 2) && (count2factoraAuths() > 0));
mainUpdate(4 + 128 + 4096); mainUpdate(4 + 128 + 4096);
// Check if none or at least 2 factors are enabled. // Check if none or at least 2 factors are enabled.

View File

@ -186,6 +186,7 @@
<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)" /> <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)" />
<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)" /> <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)" />
<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)" /> <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)" />
<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)" />
</div> </div>
</td> </td>
</tr> </tr>
@ -219,6 +220,7 @@
<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)" /> <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)" />
<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)" /> <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)" />
<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)" /> <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)" />
<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)" />
</div> </div>
</td> </td>
</tr> </tr>
@ -281,6 +283,22 @@
<input id=checkemailformargs name="urlargs" type="hidden" value="" /> <input id=checkemailformargs name="urlargs" type="hidden" value="" />
</form> </form>
</div> </div>
<div id=waitpushpanel style="display:none">
<div id=message8></div>
<table style="width:100%">
<tr>
<td style="align-content:center;padding-top:10px">
<img src="images/login/push-150.png" srcset="images/login/push-300.png 2x" loading="lazy" width="265" height="150" />
</td>
</tr>
<tr>
<td style="align-content:center;padding-top:10px">
<label id=tokenInputRememberLabel2><input id=tokenInputRemember2 name=remembertoken type=checkbox /><span id=tokenInputRememberSpan2></span></label>
</td>
</tr>
</table>
<hr /><a onclick="return xgo(1,event);" href="#" style=cursor:pointer>Back to login</a>
</div>
</td> </td>
</tr> </tr>
<tr id="welcomeTextRow"><td><div id="welcomeText" style="color:white;text-align:center;margin-left:20px;margin-right:20px"></div></td></tr> <tr id="welcomeTextRow"><td><div id="welcomeText" style="color:white;text-align:center;margin-left:20px;margin-right:20px"></div></td></tr>
@ -330,6 +348,7 @@
var publicKeyCredentialRequestOptions = null; var publicKeyCredentialRequestOptions = null;
var otpemail = (decodeURIComponent('{{{otpemail}}}') === 'true'); var otpemail = (decodeURIComponent('{{{otpemail}}}') === 'true');
var otpsms = (decodeURIComponent('{{{otpsms}}}') === 'true'); var otpsms = (decodeURIComponent('{{{otpsms}}}') === 'true');
var otppush = (decodeURIComponent('{{{otppush}}}') === 'true');
var twoFactorCookieDays = parseInt('{{{twoFactorCookieDays}}}'); var twoFactorCookieDays = parseInt('{{{twoFactorCookieDays}}}');
var authStrategies = '{{{authStrategies}}}'.split(','); var authStrategies = '{{{authStrategies}}}'.split(',');
@ -342,15 +361,16 @@
// Display the right server message // Display the right server message
var i; var i;
var messageid = parseInt('{{{messageid}}}'); 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.", "Notification sent, {0}."];
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."]; 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."];
if (messageid > 0) { if (messageid > 0) {
var msg = ''; var msg = '';
if ((messageid < 100) && (messageid < okmessages.length)) { msg = okmessages[messageid]; } if ((messageid < 100) && (messageid < okmessages.length)) { msg = okmessages[messageid]; }
else if ((messageid >= 100) && ((messageid - 100) < failmessages.length)) { msg = failmessages[messageid - 100]; } else if ((messageid >= 100) && ((messageid - 100) < failmessages.length)) { msg = failmessages[messageid - 100]; }
if (messageid == 5) { msg = format(msg, passhint); }
if (msg != '') { if (msg != '') {
if (messageid >= 100) { msg = ('<span class="msg error"><b style=color:#8C001A>' + msg + '<b></span><br /><br />'); } else { msg = ('<span class="msg success"><b>' + msg + '</b></span><br /><br />'); } if (messageid >= 100) { msg = ('<span class="msg error"><b style=color:#8C001A>' + msg + '<b></span><br /><br />'); } else { msg = ('<span class="msg success"><b>' + msg + '</b></span><br /><br />'); }
for (i = 1; i < 8; i++) { QH('message' + i, msg); } for (i = 1; i < 9; i++) { QH('message' + i, msg); }
} }
} }
@ -369,8 +389,11 @@
if (twoFactorCookieDays > 0) { if (twoFactorCookieDays > 0) {
QV('tokenInputRememberLabel', true); QV('tokenInputRememberLabel', true);
QH('tokenInputRememberSpan', format("Remember this device for {0} days.", twoFactorCookieDays)); QH('tokenInputRememberSpan', format("Remember this device for {0} days.", twoFactorCookieDays));
QV('tokenInputRememberLabel2', true);
QH('tokenInputRememberSpan2', format("Remember this device for {0} days.", twoFactorCookieDays));
} else { } else {
QV('tokenInputRememberLabel', false); QV('tokenInputRememberLabel', false);
QV('tokenInputRememberLabel2', false);
} }
// If URL arguments are provided, add them to form posts // If URL arguments are provided, add them to form posts
@ -443,10 +466,12 @@
var twofakey = (hardwareKeyChallenge != null) && (hardwareKeyChallenge.type == 'webAuthn'); var twofakey = (hardwareKeyChallenge != null) && (hardwareKeyChallenge.type == 'webAuthn');
var emailkey = otpemail && (messageid != 2) && (messageid != 4); var emailkey = otpemail && (messageid != 2) && (messageid != 4);
var smskey = otpsms && (messageid != 2) && (messageid != 4); var smskey = otpsms && (messageid != 2) && (messageid != 4);
var pushkey = otppush && (messageid != 2) && (messageid != 4);
QV('securityKeyButton', twofakey); QV('securityKeyButton', twofakey);
QV('emailKeyButton', emailkey); QV('emailKeyButton', emailkey);
QV('smsKeyButton', smskey); QV('smsKeyButton', smskey);
QV('2farow', twofakey || emailkey || smskey); QV('pushKeyButton', pushkey);
QV('2farow', twofakey || emailkey || smskey || pushkey);
} }
if (loginMode == '5') { if (loginMode == '5') {
@ -454,10 +479,12 @@
var twofakey = (hardwareKeyChallenge != null) && (hardwareKeyChallenge.type == 'webAuthn'); var twofakey = (hardwareKeyChallenge != null) && (hardwareKeyChallenge.type == 'webAuthn');
var emailkey = otpemail && (messageid != 2) && (messageid != 4); var emailkey = otpemail && (messageid != 2) && (messageid != 4);
var smskey = otpsms && (messageid != 2) && (messageid != 4); var smskey = otpsms && (messageid != 2) && (messageid != 4);
var pushkey = otppush && (messageid != 2) && (messageid != 4);
QV('securityKeyButton2', twofakey); QV('securityKeyButton2', twofakey);
QV('emailKeyButton2', emailkey); QV('emailKeyButton2', emailkey);
QV('smsKeyButton2', smskey); QV('smsKeyButton2', smskey);
QV('2farow2', twofakey || emailkey || smskey); QV('pushKeyButton', pushkey);
QV('2farow2', twofakey || emailkey || smskey || pushkey);
} }
/* /*
@ -565,6 +592,18 @@
} }
} }
function usePushToken(panelAction) {
if (panelAction == 1) {
Q('hwtokenInput').value = '**push**';
QE('tokenOkButton', true);
Q('tokenOkButton').click();
} else if (panelAction == 2) {
Q('resetHwtokenInput').value = '**push**';
QE('resetTokenOkButton', true);
Q('resetTokenOkButton').click();
}
}
function showPassHint(e) { function showPassHint(e) {
messagebox("Password Hint", passhint); messagebox("Password Hint", passhint);
haltEvent(e); haltEvent(e);
@ -595,6 +634,7 @@
QV('resettokenpanel', x == 5); QV('resettokenpanel', x == 5);
QV('resetpasswordpanel', x == 6); QV('resetpasswordpanel', x == 6);
QV('checkemailpanel', x == 7); QV('checkemailpanel', x == 7);
QV('waitpushpanel', x == 8);
if (x == 1) { Q('username').focus(); } if (x == 1) { Q('username').focus(); }
if (x == 2) { if (features & 0x200000) { Q('aemail').focus(); } else { Q('ausername').focus(); } } // Email is username if (x == 2) { if (features & 0x200000) { Q('aemail').focus(); } else { Q('ausername').focus(); } } // Email is username
if (x == 3) { Q('remail').focus(); } if (x == 3) { Q('remail').focus(); }

View File

@ -924,6 +924,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
var email2fa = (((typeof domain.passwordrequirements != 'object') || (domain.passwordrequirements.email2factor != false)) && (domain.mailserver != null) && (user.email != null) && (user.emailVerified == true) && (user.otpekey != null)); 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 sms2fa = (((typeof domain.passwordrequirements != 'object') || (domain.passwordrequirements.sms2factor != false)) && (parent.smsserver != null) && (user.phone != null));
var push2fa = ((parent.firebase != null) && (user.otpdev != null));
// Check if this user has 2-step login active // Check if this user has 2-step login active
if ((req.session.loginmode != '6') && checkUserOneTimePasswordRequired(domain, user, req)) { if ((req.session.loginmode != '6') && checkUserOneTimePasswordRequired(domain, user, req)) {
@ -951,6 +952,29 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
return; return;
} }
if ((req.body.hwtoken == '**push**') && push2fa) {
// Cause push notification to device
const logincode = obj.common.zeroPad(getRandomSixDigitInteger(), 6);
const code = Buffer.from(logincode).toString('base64');
const authCookie = parent.encodeCookie({ a: 'checkAuth', c: code, u: user._id, n: user.otpdev });
var payload = { notification: { title: "MeshCentral", body: user.name + " authentication" }, data: { url: '2fa://auth?code=' + code + '&c=' + authCookie } };
var options = { priority: 'High', timeToLive: 60 }; // TTL: 1 minute
parent.firebase.sendToDevice(user.otpdev, payload, options, function (id, err, errdesc) {
if (err == null) {
// Request that the login page wait for device auth
req.session.messageid = 5; // "Notification sent." message
req.session.passhint = logincode;
req.session.loginmode = '8';
} else {
// Indicate the push notification failed
req.session.messageid = 116; // "Unable to send device notification." message
req.session.loginmode = '4';
}
if (direct === true) { handleRootRequestEx(req, res, domain); } else { res.redirect(domain.url + getQueryPortion(req)); }
});
return;
}
checkUserOneTimePassword(req, domain, user, req.body.token, req.body.hwtoken, function (result) { checkUserOneTimePassword(req, domain, user, req.body.token, req.body.hwtoken, function (result) {
if (result == false) { if (result == false) {
var randomWaitTime = 0; var randomWaitTime = 0;
@ -973,6 +997,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
req.session.loginmode = '4'; req.session.loginmode = '4';
req.session.tokenemail = ((user.email != null) && (user.emailVerified == true) && (domain.mailserver != null) && (user.otpekey != null)); req.session.tokenemail = ((user.email != null) && (user.emailVerified == true) && (domain.mailserver != null) && (user.otpekey != null));
req.session.tokensms = ((user.phone != null) && (parent.smsserver != null)); req.session.tokensms = ((user.phone != null) && (parent.smsserver != null));
req.session.tokenpush = ((user.otpdev != null) && (parent.firebase != null));
req.session.tokenuserid = userid; req.session.tokenuserid = userid;
req.session.tokenusername = xusername; req.session.tokenusername = xusername;
req.session.tokenpassword = xpassword; req.session.tokenpassword = xpassword;
@ -1097,6 +1122,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
delete req.session.tokenpassword; delete req.session.tokenpassword;
delete req.session.tokenemail; delete req.session.tokenemail;
delete req.session.tokensms; delete req.session.tokensms;
delete req.session.tokenpush;
delete req.session.messageid; delete req.session.messageid;
delete req.session.passhint; delete req.session.passhint;
delete req.session.cuserid; delete req.session.cuserid;
@ -1301,6 +1327,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
delete req.session.resettokenpassword; delete req.session.resettokenpassword;
delete req.session.tokenemail; delete req.session.tokenemail;
delete req.session.tokensms; delete req.session.tokensms;
delete req.session.tokenpush;
delete req.session.messageid; delete req.session.messageid;
delete req.session.passhint; delete req.session.passhint;
delete req.session.cuserid; delete req.session.cuserid;
@ -1382,6 +1409,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
delete req.session.resettokenpassword; delete req.session.resettokenpassword;
delete req.session.tokenemail; delete req.session.tokenemail;
delete req.session.tokensms; delete req.session.tokensms;
delete req.session.tokenpush;
delete req.session.messageid; delete req.session.messageid;
delete req.session.passhint; delete req.session.passhint;
delete req.session.cuserid; delete req.session.cuserid;
@ -2638,7 +2666,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
var passhint = null, msgid = 0; var passhint = null, msgid = 0;
if (req.session != null) { if (req.session != null) {
msgid = req.session.messageid; msgid = req.session.messageid;
if ((loginmode == '7') || ((domain.passwordrequirements != null) && (domain.passwordrequirements.hint === true))) { passhint = EscapeHtml(req.session.passhint); } if ((msgid == 5) || (loginmode == '7') || ((domain.passwordrequirements != null) && (domain.passwordrequirements.hint === true))) { passhint = EscapeHtml(req.session.passhint); }
delete req.session.messageid; delete req.session.messageid;
delete req.session.passhint; delete req.session.passhint;
} }
@ -2658,6 +2686,8 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
if ((typeof domain.passwordrequirements == 'object') && (domain.passwordrequirements.email2factor == false)) { otpemail = false; } if ((typeof domain.passwordrequirements == 'object') && (domain.passwordrequirements.email2factor == false)) { otpemail = false; }
var otpsms = (parent.smsserver != null) && (req.session != null) && (req.session.tokensms == true); var otpsms = (parent.smsserver != null) && (req.session != null) && (req.session.tokensms == true);
if ((typeof domain.passwordrequirements == 'object') && (domain.passwordrequirements.sms2factor == false)) { otpsms = false; } if ((typeof domain.passwordrequirements == 'object') && (domain.passwordrequirements.sms2factor == false)) { otpsms = false; }
var otppush = (parent.firebase != null) && (req.session != null) && (req.session.tokenpush == true);
//if ((typeof domain.passwordrequirements == 'object') && (domain.passwordrequirements.push2factor == false)) { otppush = false; }
// See if we support two-factor trusted cookies // See if we support two-factor trusted cookies
var twoFactorCookieDays = 30; var twoFactorCookieDays = 30;
@ -2704,6 +2734,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
hwstate: hwstate, hwstate: hwstate,
otpemail: otpemail, otpemail: otpemail,
otpsms: otpsms, otpsms: otpsms,
otppush: otppush,
twoFactorCookieDays: twoFactorCookieDays, twoFactorCookieDays: twoFactorCookieDays,
authStrategies: authStrategies.join(','), authStrategies: authStrategies.join(','),
loginpicture: (typeof domain.loginpicture == 'string') loginpicture: (typeof domain.loginpicture == 'string')
@ -5844,7 +5875,8 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
// Figure out if email 2FA is allowed // Figure out if email 2FA is allowed
var email2fa = (((typeof domain.passwordrequirements != 'object') || (domain.passwordrequirements.email2factor != false)) && (domain.mailserver != null) && (user.otpekey != null)); 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 sms2fa = (((typeof domain.passwordrequirements != 'object') || (domain.passwordrequirements.sms2factor != false)) && (parent.smsserver != null) && (user.phone != null));
if ((typeof command.token != 'string') || (command.token == '**email**') || (command.token == '**sms**')) { //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)) { if ((command.token == '**email**') && (email2fa == true)) {
// Cause a token to be sent to the user's registered email // Cause a token to be sent to the user's registered email
user.otpekey = { k: obj.common.zeroPad(getRandomEightDigitInteger(), 8), d: Date.now() }; user.otpekey = { k: obj.common.zeroPad(getRandomEightDigitInteger(), 8), d: Date.now() };
@ -5861,6 +5893,17 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
parent.smsserver.sendToken(domain, user.phone, user.otpsms.k, obj.getLanguageCodes(req)); parent.smsserver.sendToken(domain, user.phone, user.otpsms.k, obj.getLanguageCodes(req));
// Ask for a login token & confirm sms was sent // 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, sms2fasent: true, twoFactorCookieDays: twoFactorCookieDays })); ws.close(); } catch (e) { }
/*
} else if ((command.token == '**push**') && (push2fa == true)) {
// Cause push notification to device
const code = Buffer.from(obj.common.zeroPad(getRandomSixDigitInteger(), 6)).toString('base64');
const authCookie = parent.encodeCookie({ a: 'checkAuth', c: code, u: user._id, n: user.otpdev });
var payload = { notification: { title: "MeshCentral", body: user.name + " authentication" }, data: { url: '2fa://auth?code=' + code + '&c=' + authCookie } };
var options = { priority: 'High', timeToLive: 60 }; // TTL: 1 minute
parent.firebase.sendToDevice(user.otpdev, payload, options, function (id, err, errdesc) {
if (err == null) { parent.debug('email', 'Successfully auth check send push message to device'); } else { parent.debug('email', 'Failed auth check push message to device, error: ' + errdesc); }
});
*/
} else { } else {
// Ask for a login token // Ask for a login token
parent.debug('web', 'Asking for login token'); parent.debug('web', 'Asking for login token');
@ -5965,7 +6008,8 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
// Figure out if email 2FA is allowed // Figure out if email 2FA is allowed
var email2fa = (((typeof domain.passwordrequirements != 'object') || (domain.passwordrequirements.email2factor != false)) && (domain.mailserver != null) && (user.otpekey != null)); 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 sms2fa = (((typeof domain.passwordrequirements != 'object') || (domain.passwordrequirements.sms2factor != false)) && (parent.smsserver != null) && (user.phone != null));
if ((typeof req.query.token != 'string') || (req.query.token == '**email**') || (req.query.token == '**sms**')) { //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)) { if ((req.query.token == '**email**') && (email2fa == true)) {
// Cause a token to be sent to the user's registered email // Cause a token to be sent to the user's registered email
user.otpekey = { k: obj.common.zeroPad(getRandomEightDigitInteger(), 8), d: Date.now() }; user.otpekey = { k: obj.common.zeroPad(getRandomEightDigitInteger(), 8), d: Date.now() };
@ -5982,6 +6026,17 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
parent.smsserver.sendToken(domain, user.phone, user.otpsms.k, obj.getLanguageCodes(req)); parent.smsserver.sendToken(domain, user.phone, user.otpsms.k, obj.getLanguageCodes(req));
// Ask for a login token & confirm sms was sent // 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, sms2fasent: true, twoFactorCookieDays: twoFactorCookieDays })); ws.close(); } catch (e) { }
/*
} else if ((command.token == '**push**') && (push2fa == true)) {
// Cause push notification to device
const code = Buffer.from(obj.common.zeroPad(getRandomSixDigitInteger(), 6)).toString('base64');
const authCookie = parent.encodeCookie({ a: 'checkAuth', c: code, u: user._id, n: user.otpdev });
var payload = { notification: { title: "MeshCentral", body: user.name + " authentication" }, data: { url: '2fa://auth?code=' + code + '&c=' + authCookie } };
var options = { priority: 'High', timeToLive: 60 }; // TTL: 1 minute
parent.firebase.sendToDevice(user.otpdev, payload, options, function (id, err, errdesc) {
if (err == null) { parent.debug('email', 'Successfully auth check send push message to device'); } else { parent.debug('email', 'Failed auth check push message to device, error: ' + errdesc); }
});
*/
} else { } else {
// Ask for a login token // Ask for a login token
parent.debug('web', 'Asking for login token'); parent.debug('web', 'Asking for login token');