1
0
Fork 0
mirror of https://github.com/Ylianst/MeshCentral.git synced 2025-03-09 15:40:18 +00:00

Improved U2F authentication, added multiple U2F key support.

This commit is contained in:
Ylian Saint-Hilaire 2019-02-10 16:04:36 -08:00
parent 922ed49cdd
commit 7272ecb089
12 changed files with 191 additions and 155 deletions

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -234,14 +234,14 @@
<div id=p3info style="overflow-y:scroll;position:absolute;top:55px;bottom:0px;width:100%">
<div style="margin-left:8px">
<div id="p3AccountActions">
<p><strong>Account actions</strong></p>
<p><strong>Account Security</strong></p>
<div style="margin-left:9px;margin-bottom:8px">
<div id="manageAuthApp" style="margin-top:5px;display:none"><a onclick="account_manageAuthApp()" style="cursor:pointer">Manage authenticator app</a></div>
<div id="manageOtp" style="margin-top:5px;display:none"><a onclick="account_manageOtp(0)" style="cursor:pointer">Manage backup codes</a></div>
</div>
<p><strong>Account Actions</strong></p>
<div style="margin-left:9px;margin-bottom:8px">
<div style="margin-top:5px"><span id="verifyEmailId" style="display:none"><a onclick="account_showVerifyEmail()" style="cursor:pointer">Verify email</a></span></div>
<div style="margin-top:5px">
<span id="otpAuth" style="display:none"><a onclick="account_addOtp()" style="cursor:pointer">Add 2-step login</a><br /></span>
<span id="otpAuthRemove" style="display:none"><a onclick="account_removeOtp()" style="cursor:pointer">Remove 2-step login</a><br /></span>
</div>
<div id="manageOtp" style="margin-top:5px;display:none"><a onclick="account_manageOtp(0)" style="cursor:pointer">One-time passwords</a></div>
<div style="margin-top:5px"><a onclick="account_showChangeEmail()" style="cursor:pointer">Change email address</a></div>
<div style="margin-top:5px"><a onclick="account_showChangePassword()" style="cursor:pointer">Change password</a></div>
<div style="margin-top:5px"><a onclick="account_showDeleteAccount()" style="cursor:pointer">Delete account</a></div>
@ -652,9 +652,8 @@
function updateSelf() {
QV('verifyEmailId', (userinfo.emailVerified !== true) && (userinfo.email != null) && (serverinfo.emailcheck == true));
QV('otpAuth', ((features & 4096) != 0) && (userinfo.otpsecret != 1));
QV('otpAuthRemove', ((features & 4096) != 0) && (userinfo.otpsecret == 1));
QV('manageOtp', ((features & 4096) != 0) && (userinfo.otpsecret == 1));
QV('manageAuthApp', features & 4096);
QV('manageOtp', ((features & 4096) != 0) && ((userinfo.otpsecret == 1) || (userinfo.otphkeys > 0)));
}
function onMessage(server, message) {
@ -742,12 +741,12 @@
}
case 'otpauth-setup': {
if (xxdialogMode) return;
setDialogMode(2, "Add 2-Step Login", 1, null, message.success ? "<b style=color:green>2-step login activation successful</b>. You will now need a valid token to login again." : "<b style=color:red>2-step login activation failed</b>. Clear the secret from the application and try again. You only have a few minutes to enter the proper code.");
setDialogMode(2, "Authenticator App", 1, null, message.success ? "<b style=color:green>2-step login activation successful</b>. You will now need a valid token to login again." : "<b style=color:red>2-step login activation failed</b>. Clear the secret from the application and try again. You only have a few minutes to enter the proper code.");
break;
}
case 'otpauth-clear': {
if (xxdialogMode) return;
setDialogMode(2, "Remove 2-Step Login", 1, null, message.success ? "<b style=color:green>2-step login activation removed</b>. You can reactivate this feature at any time." : "<b style=color:red>2-step login activation removal failed</b>. Try again.");
setDialogMode(2, "Authenticator App", 1, null, message.success ? "<b style=color:green>2-step login activation removed</b>. You can reactivate this feature at any time." : "<b style=color:red>2-step login activation removal failed</b>. Try again.");
break;
}
case 'otpauth-getpasswords': {
@ -770,7 +769,7 @@
x += "<input type=button value='New Tokens' onclick='account_manageOtp(1);'></input>";
if (message.passwords != null) { x += "<input type=button value='Clear' onclick='account_manageOtp(2);'></input>"; }
x += "</div><br />";
setDialogMode(2, "One-Time Passwords", 8, null, x, 'otpauth-manage');
setDialogMode(2, "Manage Backup Codes", 8, null, x, 'otpauth-manage');
break;
}
case 'event': {
@ -976,13 +975,13 @@
break;
}
default:
console.log('Unknown message.event.action', message.event.action);
//console.log('Unknown message.event.action', message.event.action);
break;
}
break;
}
default:
console.log('Unknown message.action', message.action);
//console.log('Unknown message.action', message.action);
break;
}
}
@ -1028,9 +1027,14 @@
// MY ACCOUNT
//
function account_manageAuthApp() {
if (xxdialogMode || ((features & 4096) == 0)) return;
if (userinfo.otpsecret == 1) { account_removeOtp(); } else { account_addOtp(); }
}
function account_addOtp() {
if (xxdialogMode || (userinfo.otpsecret == 1) || ((features & 4096) == 0)) return;
setDialogMode(2, "Add 2-Step Login", 2, function () { meshserver.send({ action: 'otpauth-setup', secret: Q('d2optsecret').attributes.secret.value, token: Q('d2otpauthinput').value }); }, "<div id=d2optinfo>Loading...</div>", 'otpauth-request');
setDialogMode(2, "Authenticator App", 2, function () { meshserver.send({ action: 'otpauth-setup', secret: Q('d2optsecret').attributes.secret.value, token: Q('d2otpauthinput').value }); }, "<div id=d2optinfo>Loading...</div>", 'otpauth-request');
meshserver.send({ action: 'otpauth-request' });
}
@ -1042,7 +1046,7 @@
function account_removeOtp() {
if (xxdialogMode || (userinfo.otpsecret != 1) || ((features & 4096) == 0)) return;
setDialogMode(2, "Remove 2-Step Login", 3, function () { meshserver.send({ action: 'otpauth-clear' }); }, "Confirm removal of 2-step login?");
setDialogMode(2, "Authenticator App", 3, function () { meshserver.send({ action: 'otpauth-clear' }); }, "Confirm removal of authenticator application 2-step login?");
}
function account_manageOtp(action) {

View file

@ -246,14 +246,19 @@
</div>
<div id=p2 style="display:none">
<h1>My Account</h1>
<img alt="" width=150 height=103 src=images/mainaccount.jpg style=margin-bottom:10px;margin-right:20px;float:right />
<div id="p2AccountSecurity" style="display:none">
<p><strong>Account security</strong></p>
<div style="margin-left:25px">
<div id="manageAuthApp"><div style="width:15px;display:inline-block"><span id="authAppSetupCheck" style="color:green;font-size:10px"><strong>&#x2713;</strong></span></div><span><a onclick="account_manageAuthApp()" style="cursor:pointer">Manage authenticator app</a><br /></span></div>
<div id="manageHardwareOtp"><div style="width:15px;display:inline-block"><span id="authKeySetupCheck" style="color:green;font-size:10px"><strong>&#x2713;</strong></span></div><span><a onclick="account_manageHardwareOtp(0)" style="cursor:pointer">Manage security keys</a><br /></span></div>
<div id="manageOtp"><div style="width:15px;display:inline-block"><span id="authCodesSetupCheck" style="color:green;font-size:10px"><strong>&#x2713;</strong></span></div><span><a onclick="account_manageOtp(0)" style="cursor:pointer">Manage backup codes</a><br /></span></div>
</div>
</div>
<div id="p2AccountActions">
<p><strong><img alt="" width=150 height=103 src=images/mainaccount.jpg style=margin-bottom:10px;margin-right:20px;float:right />Account actions</strong></p>
<p><strong>Account actions</strong></p>
<p style="margin-left:40px">
<span id="verifyEmailId" style="display:none"><a onclick="account_showVerifyEmail()" style="cursor:pointer">Verify email</a><br /></span>
<span id="otpAuth" style="display:none"><a onclick="account_addOtp()" style="cursor:pointer">Add 2-step login</a><br /></span>
<span id="otpAuthRemove" style="display:none"><a onclick="account_removeOtp()" style="cursor:pointer">Remove 2-step login</a><br /></span>
<span id="manageHardwareOtp" style="display:none"><a onclick="account_manageHardwareOtp(0)" style="cursor:pointer">Manage hardware login keys</a><br /></span>
<span id="manageOtp" style="display:none"><a onclick="account_manageOtp(0)" style="cursor:pointer">Manage one time passwords</a><br /></span>
<a onclick="account_showChangeEmail()" style="cursor:pointer">Change email address</a><br />
<a onclick="account_showChangePassword()" style="cursor:pointer">Change password</a><br />
<a onclick="account_showDeleteAccount()" style="cursor:pointer">Delete account</a><br />
@ -1109,6 +1114,7 @@
// Update account actions
QV('p2AccountActions', ((features & 4) == 0) && (serverinfo.domainauth == false)); // Hide Account Actions if in single user mode or domain authentication
QV('p2AccountSecurity', ((features & 4) == 0) && (serverinfo.domainauth == false) && ((features & 4096) != 0)); // Hide Account Security if in single user mode, domain authentication to 2 factor auth not supported.
QV('p2ServerActions', siteRights & 21);
QV('LeftMenuMyServer', siteRights & 21); // 16 + 4 + 1
QV('MainMenuMyServer', siteRights & 21);
@ -1171,10 +1177,10 @@
function updateSelf() {
QV('verifyEmailId', (userinfo.emailVerified !== true) && (userinfo.email != null) && (serverinfo.emailcheck == true));
QV('verifyEmailId2', (userinfo.emailVerified !== true) && (userinfo.email != null) && (serverinfo.emailcheck == true));
QV('otpAuth', ((features & 4096) != 0) && (userinfo.otpsecret != 1));
QV('otpAuthRemove', ((features & 4096) != 0) && (userinfo.otpsecret == 1));
QV('manageOtp', ((features & 4096) != 0) && ((userinfo.otpsecret == 1) || (userinfo.otphkeys > 0)));
QV('manageHardwareOtp', ((features & 0x5000) != 0)); // Requires 2-step login + YubiKey support
QV('manageOtp', (userinfo.otpsecret == 1) || (userinfo.otphkeys > 0));
QV('authAppSetupCheck', userinfo.otpsecret == 1);
QV('authKeySetupCheck', userinfo.otphkeys > 0);
QV('authCodesSetupCheck', userinfo.otpkeys > 0);
}
function onMessage(server, message) {
@ -1410,12 +1416,12 @@
}
case 'otpauth-setup': {
if (xxdialogMode) return;
setDialogMode(2, "Add 2-Step Login", 1, null, message.success ? "<b style=color:green>2-step login activation successful</b>. You will now need a valid token to login again." : "<b style=color:red>2-step login activation failed</b>. Clear the secret from the application and try again. You only have a few minutes to enter the proper code.");
setDialogMode(2, "Authenticator App", 1, null, message.success ? "<b style=color:green>Authenticator app activation successful</b>. You will now need a valid token to login again." : "<b style=color:red>2-step login activation failed</b>. Clear the secret from the application and try again. You only have a few minutes to enter the proper code.");
break;
}
case 'otpauth-clear': {
if (xxdialogMode) return;
setDialogMode(2, "Remove 2-Step Login", 1, null, message.success ? "<b style=color:green>2-step login activation removed</b>. You can reactivate this feature at any time." : "<b style=color:red>2-step login activation removal failed</b>. Try again.");
setDialogMode(2, "Authenticator App", 1, null, message.success ? "<b>Authenticator application removed</b>. You can reactivate this feature at any time." : "<b style=color:red>2-step login activation removal failed</b>. Try again.");
break;
}
case 'otpauth-getpasswords': {
@ -1438,55 +1444,53 @@
x += "<input type=button value='Generate New Tokens' onclick='account_manageOtp(1);'></input>";
if (message.passwords != null) { x += "<input type=button value='Clear Tokens' onclick='account_manageOtp(2);'></input>"; }
x += "</div><br />";
setDialogMode(2, "Manage One Time Passwords", 8, null, x, 'otpauth-manage');
setDialogMode(2, "Manage Backup Codes", 8, null, x, 'otpauth-manage');
break;
}
case 'otp-hkey-get': {
if (xxdialogMode && (xxdialogTag != 'otpauth-hardware-manage')) return;
var start = "<div style='border-radius:6px;border: 2px solid #CCC;background-color:#BBB;width:100%;margin-top:8px'><div style='padding:8px;font-family:Arial, Helvetica, sans-serif;font-size:16px;font-weight:bold'><table style=width:100%;text-align:left>";
var start = "<div style='border-radius:6px;border:2px solid #CCC;background-color:#BBB;width:100%;box-sizing:border-box;margin-bottom:6px'><div style='margin:3px;font-family:Arial, Helvetica, sans-serif;font-size:16px;font-weight:bold'><table style=width:100%;text-align:left>";
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;
x += "<div style='max-height:150px;overflow-y:auto;overflow-x:hidden;margin-top:6px;margin-bottom:6px'>";
if (message.keys && message.keys.length > 0) {
for (var i in message.keys) {
var key = message.keys[i];
var type = 'OTP';
if (key.type == 1) { keyType1++; type = 'U2F'; }
var key = message.keys[i], type = (key.type == 1)?'U2F':'OTP';
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 += start + '<tr style=text-align:center><td>No Keys Configured' + end;
}
x += "<br />";
x += "</div>";
x += "<div><input type=button value='Close' onclick=setDialogMode(0) style=float:right></input>";
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() == false) || (keyType1 > 0)) { QE('d2addkey1', false); }
setDialogMode(2, "Manage Security Keys", 8, null, x, 'otpauth-hardware-manage');
if (u2fSupported() == false) { 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 />');
setDialogMode(2, "Add Security 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 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) {
setDialogMode(2, "Add Security Key", 2, null, x);
window.u2f.register(message.request.appId, message.request.registerRequests, message.request.registeredKeys, 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');
meshserver.send({ action: 'otp-hkey-setup-response', response: registrationResponse, name: Q('dp1keyname').value });
setDialogMode(2, "Add Security Key", 0, null, '<br />Checking...<br /><br /><br />', 'otpauth-hardware-manage');
} else {
setDialogMode(2, "Add Hardware Login Key", 1, null, '<br />Error code ' + registrationResponse.errorCode + '<br /><br />');
var errorCodes = ['', 'Unknown error', 'Bad request', 'Unsupported configuration', 'This key was already registered', 'Timeout'];
setDialogMode(2, "Add Security Key", 1, null, '<br />' + errorCodes[registrationResponse.errorCode] + '.<br /><br />');
}
});
}, message.request.timeoutSeconds);
break;
}
case 'otp-hkey-setup-response': {
@ -1494,7 +1498,7 @@
if (message.result == true) {
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 />', 'otpauth-hardware-manage');
setDialogMode(2, "Add Security Key", 1, null, '<br />ERROR: Unable to add key.<br /><br />', 'otpauth-hardware-manage');
}
break;
}
@ -5287,9 +5291,14 @@
// MY ACCOUNT
//
function account_manageAuthApp() {
if (xxdialogMode || ((features & 4096) == 0)) return;
if (userinfo.otpsecret == 1) { account_removeOtp(); } else { account_addOtp(); }
}
function account_addOtp() {
if (xxdialogMode || (userinfo.otpsecret == 1) || ((features & 4096) == 0)) return;
setDialogMode(2, "Add 2-Step Login", 2, function () { meshserver.send({ action: 'otpauth-setup', secret: Q('d2optsecret').attributes.secret.value, token: Q('d2otpauthinput').value }); }, "<div id=d2optinfo>Loading...</div>", 'otpauth-request');
setDialogMode(2, "Authenticator App", 2, function () { meshserver.send({ action: 'otpauth-setup', secret: Q('d2optsecret').attributes.secret.value, token: Q('d2otpauthinput').value }); }, "<div id=d2optinfo>Loading...</div>", 'otpauth-request');
meshserver.send({ action: 'otpauth-request' });
}
@ -5301,7 +5310,7 @@
function account_removeOtp() {
if (xxdialogMode || (userinfo.otpsecret != 1) || ((features & 4096) == 0)) return;
setDialogMode(2, "Remove 2-Step Login", 3, function () { meshserver.send({ action: 'otpauth-clear' }); }, "Confirm removal of 2-step login?");
setDialogMode(2, "Authenticator App", 3, function () { meshserver.send({ action: 'otpauth-clear' }); }, "Confirm removal of authenticator application 2-step login?");
}
function account_manageOtp(action) {
@ -5321,11 +5330,11 @@
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,2) />');
} else if (type == 2) {
var x = "Type in a key name, select the OTP box and press the USB key button<br /><br />";
var x = "Type in a key name, select the OTP box and press the button on the YubiKey&trade;.<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) />');
x += addHtmlValue('YubiKey&trade; OTP', '<input id=dp1key style=width:230px autocomplete=off onkeyup=account_addhkeyValidate(event,2) />');
}
setDialogMode(2, "Add Hardware Login Key", 3, account_addhkeyEx, x, type);
setDialogMode(2, "Add Security Key", 3, account_addhkeyEx, x, type);
Q('dp1keyname').focus();
}
@ -5340,7 +5349,7 @@
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');
setDialogMode(2, "Add Security Key", 0, null, "<br />Checking...<br /><br /><br />", 'otpauth-hardware-manage');
}
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -151,8 +151,7 @@
<td align=right width=100>Login token:</td>
<td>
<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" />
<input id=hwtokenInput type=text name=hwtoken style="display:none" />
</td>
</tr>
<tr>
@ -229,15 +228,13 @@
if ('{{loginmode}}' == '4') {
try { if (hardwareKeyChallenge.length > 0) { hardwareKeyChallenge = JSON.parse(hardwareKeyChallenge); } else { hardwareKeyChallenge = null; } } catch (ex) { hardwareKeyChallenge = null }
if ((hardwareKeyChallenge != null) && u2fSupported()) {
var c = hardwareKeyChallenge[0];
window.u2f.sign(c.appId, c.challenge, hardwareKeyChallenge, function (authResponse) {
window.u2f.sign(hardwareKeyChallenge.appId, hardwareKeyChallenge.challenge, hardwareKeyChallenge.registeredKeys, function (authResponse) {
if (authResponse.signatureData) {
Q('hwtokenInput1').value = JSON.stringify(hardwareKeyChallenge);
Q('hwtokenInput2').value = JSON.stringify(authResponse);
Q('hwtokenInput').value = JSON.stringify(authResponse);
QE('tokenOkButton', true);
Q('tokenOkButton').click();
}
});
}, hardwareKeyChallenge.timeoutSeconds);
}
}
}

