Added support for both U2F and OTP hardware login keys.
39
meshuser.js
@ -1503,9 +1503,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
|
||||
|
||||
// Send back the list of keys we have, just send the list of names and index
|
||||
var hkeys = [];
|
||||
if (user.otphkeys != null) { for (var i = 0; i < user.otphkeys.length; i++) { hkeys.push({ i: user.otphkeys[i].keyIndex, name: user.otphkeys[i].name }); } }
|
||||
|
||||
//hkeys = [{ i: 1234, name: 'My Normal Key' }, { i: 5678, name: 'Backup Key' }, { i: 90122, name: 'Blue Extra Key' }];
|
||||
if (user.otphkeys != null) { for (var i = 0; i < user.otphkeys.length; i++) { hkeys.push({ i: user.otphkeys[i].keyIndex, name: user.otphkeys[i].name, type: user.otphkeys[i].type }); } }
|
||||
|
||||
ws.send(JSON.stringify({ action: 'otp-hkey-get', keys: hkeys }));
|
||||
break;
|
||||
@ -1539,22 +1537,31 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
|
||||
// Check if Yubikey support is present
|
||||
if ((typeof domain.yubikey != 'object') || (typeof domain.yubikey.id != 'string') || (typeof domain.yubikey.secret != 'string')) break;
|
||||
|
||||
/*
|
||||
var yub = require('yubikey-client');
|
||||
yub.init(domain.yubikey.id, domain.yubikey.secret);
|
||||
yub.verify(command.otp, function (err, data) {
|
||||
console.log(err, data);
|
||||
});
|
||||
*/
|
||||
|
||||
// Query the YubiKey server to validate the OTP
|
||||
var yubikeyotp = require('yubikeyotp');
|
||||
//var request = { otp: command.otp, id: domain.yubikey.id, key: domain.yubikey.secret, sl: '100', timestamp: true }
|
||||
var request = { otp: command.otp, id: domain.yubikey.id, key: domain.yubikey.secret, timestamp: true }
|
||||
if (domain.yubikey.proxy) { request.requestParams = { proxy: domain.yubikey.proxy }; }
|
||||
|
||||
console.log('YubiKey Request: ' + JSON.stringify(request));
|
||||
yubikeyotp.verifyOTP(request, function (err, results) {
|
||||
console.log(err, results);
|
||||
if (results.status == 'OK') {
|
||||
var keyIndex = obj.parent.crypto.randomBytes(4).readUInt32BE(0);
|
||||
var keyId = command.otp.substring(0, 12);
|
||||
if (user.otphkeys == null) { user.otphkeys = []; }
|
||||
|
||||
// Check if this key was already registered, if so, remove it.
|
||||
var foundAtIndex = -1;
|
||||
for (var i = 0; i < user.otphkeys.length; i++) { if (user.otphkeys[i].keyid == keyId) { foundAtIndex = i; } }
|
||||
if (foundAtIndex != -1) { user.otphkeys.splice(foundAtIndex, 1); }
|
||||
|
||||
// Add the new key and notify
|
||||
user.otphkeys.push({ name: command.name, type: 2, keyid: keyId, keyIndex: keyIndex });
|
||||
obj.parent.db.SetUser(user);
|
||||
ws.send(JSON.stringify({ action: 'otp-hkey-yubikey-add', result: true, name: command.name, index: keyIndex }));
|
||||
|
||||
// Notify change TODO: Should be done on all sessions/servers for this user.
|
||||
try { ws.send(JSON.stringify({ action: 'userinfo', userinfo: obj.parent.CloneSafeUser(user) })); } catch (ex) { }
|
||||
} else {
|
||||
ws.send(JSON.stringify({ action: 'otp-hkey-yubikey-add', result: false, name: command.name }));
|
||||
}
|
||||
});
|
||||
|
||||
break;
|
||||
@ -1587,7 +1594,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
|
||||
ws.send(JSON.stringify({ action: 'otp-hkey-setup-response', result: result.successful, name: command.name, index: keyIndex }));
|
||||
if (result.successful) {
|
||||
if (user.otphkeys == null) { user.otphkeys = []; }
|
||||
user.otphkeys.push({ name: command.name, publicKey: result.publicKey, keyHandle: result.keyHandle, keyIndex: keyIndex });
|
||||
user.otphkeys.push({ name: command.name, type: 1, publicKey: result.publicKey, keyHandle: result.keyHandle, keyIndex: keyIndex });
|
||||
obj.parent.db.SetUser(user);
|
||||
//console.log('KEYS', JSON.stringify(user.otphkeys));
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "meshcentral",
|
||||
"version": "0.2.7-n",
|
||||
"version": "0.2.7-o",
|
||||
"keywords": [
|
||||
"Remote Management",
|
||||
"Intel AMT",
|
||||
|
Before Width: | Height: | Size: 362 B |
Before Width: | Height: | Size: 578 B |
BIN
public/images/hardware-key-OTP-24.png
Normal file
After Width: | Height: | Size: 779 B |
BIN
public/images/hardware-key-U2F-24.png
Normal file
After Width: | Height: | Size: 988 B |
BIN
public/images/hardware-keypress-120 - Copy.png
Normal file
After Width: | Height: | Size: 2.6 KiB |
Before Width: | Height: | Size: 2.6 KiB After Width: | Height: | Size: 8.5 KiB |
@ -1447,38 +1447,44 @@
|
||||
var end = "</table></div></div>";
|
||||
var x = "<a href='https://www.yubico.com/' rel='noreferrer noopener' target='_blank'>Hardware keys</a> are used as secondary login authentication.";
|
||||
x += "";
|
||||
var keyType1 = 0;
|
||||
if (message.keys && message.keys.length > 0) {
|
||||
for (var i in message.keys) {
|
||||
var key = message.keys[i];
|
||||
x += start + '<tr style=margin:5px><td style=width:30px><img src="images/hardware-key-24.png"><td style=width:250px>' + key.name + "<td><input type=button value='Remove' onclick=account_removehkey(" + key.i + ")></input>" + end;
|
||||
var type = 'OTP';
|
||||
if (key.type == 1) { keyType1++; type = 'U2F'; }
|
||||
x += start + '<tr style=margin:5px><td style=width:30px><img width=24 height=18 src="images/hardware-key-' + type + '-24.png" style=margin-top:4px><td style=width:250px>' + key.name + "<td><input type=button value='Remove' onclick=account_removehkey(" + key.i + ")></input>" + end;
|
||||
}
|
||||
} else {
|
||||
x += start + '<tr style=text-align:center><td>No Hardware Keys Configured' + end;
|
||||
}
|
||||
x += "<br />";
|
||||
x += "<div><input type=button value='Close' onclick=setDialogMode(0) style=float:right></input>";
|
||||
//x += "<input type=button value='Add YubiKey' onclick='account_addYubiKey();'></input>";
|
||||
|
||||
if (u2fSupported()) {
|
||||
x += "<input id=d2addkey type=button value='Add Key' onclick='account_addhkey();'></input>";
|
||||
} else {
|
||||
x += "No hardware key support on this browser.";
|
||||
}
|
||||
x += "<input id=d2addkey1 type=button value='Add U2F Key' onclick='account_addhkey(1);'></input>";
|
||||
if ((features & 0x4000) != 0) { x += "<input id=d2addkey2 type=button value='Add OTP Key' onclick='account_addhkey(2);'></input>"; }
|
||||
x += "</div><br />";
|
||||
setDialogMode(2, "Manage Hardware Login Keys", 8, null, x, 'otpauth-hardware-manage');
|
||||
if (u2fSupported() && (message.keys.length > 0)) { QE('d2addkey', false); }
|
||||
if ((u2fSupported() == false) || (keyType1 > 0)) { QE('d2addkey1', false); }
|
||||
break;
|
||||
}
|
||||
case 'otp-hkey-yubikey-add': {
|
||||
if (message.result) {
|
||||
meshserver.send({ action: 'otp-hkey-get' }); // Success, ask for the full list of keys.
|
||||
} else {
|
||||
setDialogMode(2, "Add Hardware Login Key", 1, null, '<br />Error, Unable to add key.<br /><br />');
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'otp-hkey-setup-request': {
|
||||
if (xxdialogMode && (xxdialogTag != 'otpauth-hardware-manage')) return;
|
||||
var x = "Press the key button now.<br /><br /><div style=width:100%;text-align:center><img src='images/hardware-keypress-120.png' /></div><input id=dp1keyname style=display:none value=" + message.name + " />";
|
||||
var x = "Press the key button now.<br /><br /><div style=width:100%;text-align:center><img width=120 height=117 src='images/hardware-keypress-120.png' /></div><input id=dp1keyname style=display:none value=" + message.name + " />";
|
||||
setDialogMode(2, "Add Hardware Login Key", 2, null, x);
|
||||
window.u2f.register(message.request.appId, [message.request], [], function (registrationResponse) {
|
||||
if (registrationResponse.registrationData) {
|
||||
meshserver.send({ action: 'otp-hkey-setup-response', request: message.request, response: registrationResponse, name: Q('dp1keyname').value });
|
||||
setDialogMode(2, "Add Hardware Login Key", 0, null, '<br />Checking...<br /><br /><br />', 'otpauth-hardware-manage');
|
||||
} else {
|
||||
setDialogMode(0);
|
||||
setDialogMode(2, "Add Hardware Login Key", 1, null, '<br />Error code ' + registrationResponse.errorCode + '<br /><br />');
|
||||
}
|
||||
});
|
||||
break;
|
||||
@ -5310,38 +5316,32 @@
|
||||
meshserver.send({ action: 'otp-hkey-get' });
|
||||
}
|
||||
|
||||
function account_addhkey() {
|
||||
function account_addhkey(type) {
|
||||
if (type == 1) {
|
||||
var x = "Type in the name of the key to add.<br /><br />";
|
||||
x += addHtmlValue('Key Name', '<input id=dp1keyname style=width:230px maxlength=20 autocomplete=off placeholder="MyKey" onkeyup=account_addhkeyValidate(event) />');
|
||||
setDialogMode(2, "Add Hardware Login Key", 3, account_addhkeyEx, x);
|
||||
x += addHtmlValue('Key Name', '<input id=dp1keyname style=width:230px maxlength=20 autocomplete=off placeholder="MyKey" onkeyup=account_addhkeyValidate(event,2) />');
|
||||
} else if (type == 2) {
|
||||
var x = "Type in a key name, select the OTP box and press the USB key button<br /><br />";
|
||||
x += addHtmlValue('Key Name', '<input id=dp1keyname style=width:230px maxlength=20 autocomplete=off placeholder="MyKey" onkeyup=account_addhkeyValidate(event,1) />');
|
||||
x += addHtmlValue('OTP from key', '<input id=dp1key style=width:230px autocomplete=off onkeyup=account_addhkeyValidate(event,2) />');
|
||||
}
|
||||
setDialogMode(2, "Add Hardware Login Key", 3, account_addhkeyEx, x, type);
|
||||
Q('dp1keyname').focus();
|
||||
}
|
||||
|
||||
function account_addhkeyValidate(e) {
|
||||
if ((e != null) && (e.keyCode == 13)) { dialogclose(1); }
|
||||
function account_addhkeyValidate(e,action) {
|
||||
if ((e != null) && (e.keyCode == 13)) { if (action == 2) { dialogclose(1); } else { Q('dp1key').focus(); } }
|
||||
}
|
||||
|
||||
function account_addhkeyEx() {
|
||||
function account_addhkeyEx(button, type) {
|
||||
var name = Q('dp1keyname').value;
|
||||
if (name == '') { name = 'MyKey'; }
|
||||
if (type == 1) {
|
||||
meshserver.send({ action: 'otp-hkey-setup-request', name: name });
|
||||
} else if (type == 2) {
|
||||
meshserver.send({ action: 'otp-hkey-yubikey-add', name: name, otp: Q('dp1key').value });
|
||||
setDialogMode(2, "Add Hardware Login Key", 0, null, "<br />Checking...<br /><br /><br />", 'otpauth-hardware-manage');
|
||||
}
|
||||
|
||||
function account_addYubiKey() {
|
||||
if (xxdialogMode && (xxdialogTag != 'otpauth-hardware-manage')) return;
|
||||
var x = "Type in a name for the key and press button on the key to register the new hardware key.<br /><br />";
|
||||
x += addHtmlValue('Key Name', '<input id=dp1keyname style=width:230px maxlength=20 autocomplete=off onchange=account_addYubiKeyValidate() onkeyup=account_addYubiKeyValidate() />');
|
||||
x += addHtmlValue('Key Token', '<input id=dp1keytoken style=width:230px maxlength=2048 autocomplete=off onchange=account_addYubiKeyValidate() onkeyup=account_addYubiKeyValidate() />');
|
||||
setDialogMode(2, "Add Yubikey", 3, account_addYubiKeyEx, x);
|
||||
account_addYubiKeyValidate();
|
||||
}
|
||||
|
||||
function account_addYubiKeyValidate() {
|
||||
QE('idx_dlgOkButton', (Q('dp1keyname').value.length > 0) && (Q('dp1keytoken').value.length > 0));
|
||||
}
|
||||
|
||||
function account_addYubiKeyEx() {
|
||||
meshserver.send({ action: 'otp-hkey-yubikey-add', name: Q('dp1keyname').value, otp: Q('dp1keytoken').value });
|
||||
}
|
||||
|
||||
function account_removehkey(index) {
|
||||
|
@ -150,7 +150,7 @@
|
||||
<tr>
|
||||
<td align=right width=100>Login token:</td>
|
||||
<td>
|
||||
<input id=tokenInput type=text name=token maxlength=12 onkeypress="return (event.keyCode == 8) || (event.keyCode == 13) || (event.charCode >= 48 && event.charCode <= 57)" onkeyup=checkToken(event) onkeydown=checkToken(event) />
|
||||
<input id=tokenInput type=text name=token maxlength=50 onkeyup=checkToken(event) onkeydown=checkToken(event) />
|
||||
<input id=hwtokenInput1 type=text name=hwtoken1 style="display:none" />
|
||||
<input id=hwtokenInput2 type=text name=hwtoken2 style="display:none" />
|
||||
</td>
|
||||
@ -351,9 +351,9 @@
|
||||
|
||||
function checkToken() {
|
||||
var t1 = Q('tokenInput').value;
|
||||
var t2 = t1.replace(/\D/g, '');
|
||||
var t2 = t1.split(' ').join('');
|
||||
if (t1 != t2) { Q('tokenInput').value = t2; }
|
||||
QE('tokenOkButton', (Q('tokenInput').value.length == 6) || (Q('tokenInput').value.length == 8));
|
||||
QE('tokenOkButton', (Q('tokenInput').value.length == 6) || (Q('tokenInput').value.length == 8) || (Q('tokenInput').value.length == 44));
|
||||
}
|
||||
|
||||
//
|
||||
|
@ -223,7 +223,7 @@
|
||||
<tr>
|
||||
<td align=right width=100>Login token:</td>
|
||||
<td>
|
||||
<input id=tokenInput type=text name=token maxlength=12 onkeypress="return (event.keyCode == 8) || (event.keyCode == 13) || (event.charCode >= 48 && event.charCode <= 57)" onkeyup=checkToken(event) onkeydown=checkToken(event) />
|
||||
<input id=tokenInput type=text name=token maxlength=50 onkeyup=checkToken(event) onkeydown=checkToken(event) />
|
||||
<input id=hwtokenInput1 type=text name=hwtoken1 style="display:none" />
|
||||
<input id=hwtokenInput2 type=text name=hwtoken2 style="display:none" />
|
||||
</td>
|
||||
@ -446,9 +446,9 @@
|
||||
|
||||
function checkToken() {
|
||||
var t1 = Q('tokenInput').value;
|
||||
var t2 = t1.replace(/\D/g, '');
|
||||
var t2 = t1.split(' ').join('');
|
||||
if (t1 != t2) { Q('tokenInput').value = t2; }
|
||||
QE('tokenOkButton', (Q('tokenInput').value.length == 6) || (Q('tokenInput').value.length == 8));
|
||||
QE('tokenOkButton', (Q('tokenInput').value.length == 6) || (Q('tokenInput').value.length == 8) || (Q('tokenInput').value.length == 44));
|
||||
}
|
||||
|
||||
//
|
||||
|
123
webserver.js
@ -342,44 +342,77 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
|
||||
}
|
||||
|
||||
// Check the 2-step auth token
|
||||
function checkUserOneTimePassword(domain, user, token, hwtoken1, hwtoken2) {
|
||||
function checkUserOneTimePassword(domain, user, token, hwtoken1, hwtoken2, func) {
|
||||
const twoStepLoginSupported = ((domain.auth != 'sspi') && (obj.parent.certificates.CommonName != 'un-configured') && (obj.args.lanonly !== true) && (obj.args.nousers !== true));
|
||||
if (twoStepLoginSupported == false) return true;
|
||||
if (twoStepLoginSupported == false) { func(true); return; };
|
||||
|
||||
// Check hardware key
|
||||
// Check U2F hardware key
|
||||
if (user.otphkeys && (user.otphkeys.length > 0) && (typeof (hwtoken1) == 'string') && (typeof (hwtoken2) == 'string')) {
|
||||
var u2fpublicKey = null;
|
||||
|
||||
// Find a U2F key
|
||||
for (var i = 0; i < user.otphkeys.length; i++) { if (user.otphkeys[i].type == 1) { u2fpublicKey = user.otphkeys[i].publicKey; } }
|
||||
|
||||
if (u2fpublicKey != null) {
|
||||
// Check hardware token
|
||||
var authRequest = null, authResponse = null;
|
||||
try { authRequest = JSON.parse(hwtoken1); } catch (ex) { }
|
||||
try { authResponse = JSON.parse(hwtoken2); } catch (ex) { }
|
||||
if ((authRequest != null) && (authResponse != null)) {
|
||||
const u2f = require('u2f');
|
||||
const result = u2f.checkSignature(authRequest[0], authResponse, user.otphkeys[0].publicKey);
|
||||
if (result.successful === true) return true;
|
||||
const result = u2f.checkSignature(authRequest[0], authResponse, u2fpublicKey);
|
||||
if (result.successful === true) { func(true); return; };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check Google Authenticator
|
||||
const otplib = require('otplib')
|
||||
if (user.otpsecret && (typeof (token) == 'string') && (otplib.authenticator.check(token, user.otpsecret) == true)) return true;
|
||||
if (user.otpsecret && (typeof (token) == 'string') && (token.length == 6) && (otplib.authenticator.check(token, user.otpsecret) == true)) { func(true); return; };
|
||||
|
||||
// Check written down keys
|
||||
if ((user.otpkeys != null) && (user.otpkeys.keys != null)) {
|
||||
if ((user.otpkeys != null) && (user.otpkeys.keys != null) && (typeof (token) == 'string') && (token.length == 8)) {
|
||||
var tokenNumber = parseInt(token);
|
||||
for (var i = 0; i < user.otpkeys.keys.length; i++) { if ((tokenNumber === user.otpkeys.keys[i].p) && (user.otpkeys.keys[i].u === true)) { user.otpkeys.keys[i].u = false; return true; } }
|
||||
for (var i = 0; i < user.otpkeys.keys.length; i++) { if ((tokenNumber === user.otpkeys.keys[i].p) && (user.otpkeys.keys[i].u === true)) { user.otpkeys.keys[i].u = false; func(true); return; } }
|
||||
}
|
||||
|
||||
return false;
|
||||
// Check OTP hardware key
|
||||
if (domain.yubikey.id && domain.yubikey.secret && user.otphkeys && (user.otphkeys.length > 0) && (typeof (token) == 'string') && (token.length == 44)) {
|
||||
var keyId = token.substring(0, 12);
|
||||
|
||||
// Find a matching OPT key
|
||||
var match = false;
|
||||
for (var i = 0; i < user.otphkeys.length; i++) { if ((user.otphkeys[i].type === 2) && (user.otphkeys[i].keyid === keyId)) { match = true; } }
|
||||
|
||||
// If we have a match, check the OTP
|
||||
if (match === true) {
|
||||
var yubikeyotp = require('yubikeyotp');
|
||||
var request = { otp: token, id: domain.yubikey.id, key: domain.yubikey.secret, timestamp: true }
|
||||
if (domain.yubikey.proxy) { request.requestParams = { proxy: domain.yubikey.proxy }; }
|
||||
yubikeyotp.verifyOTP(request, function (err, results) { func(results.status == 'OK'); });
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Return a hardware key challenge
|
||||
func(false);
|
||||
}
|
||||
|
||||
// Return a U2F hardware key challenge
|
||||
// TODO: Figure out how to support many U2F keys at the same time.
|
||||
function getHardwareKeyChallenge(domain, user) {
|
||||
if (user.otphkeys && (user.otphkeys.length > 0)) {
|
||||
// Find a U2F key
|
||||
var u2fKeyHandle = null;
|
||||
for (var i = 0; i < user.otphkeys.length; i++) { if (user.otphkeys[i].type == 1) { u2fKeyHandle = user.otphkeys[i].keyHandle; } }
|
||||
|
||||
// Generate a U2F challenge
|
||||
if (u2fKeyHandle != null) {
|
||||
var requests = [];
|
||||
const u2f = require('u2f');
|
||||
for (var i in user.otphkeys) { requests.push(u2f.request('https://' + obj.parent.certificates.CommonName, user.otphkeys[i].keyHandle)); }
|
||||
for (var i in user.otphkeys) { requests.push(u2f.request('https://' + obj.parent.certificates.CommonName, u2fKeyHandle)); }
|
||||
return JSON.stringify(requests);
|
||||
}
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
@ -398,51 +431,38 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
|
||||
|
||||
// Check if this user has 2-step login active
|
||||
if (checkUserOneTimePasswordRequired(domain, user)) {
|
||||
if (checkUserOneTimePassword(domain, user, req.body.token, req.body.hwtoken1, req.body.hwtoken2) == false) {
|
||||
checkUserOneTimePassword(domain, user, req.body.token, req.body.hwtoken1, req.body.hwtoken2, function (result) {
|
||||
if (result == false) {
|
||||
// 2-step auth is required, but the token is not present or not valid.
|
||||
if (user.otpsecret != null) { req.session.error = '<b style=color:#8C001A>Invalid token, try again.</b>'; }
|
||||
req.session.loginmode = '4';
|
||||
req.session.tokenusername = xusername;
|
||||
req.session.tokenpassword = xpassword;
|
||||
res.redirect(domain.url);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
// Check if this user has 2-step login active
|
||||
var tokenValid = 0;
|
||||
const twoStepLoginSupported = ((domain.auth != 'sspi') && (obj.parent.certificates.CommonName != 'un-configured') && (obj.args.lanonly !== true) && (obj.args.nousers !== true));
|
||||
const otplib = require('otplib')
|
||||
otplib.authenticator.options = { window: 6 }; // Set +/- 3 minute window
|
||||
if (twoStepLoginSupported && user.otpsecret && ((typeof (req.body.token) != 'string') || ((tokenValid = otplib.authenticator.check(req.body.token, user.otpsecret)) !== true))) {
|
||||
// Failed OTP, check user's one time passwords
|
||||
console.log(user);
|
||||
if ((req.body.token != null) && ((user.otpkeys != null) && (user.otpkeys.keys != null)) || (user.otphkeys && user.otphkeys.length > 0)) {
|
||||
var found = null;
|
||||
var tokenNumber = parseInt(req.body.token);
|
||||
for (var i = 0; i < user.otpkeys.keys.length; i++) { if ((tokenNumber === user.otpkeys.keys[i].p) && (user.otpkeys.keys[i].u === true)) { user.otpkeys.keys[i].u = false; found = i; } }
|
||||
if (found == null) {
|
||||
// 2-step auth is required, but the token is not present or not valid.
|
||||
if (user.otpsecret != null) { req.session.error = '<b style=color:#8C001A>Invalid token, try again.</b>'; }
|
||||
req.session.loginmode = '4';
|
||||
req.session.tokenusername = xusername;
|
||||
req.session.tokenpassword = xpassword;
|
||||
res.redirect(domain.url);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
// 2-step auth is required, but the token is not present or not valid.
|
||||
if (user.otpsecret != null) { req.session.error = '<b style=color:#8C001A>Invalid token, try again.</b>'; }
|
||||
req.session.loginmode = '4';
|
||||
req.session.tokenusername = xusername;
|
||||
req.session.tokenpassword = xpassword;
|
||||
res.redirect(domain.url);
|
||||
// Login succesful
|
||||
completeLoginRequest(req, res, domain, user, userid);
|
||||
}
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
// Login succesful
|
||||
completeLoginRequest(req, res, domain, user, userid);
|
||||
} else {
|
||||
delete req.session.loginmode;
|
||||
if (err == 'locked') { req.session.error = '<b style=color:#8C001A>Account locked.</b>'; } else { req.session.error = '<b style=color:#8C001A>Login failed, check username and password.</b>'; }
|
||||
if ((passhint != null) && (passhint.length > 0)) {
|
||||
req.session.passhint = passhint;
|
||||
} else {
|
||||
if (req.session.passhint) { delete req.session.passhint; }
|
||||
}
|
||||
res.redirect(domain.url);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function completeLoginRequest(req, res, domain, user, userid) {
|
||||
// Save login time
|
||||
user.login = Math.floor(Date.now() / 1000);
|
||||
obj.db.SetUser(user);
|
||||
@ -481,17 +501,6 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
|
||||
//});
|
||||
|
||||
obj.parent.DispatchEvent(['*'], obj, { etype: 'user', username: user.name, action: 'login', msg: 'Account login', domain: domain.id });
|
||||
} else {
|
||||
delete req.session.loginmode;
|
||||
if (err == 'locked') { req.session.error = '<b style=color:#8C001A>Account locked.</b>'; } else { req.session.error = '<b style=color:#8C001A>Login failed, check username and password.</b>'; }
|
||||
if ((passhint != null) && (passhint.length > 0)) {
|
||||
req.session.passhint = passhint;
|
||||
} else {
|
||||
if (req.session.passhint) { delete req.session.passhint; }
|
||||
}
|
||||
res.redirect(domain.url);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function handleCreateAccountRequest(req, res) {
|
||||
|