Added support for email 2FA in MeshCentral Router.

This commit is contained in:
Ylian Saint-Hilaire 2020-03-21 22:52:23 -07:00
parent d4236385cc
commit 4d564a1195
3 changed files with 33 additions and 5 deletions

Binary file not shown.

View File

@ -2848,6 +2848,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
// Event the node change. Only do this if the database will not do it. // Event the node change. Only do this if the database will not do it.
event.msg = 'Changed device ' + node.name + ' from group ' + mesh.name + ': ' + changes.join(', '); event.msg = 'Changed device ' + node.name + ' from group ' + mesh.name + ': ' + changes.join(', ');
event.node = parent.CloneSafeNode(node); event.node = parent.CloneSafeNode(node);
if (command.rdpport == 3389) { event.node.rdpport = 3389; }
if (db.changeStream) { event.noact = 1; } // If DB change stream is active, don't use this event to change the node. Another event will come. if (db.changeStream) { event.noact = 1; } // If DB change stream is active, don't use this event to change the node. Another event will come.
parent.parent.DispatchEvent(['*', node.meshid, user._id, node._id], obj, event); parent.parent.DispatchEvent(['*', node.meshid, user._id, node._id], obj, event);
} }

View File

@ -3777,12 +3777,26 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
if ((err == null) && (user)) { if ((err == null) && (user)) {
// Check if a 2nd factor is needed // Check if a 2nd factor is needed
if (checkUserOneTimePasswordRequired(domain, user, req) == true) { if (checkUserOneTimePasswordRequired(domain, user, req) == true) {
if (typeof req.query.token != 'string') { // Figure out if email 2FA is allowed
try { ws.send(JSON.stringify({ action: 'close', cause: 'noauth', msg: 'tokenrequired' })); ws.close(); } catch (e) { } var email2fa = (((typeof domain.passwordrequirements != 'object') || (domain.passwordrequirements.email2factor != false)) && (parent.mailserver != null) && (user.otpekey != null));
if ((typeof req.query.token != 'string') || (req.query.token == '**email**')) {
if ((req.query.token == '**email**') && (email2fa == true)) {
// Cause a token to be sent to the user's registered email
user.otpekey = { k: obj.common.zeroPad(getRandomEightDigitInteger(), 8), d: Date.now() };
obj.db.SetUser(user);
parent.debug('web', 'Sending 2FA email to: ' + user.email);
parent.mailserver.sendAccountLoginMail(domain, user.email, user.otpekey.k);
// Ask for a login token & confirm email was sent
try { ws.send(JSON.stringify({ action: 'close', cause: 'noauth', msg: 'tokenrequired', email2fa: email2fa, email2fasent: true })); ws.close(); } catch (e) { }
} else {
// Ask for a login token
try { ws.send(JSON.stringify({ action: 'close', cause: 'noauth', msg: 'tokenrequired', email2fa: email2fa })); ws.close(); } catch (e) { }
}
} else { } else {
checkUserOneTimePassword(req, domain, user, req.query.token, null, function (result) { checkUserOneTimePassword(req, domain, user, req.query.token, null, function (result) {
if (result == false) { if (result == false) {
try { ws.send(JSON.stringify({ action: 'close', cause: 'noauth', msg: 'tokenrequired' })); ws.close(); } catch (e) { } // Failed, ask for a login token again
try { ws.send(JSON.stringify({ action: 'close', cause: 'noauth', msg: 'tokenrequired', email2fa: email2fa })); ws.close(); } catch (e) { }
} else { } else {
// We are authenticated with 2nd factor. // We are authenticated with 2nd factor.
func(ws, req, domain, user); func(ws, req, domain, user);
@ -3836,12 +3850,25 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
if ((err == null) && (user)) { if ((err == null) && (user)) {
// Check if a 2nd factor is needed // Check if a 2nd factor is needed
if (checkUserOneTimePasswordRequired(domain, user, req) == true) { if (checkUserOneTimePasswordRequired(domain, user, req) == true) {
// Figure out if email 2FA is allowed
var email2fa = (((typeof domain.passwordrequirements != 'object') || (domain.passwordrequirements.email2factor != false)) && (parent.mailserver != null) && (user.otpekey != null));
if (s.length != 3) { if (s.length != 3) {
try { ws.send(JSON.stringify({ action: 'close', cause: 'noauth', msg: 'tokenrequired' })); ws.close(); } catch (e) { } try { ws.send(JSON.stringify({ action: 'close', cause: 'noauth', msg: 'tokenrequired', email2fa: email2fa })); ws.close(); } catch (e) { }
} else { } else {
checkUserOneTimePassword(req, domain, user, s[2], null, function (result) { checkUserOneTimePassword(req, domain, user, s[2], null, function (result) {
if (result == false) { if (result == false) {
try { ws.send(JSON.stringify({ action: 'close', cause: 'noauth', msg: 'tokenrequired' })); ws.close(); } catch (e) { } if ((s[2] == '**email**') && (email2fa == true)) {
// Cause a token to be sent to the user's registered email
user.otpekey = { k: obj.common.zeroPad(getRandomEightDigitInteger(), 8), d: Date.now() };
obj.db.SetUser(user);
parent.debug('web', 'Sending 2FA email to: ' + user.email);
parent.mailserver.sendAccountLoginMail(domain, user.email, user.otpekey.k);
// Ask for a login token & confirm email was sent
try { ws.send(JSON.stringify({ action: 'close', cause: 'noauth', msg: 'tokenrequired', email2fa: email2fa, email2fasent: true })); ws.close(); } catch (e) { }
} else {
// Ask for a login token
try { ws.send(JSON.stringify({ action: 'close', cause: 'noauth', msg: 'tokenrequired', email2fa: email2fa })); ws.close(); } catch (e) { }
}
} else { } else {
// We are authenticated with 2nd factor. // We are authenticated with 2nd factor.
func(ws, req, domain, user); func(ws, req, domain, user);