Added support for email 2FA in MeshCentral Router.

Ylian Saint-Hilaire committed Mar 21, 2020 at 22:52 UTC d089062167203f7db605cbb9c4386551e3ae517a
3 files changed +33 -5
agents/MeshCentralRouter.exe
Binary files a/agents/MeshCentralRouter.exe and b/agents/MeshCentralRouter.exe differ
meshuser.js
+1
@@ -2848,6 +2848,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
2848 // Event the node change. Only do this if the database will not do it.
2849 event.msg = 'Changed device ' + node.name + ' from group ' + mesh.name + ': ' + changes.join(', ');
2850 event.node = parent.CloneSafeNode(node);
2851 + if (command.rdpport == 3389) { event.node.rdpport = 3389; }
2852 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.
2853 parent.parent.DispatchEvent(['*', node.meshid, user._id, node._id], obj, event);
2854 }
webserver.js
+32 -5
@@ -3777,12 +3777,26 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
3777 if ((err == null) && (user)) {
3778 // Check if a 2nd factor is needed
3779 if (checkUserOneTimePasswordRequired(domain, user, req) == true) {
3780 - if (typeof req.query.token != 'string') {
3781 - try { ws.send(JSON.stringify({ action: 'close', cause: 'noauth', msg: 'tokenrequired' })); ws.close(); } catch (e) { }
3780 + // Figure out if email 2FA is allowed
3781 + var email2fa = (((typeof domain.passwordrequirements != 'object') || (domain.passwordrequirements.email2factor != false)) && (parent.mailserver != null) && (user.otpekey != null));
3782 + if ((typeof req.query.token != 'string') || (req.query.token == '**email**')) {
3783 + if ((req.query.token == '**email**') && (email2fa == true)) {
3784 + // Cause a token to be sent to the user's registered email
3785 + user.otpekey = { k: obj.common.zeroPad(getRandomEightDigitInteger(), 8), d: Date.now() };
3786 + obj.db.SetUser(user);
3787 + parent.debug('web', 'Sending 2FA email to: ' + user.email);
3788 + parent.mailserver.sendAccountLoginMail(domain, user.email, user.otpekey.k);
3789 + // Ask for a login token & confirm email was sent
3790 + try { ws.send(JSON.stringify({ action: 'close', cause: 'noauth', msg: 'tokenrequired', email2fa: email2fa, email2fasent: true })); ws.close(); } catch (e) { }
3791 + } else {
3792 + // Ask for a login token
3793 + try { ws.send(JSON.stringify({ action: 'close', cause: 'noauth', msg: 'tokenrequired', email2fa: email2fa })); ws.close(); } catch (e) { }
3794 + }
3795 } else {
3796 checkUserOneTimePassword(req, domain, user, req.query.token, null, function (result) {
3797 if (result == false) {
3785 - try { ws.send(JSON.stringify({ action: 'close', cause: 'noauth', msg: 'tokenrequired' })); ws.close(); } catch (e) { }
3798 + // Failed, ask for a login token again
3799 + try { ws.send(JSON.stringify({ action: 'close', cause: 'noauth', msg: 'tokenrequired', email2fa: email2fa })); ws.close(); } catch (e) { }
3800 } else {
3801 // We are authenticated with 2nd factor.
3802 func(ws, req, domain, user);
@@ -3836,12 +3850,25 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
3850 if ((err == null) && (user)) {
3851 // Check if a 2nd factor is needed
3852 if (checkUserOneTimePasswordRequired(domain, user, req) == true) {
3853 + // Figure out if email 2FA is allowed
3854 + var email2fa = (((typeof domain.passwordrequirements != 'object') || (domain.passwordrequirements.email2factor != false)) && (parent.mailserver != null) && (user.otpekey != null));
3855 if (s.length != 3) {
3840 - try { ws.send(JSON.stringify({ action: 'close', cause: 'noauth', msg: 'tokenrequired' })); ws.close(); } catch (e) { }
3856 + try { ws.send(JSON.stringify({ action: 'close', cause: 'noauth', msg: 'tokenrequired', email2fa: email2fa })); ws.close(); } catch (e) { }
3857 } else {
3858 checkUserOneTimePassword(req, domain, user, s[2], null, function (result) {
3859 if (result == false) {
3844 - try { ws.send(JSON.stringify({ action: 'close', cause: 'noauth', msg: 'tokenrequired' })); ws.close(); } catch (e) { }
3860 + if ((s[2] == '**email**') && (email2fa == true)) {
3861 + // Cause a token to be sent to the user's registered email
3862 + user.otpekey = { k: obj.common.zeroPad(getRandomEightDigitInteger(), 8), d: Date.now() };
3863 + obj.db.SetUser(user);
3864 + parent.debug('web', 'Sending 2FA email to: ' + user.email);
3865 + parent.mailserver.sendAccountLoginMail(domain, user.email, user.otpekey.k);
3866 + // Ask for a login token & confirm email was sent
3867 + try { ws.send(JSON.stringify({ action: 'close', cause: 'noauth', msg: 'tokenrequired', email2fa: email2fa, email2fasent: true })); ws.close(); } catch (e) { }
3868 + } else {
3869 + // Ask for a login token
3870 + try { ws.send(JSON.stringify({ action: 'close', cause: 'noauth', msg: 'tokenrequired', email2fa: email2fa })); ws.close(); } catch (e) { }
3871 + }
3872 } else {
3873 // We are authenticated with 2nd factor.
3874 func(ws, req, domain, user);