View file

@ -224,8 +224,7 @@
<td align=right width=100>Login token:</td>
<td>
<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" />
<input id=hwtokenInput type=text name=hwtoken style="display:none" />
</td>
</tr>
<tr>
@ -312,15 +311,13 @@
if ('{{loginmode}}' == '4') {
try { if (hardwareKeyChallenge.length > 0) { hardwareKeyChallenge = JSON.parse(hardwareKeyChallenge); } else { hardwareKeyChallenge = null; } } catch (ex) { hardwareKeyChallenge = null }
if ((hardwareKeyChallenge != null) && u2fSupported()) {
var c = hardwareKeyChallenge[0];
window.u2f.sign(c.appId, c.challenge, hardwareKeyChallenge, function (authResponse) {
window.u2f.sign(hardwareKeyChallenge.appId, hardwareKeyChallenge.challenge, hardwareKeyChallenge.registeredKeys, function (authResponse) {
if (authResponse.signatureData) {
Q('hwtokenInput1').value = JSON.stringify(hardwareKeyChallenge);
Q('hwtokenInput2').value = JSON.stringify(authResponse);
Q('hwtokenInput').value = JSON.stringify(authResponse);
QE('tokenOkButton', true);
Q('tokenOkButton').click();
}
});
}, hardwareKeyChallenge.timeoutSeconds);
}
}
}