Added autofido2fa option in PasswordRequirements, #2952

Ylian Saint-Hilaire committed Jul 28, 2021 at 10:35 UTC 15ddd3cd00e1cb3fbcd5fa53716743017f1906f0
5 files changed +30 -5
meshcentral-config-schema.json
+2 -1
@@ -385,7 +385,8 @@
385 "oldPasswordBan": { "type": "integer", "description": "Number of old passwords the server should remember and not allow the user to switch back to." },
386 "banCommonPasswords": { "type": "boolean", "default": false, "description": "Uses WildLeek to block use of the 10000 most commonly used passwords." },
387 "loginTokens": { "type": "boolean", "default": true, "description": "Allows users to create alternative username/passwords for their account." },
388 - "twoFactorTimeout": { "type": "integer", "default": 300, "description": "Maximum about of time the to wait for a 2FA token on the login page in seconds." }
388 + "twoFactorTimeout": { "type": "integer", "default": 300, "description": "Maximum about of time the to wait for a 2FA token on the login page in seconds." },
389 + "autofido2fa": { "type": "boolean", "default": false, "description": "If true and user account has FIDO key setup, 2FA login screen will automatically request FIDO 2FA." }
390 }
391 },
392 "twoFactorCookieDurationDays": { "type": "integer", "default": 30, "description": "Number of days that a user is allowed to remember this device for when completing 2FA. Set this to 0 to remove this option." },
views/login-mobile.handlebars
+8 -2
@@ -324,6 +324,7 @@
324 var currentpanel = 0;
325 var otpemail = ('{{{otpemail}}}' === 'true');
326 var otpsms = ('{{{otpsms}}}' === 'true');
327 + var autofido = (decodeURIComponent('{{{autofido}}}') === 'true');
328 var twoFactorCookieDays = parseInt('{{{twoFactorCookieDays}}}');
329 var authStrategies = '{{{authStrategies}}}'.split(',');
330 var tokenTimeout = parseInt('{{{tokenTimeout}}}');
@@ -405,15 +406,20 @@
406 if (loginMode == '4') {
407 if (tokenTimeout > 0) { setTimeout(function () { Q('hwtokenInput').value = '**timeout**'; QE('tokenOkButton', true); Q('tokenOkButton').click(); }, tokenTimeout); }
408 try { if (hardwareKeyChallenge.length > 0) { hardwareKeyChallenge = JSON.parse(hardwareKeyChallenge); } else { hardwareKeyChallenge = null; } } catch (ex) { hardwareKeyChallenge = null }
408 - QV('securityKeyButton', (hardwareKeyChallenge != null) && (hardwareKeyChallenge.type == 'webAuthn'));
409 + var twofakey = (hardwareKeyChallenge != null) && (hardwareKeyChallenge.type == 'webAuthn');
410 + QV('securityKeyButton', twofakey);
411 QV('emailKeyButton', otpemail && (messageid != 2) && (messageid != 4));
412 QV('smsKeyButton', otpsms && (messageid != 2) && (messageid != 4));
413 +
414 + // If hardware key is an option, trigger it now
415 + if (autofido && twofakey) { setTimeout(function () { useSecurityKey(1); }, 300); }
416 }
417
418 if (loginMode == '5') {
419 if (tokenTimeout > 0) { setTimeout(function () { Q('hwtokenInput').value = '**timeout**'; QE('tokenOkButton', true); Q('tokenOkButton').click(); }, tokenTimeout); }
420 try { if (hardwareKeyChallenge.length > 0) { hardwareKeyChallenge = JSON.parse(hardwareKeyChallenge); } else { hardwareKeyChallenge = null; } } catch (ex) { hardwareKeyChallenge = null }
416 - if ((hardwareKeyChallenge != null) && (hardwareKeyChallenge.type == 'webAuthn')) {
421 + var twofakey = (hardwareKeyChallenge != null) && (hardwareKeyChallenge.type == 'webAuthn');
422 + if (twofakey) {
423 if (typeof hardwareKeyChallenge.challenge == 'string') { hardwareKeyChallenge.challenge = Uint8Array.from(atob(hardwareKeyChallenge.challenge), function (c) { return c.charCodeAt(0) }).buffer; }
424
425 publicKeyCredentialRequestOptions = { challenge: hardwareKeyChallenge.challenge, allowCredentials: [], timeout: hardwareKeyChallenge.timeout }
views/login.handlebars
+11 -2
@@ -323,6 +323,7 @@
323 var publicKeyCredentialRequestOptions = null;
324 var otpemail = (decodeURIComponent('{{{otpemail}}}') === 'true');
325 var otpsms = (decodeURIComponent('{{{otpsms}}}') === 'true');
326 + var autofido = (decodeURIComponent('{{{autofido}}}') === 'true');
327 var twoFactorCookieDays = parseInt('{{{twoFactorCookieDays}}}');
328 var authStrategies = '{{{authStrategies}}}'.split(',');
329 var tokenTimeout = parseInt('{{{tokenTimeout}}}');
@@ -435,17 +436,25 @@
436 if (loginMode == '4') {
437 if (tokenTimeout > 0) { setTimeout(function () { Q('hwtokenInput').value = '**timeout**'; QE('tokenOkButton', true); Q('tokenOkButton').click(); }, tokenTimeout); }
438 try { if (hardwareKeyChallenge.length > 0) { hardwareKeyChallenge = JSON.parse(hardwareKeyChallenge); } else { hardwareKeyChallenge = null; } } catch (ex) { hardwareKeyChallenge = null }
438 - QV('securityKeyButton', (hardwareKeyChallenge != null) && (hardwareKeyChallenge.type == 'webAuthn'));
439 + var twofakey = (hardwareKeyChallenge != null) && (hardwareKeyChallenge.type == 'webAuthn');
440 + QV('securityKeyButton', twofakey);
441 QV('emailKeyButton', otpemail && (messageid != 2) && (messageid != 4));
442 QV('smsKeyButton', otpsms && (messageid != 2) && (messageid != 4));
443 +
444 + // If hardware key is an option, trigger it now
445 + if (autofido && twofakey) { setTimeout(function () { useSecurityKey(1); }, 300); }
446 }
447
448 if (loginMode == '5') {
449 if (tokenTimeout > 0) { setTimeout(function () { Q('hwtokenInput').value = '**timeout**'; QE('tokenOkButton', true); Q('tokenOkButton').click(); }, tokenTimeout); }
450 try { if (hardwareKeyChallenge.length > 0) { hardwareKeyChallenge = JSON.parse(hardwareKeyChallenge); } else { hardwareKeyChallenge = null; } } catch (ex) { hardwareKeyChallenge = null }
446 - QV('securityKeyButton2', (hardwareKeyChallenge != null) && (hardwareKeyChallenge.type == 'webAuthn'));
451 + var twofakey = (hardwareKeyChallenge != null) && (hardwareKeyChallenge.type == 'webAuthn');
452 + QV('securityKeyButton2', twofakey);
453 QV('emailKeyButton2', otpemail && (messageid != 2) && (messageid != 4));
454 QV('smsKeyButton2', otpsms && (messageid != 2) && (messageid != 4));
455 +
456 + // If hardware key is an option, trigger it now
457 + if (autofido && twofakey) { setTimeout(function () { useSecurityKey(2); }, 300); }
458 }
459
460 /*
views/login2.handlebars
+7
@@ -356,6 +356,7 @@
356 var otpemail = (decodeURIComponent('{{{otpemail}}}') === 'true');
357 var otpsms = (decodeURIComponent('{{{otpsms}}}') === 'true');
358 var otppush = (decodeURIComponent('{{{otppush}}}') === 'true');
359 + var autofido = (decodeURIComponent('{{{autofido}}}') === 'true');
360 var twoFactorCookieDays = parseInt('{{{twoFactorCookieDays}}}');
361 var authStrategies = '{{{authStrategies}}}'.split(',');
362 var tokenTimeout = parseInt('{{{tokenTimeout}}}');
@@ -475,6 +476,9 @@
476 QV('smsKeyButton', smskey);
477 QV('pushKeyButton', pushkey);
478 QV('2farow', twofakey || emailkey || smskey || pushkey);
479 +
480 + // If hardware key is an option, trigger it now
481 + if (autofido && twofakey) { setTimeout(function () { useSecurityKey(1); }, 300); }
482 }
483
484 if (loginMode == '5') {
@@ -489,6 +493,9 @@
493 QV('smsKeyButton2', smskey);
494 QV('pushKeyButton', pushkey);
495 QV('2farow2', twofakey || emailkey || smskey || pushkey);
496 +
497 + // If hardware key is an option, trigger it now
498 + if (autofido && twofakey) { setTimeout(function () { useSecurityKey(2); }, 300); }
499 }
500
501 if (loginMode == '8') {
webserver.js
+2
@@ -2863,6 +2863,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
2863 if ((typeof domain.passwordrequirements == 'object') && (domain.passwordrequirements.sms2factor == false)) { otpsms = false; }
2864 var otppush = (parent.firebase != null) && (req.session != null) && (req.session.tpush === 1);
2865 if ((typeof domain.passwordrequirements == 'object') && (domain.passwordrequirements.push2factor == false)) { otppush = false; }
2866 + const autofido = ((typeof domain.passwordrequirements == 'object') && (domain.passwordrequirements.autofido2fa == true)); // See if FIDO should be automatically prompted if user account has it.
2867
2868 // See if we support two-factor trusted cookies
2869 var twoFactorCookieDays = 30;
@@ -2917,6 +2918,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
2918 otpemail: otpemail,
2919 otpsms: otpsms,
2920 otppush: otppush,
2921 + autofido: autofido,
2922 twoFactorCookieDays: twoFactorCookieDays,
2923 authStrategies: authStrategies.join(','),
2924 loginpicture: (typeof domain.loginpicture == 'string'),