From 3670f229b9cb6a396bd3526934517d3fffaf04d0 Mon Sep 17 00:00:00 2001 From: Ylian Saint-Hilaire Date: Thu, 28 Nov 2019 22:29:22 -0800 Subject: [PATCH] Added remember for 30 days 2nd factor option. --- agents/meshcmd.js | 2 +- agents/meshcmd.min.js | 2 +- interceptor.js | 2 +- views/default-min.handlebars | 2 +- views/default.handlebars | 2 +- views/login-min.handlebars | 2 +- views/login-mobile-min.handlebars | 2 +- views/login-mobile.handlebars | 70 +++++++++++-------- views/login.handlebars | 69 ++++++++++-------- views/translations/default-min_fr.handlebars | 2 +- views/translations/default_fr.handlebars | 2 +- views/translations/login-min_fr.handlebars | 2 +- .../login-mobile-min_fr.handlebars | 2 +- views/translations/login-mobile_fr.handlebars | 70 +++++++++++-------- views/translations/login_fr.handlebars | 69 ++++++++++-------- webserver.js | 30 ++++++-- 16 files changed, 197 insertions(+), 133 deletions(-) diff --git a/agents/meshcmd.js b/agents/meshcmd.js index e8216e54..17bad00e 100644 --- a/agents/meshcmd.js +++ b/agents/meshcmd.js @@ -778,7 +778,7 @@ function performAmtAgentPresenceAssert() { // Called after the agent is asserted function performAmtAgentPresenceAssertRetry(stack, name, response, status, watchdog) { if (status == 200) { - debug(1, 'Succesful assert, sequence = ' + watchdog.Seq); + debug(1, 'Successful assert, sequence = ' + watchdog.Seq); watchdog.Retry = 0; tempWatchdogTimer = setTimeout(performAmtAgentPresenceAssert, watchdog.Interval); } else { diff --git a/agents/meshcmd.min.js b/agents/meshcmd.min.js index e8216e54..17bad00e 100644 --- a/agents/meshcmd.min.js +++ b/agents/meshcmd.min.js @@ -778,7 +778,7 @@ function performAmtAgentPresenceAssert() { // Called after the agent is asserted function performAmtAgentPresenceAssertRetry(stack, name, response, status, watchdog) { if (status == 200) { - debug(1, 'Succesful assert, sequence = ' + watchdog.Seq); + debug(1, 'Successful assert, sequence = ' + watchdog.Seq); watchdog.Retry = 0; tempWatchdogTimer = setTimeout(performAmtAgentPresenceAssert, watchdog.Interval); } else { diff --git a/interceptor.js b/interceptor.js index c4b9c298..c32ad805 100644 --- a/interceptor.js +++ b/interceptor.js @@ -326,7 +326,7 @@ module.exports.CreateRedirInterceptor = function (args) { obj.amt.digestQOP = obj.amt.acc.substring(12 + realmlen + noncelen, 12 + realmlen + noncelen + qoplen); } else if (authType != obj.AuthenticationType.QUERY && authstatus == obj.AuthenticationStatus.SUCCESS) { - // Intel AMT relayed that authentication was succesful, go to direct relay mode in both directions. + // Intel AMT relayed that authentication was successful, go to direct relay mode in both directions. obj.ws.direct = true; obj.amt.direct = true; } diff --git a/views/default-min.handlebars b/views/default-min.handlebars index f4bbf755..879d315a 100644 --- a/views/default-min.handlebars +++ b/views/default-min.handlebars @@ -864,7 +864,7 @@ if (message.keys && message.keys.length > 0) { for (var i in message.keys) { var key = message.keys[i], type = (key.type == 2)?'OTP':'WebAuthn'; - x += start + '' + key.name + '' + end; + x += start + '' + key.name + '' + end; } } else { x += start + '' + "No Keys Configured" + end; diff --git a/views/default.handlebars b/views/default.handlebars index 62fcf690..ff229c5d 100644 --- a/views/default.handlebars +++ b/views/default.handlebars @@ -1888,7 +1888,7 @@ if (message.keys && message.keys.length > 0) { for (var i in message.keys) { var key = message.keys[i], type = (key.type == 2)?'OTP':'WebAuthn'; - x += start + '' + key.name + '' + end; + x += start + '' + key.name + '' + end; } } else { x += start + '' + "No Keys Configured" + end; diff --git a/views/login-min.handlebars b/views/login-min.handlebars index 5278937a..047bb5f2 100644 --- a/views/login-min.handlebars +++ b/views/login-min.handlebars @@ -1 +1 @@ -{{{title}}} - Login
{{{title}}}
{{{title2}}}

Welcome


\ No newline at end of file +{{{title}}} - Login
{{{title}}}
{{{title2}}}

Welcome


\ No newline at end of file diff --git a/views/login-mobile-min.handlebars b/views/login-mobile-min.handlebars index f95e95fd..1da13992 100644 --- a/views/login-mobile-min.handlebars +++ b/views/login-mobile-min.handlebars @@ -1 +1 @@ -MeshCentral - Login
{{{title}}}
{{{title2}}}
\ No newline at end of file +MeshCentral - Login
{{{title}}}
{{{title2}}}
\ No newline at end of file diff --git a/views/login-mobile.handlebars b/views/login-mobile.handlebars index 67951822..1a6efa7d 100644 --- a/views/login-mobile.handlebars +++ b/views/login-mobile.handlebars @@ -156,9 +156,15 @@ + + + + +
+
@@ -265,6 +271,7 @@ if (passRequirements != '') { passRequirements = JSON.parse(decodeURIComponent(passRequirements)); } else { passRequirements = {}; } var passRequirementsEx = ((passRequirements.min != null) || (passRequirements.max != null) || (passRequirements.upper != null) || (passRequirements.lower != null) || (passRequirements.numeric != null) || (passRequirements.nonalpha != null)); var hardwareKeyChallenge = decodeURIComponent('{{{hkey}}}'); + var publicKeyCredentialRequestOptions = null; var currentpanel = 0; // Display the right server message @@ -322,41 +329,15 @@ if (loginMode == '4') { try { if (hardwareKeyChallenge.length > 0) { hardwareKeyChallenge = JSON.parse(hardwareKeyChallenge); } else { hardwareKeyChallenge = null; } } catch (ex) { hardwareKeyChallenge = null } - if ((hardwareKeyChallenge != null) && (hardwareKeyChallenge.type == 'webAuthn')) { - hardwareKeyChallenge.challenge = Uint8Array.from(atob(hardwareKeyChallenge.challenge), function (c) { return c.charCodeAt(0) }).buffer; - - var publicKeyCredentialRequestOptions = { challenge: hardwareKeyChallenge.challenge, allowCredentials: [], timeout: hardwareKeyChallenge.timeout } - for (var i = 0; i < hardwareKeyChallenge.keyIds.length; i++) { - publicKeyCredentialRequestOptions.allowCredentials.push( - { id: Uint8Array.from(atob(hardwareKeyChallenge.keyIds[i]), function (c) { return c.charCodeAt(0) }), type: 'public-key', transports: ['usb', 'ble', 'nfc'], } - ); - } - - // New WebAuthn hardware keys - navigator.credentials.get({ publicKey: publicKeyCredentialRequestOptions }).then( - function (rawAssertion) { - var assertion = { - id: btoa(String.fromCharCode.apply(null, new Uint8Array(rawAssertion.rawId))), - clientDataJSON: btoa(String.fromCharCode.apply(null, new Uint8Array(rawAssertion.response.clientDataJSON))), - userHandle: btoa(String.fromCharCode.apply(null, new Uint8Array(rawAssertion.response.userHandle))), - signature: btoa(String.fromCharCode.apply(null, new Uint8Array(rawAssertion.response.signature))), - authenticatorData: btoa(String.fromCharCode.apply(null, new Uint8Array(rawAssertion.response.authenticatorData))), - }; - Q('hwtokenInput').value = JSON.stringify(assertion); - QE('tokenOkButton', true); - Q('tokenOkButton').click(); - }, - function (error) { console.log('credentials-get error', error); } - ); - } + QV('securityKeyButton', (hardwareKeyChallenge != null) && (hardwareKeyChallenge.type == 'webAuthn')); } if (loginMode == '5') { try { if (hardwareKeyChallenge.length > 0) { hardwareKeyChallenge = JSON.parse(hardwareKeyChallenge); } else { hardwareKeyChallenge = null; } } catch (ex) { hardwareKeyChallenge = null } if ((hardwareKeyChallenge != null) && (hardwareKeyChallenge.type == 'webAuthn')) { - hardwareKeyChallenge.challenge = Uint8Array.from(atob(hardwareKeyChallenge.challenge), function (c) { return c.charCodeAt(0) }).buffer; + if (typeof hardwareKeyChallenge.challenge == 'string') { hardwareKeyChallenge.challenge = Uint8Array.from(atob(hardwareKeyChallenge.challenge), function (c) { return c.charCodeAt(0) }).buffer; } - var publicKeyCredentialRequestOptions = { challenge: hardwareKeyChallenge.challenge, allowCredentials: [], timeout: hardwareKeyChallenge.timeout } + publicKeyCredentialRequestOptions = { challenge: hardwareKeyChallenge.challenge, allowCredentials: [], timeout: hardwareKeyChallenge.timeout } for (var i = 0; i < hardwareKeyChallenge.keyIds.length; i++) { publicKeyCredentialRequestOptions.allowCredentials.push( { id: Uint8Array.from(atob(hardwareKeyChallenge.keyIds[i]), function (c) { return c.charCodeAt(0) }), type: 'public-key', transports: ['usb', 'ble', 'nfc'], } @@ -382,6 +363,37 @@ } } } + + // Use a hardware security key + function useSecurityKey() { + if ((hardwareKeyChallenge != null) && (hardwareKeyChallenge.type == 'webAuthn')) { + if (typeof hardwareKeyChallenge.challenge == 'string') { hardwareKeyChallenge.challenge = Uint8Array.from(atob(hardwareKeyChallenge.challenge), function (c) { return c.charCodeAt(0) }).buffer; } + + publicKeyCredentialRequestOptions = { challenge: hardwareKeyChallenge.challenge, allowCredentials: [], timeout: hardwareKeyChallenge.timeout } + for (var i = 0; i < hardwareKeyChallenge.keyIds.length; i++) { + publicKeyCredentialRequestOptions.allowCredentials.push( + { id: Uint8Array.from(atob(hardwareKeyChallenge.keyIds[i]), function (c) { return c.charCodeAt(0) }), type: 'public-key', transports: ['usb', 'ble', 'nfc'], } + ); + } + + // New WebAuthn hardware keys + navigator.credentials.get({ publicKey: publicKeyCredentialRequestOptions }).then( + function (rawAssertion) { + var assertion = { + id: btoa(String.fromCharCode.apply(null, new Uint8Array(rawAssertion.rawId))), + clientDataJSON: btoa(String.fromCharCode.apply(null, new Uint8Array(rawAssertion.response.clientDataJSON))), + userHandle: btoa(String.fromCharCode.apply(null, new Uint8Array(rawAssertion.response.userHandle))), + signature: btoa(String.fromCharCode.apply(null, new Uint8Array(rawAssertion.response.signature))), + authenticatorData: btoa(String.fromCharCode.apply(null, new Uint8Array(rawAssertion.response.authenticatorData))), + }; + Q('hwtokenInput').value = JSON.stringify(assertion); + QE('tokenOkButton', true); + Q('tokenOkButton').click(); + }, + function (error) { console.log('credentials-get error', error); } + ); + } + } function showPassHint() { if (passRequirements.hint === true) { messagebox("Password Hint", passhint); } diff --git a/views/login.handlebars b/views/login.handlebars index 54adc728..9ededb02 100644 --- a/views/login.handlebars +++ b/views/login.handlebars @@ -147,13 +147,19 @@ Login token: - +
+ + + + +
+
@@ -331,39 +337,13 @@ if (loginMode == '4') { try { if (hardwareKeyChallenge.length > 0) { hardwareKeyChallenge = JSON.parse(hardwareKeyChallenge); } else { hardwareKeyChallenge = null; } } catch (ex) { hardwareKeyChallenge = null } - if ((hardwareKeyChallenge != null) && (hardwareKeyChallenge.type == 'webAuthn')) { - hardwareKeyChallenge.challenge = Uint8Array.from(atob(hardwareKeyChallenge.challenge), function (c) { return c.charCodeAt(0) }).buffer; - - publicKeyCredentialRequestOptions = { challenge: hardwareKeyChallenge.challenge, allowCredentials: [], timeout: hardwareKeyChallenge.timeout } - for (var i = 0; i < hardwareKeyChallenge.keyIds.length; i++) { - publicKeyCredentialRequestOptions.allowCredentials.push( - { id: Uint8Array.from(atob(hardwareKeyChallenge.keyIds[i]), function (c) { return c.charCodeAt(0) }), type: 'public-key', transports: ['usb', 'ble', 'nfc'], } - ); - } - - // New WebAuthn hardware keys - navigator.credentials.get({ publicKey: publicKeyCredentialRequestOptions }).then( - function (rawAssertion) { - var assertion = { - id: btoa(String.fromCharCode.apply(null, new Uint8Array(rawAssertion.rawId))), - clientDataJSON: btoa(String.fromCharCode.apply(null, new Uint8Array(rawAssertion.response.clientDataJSON))), - userHandle: btoa(String.fromCharCode.apply(null, new Uint8Array(rawAssertion.response.userHandle))), - signature: btoa(String.fromCharCode.apply(null, new Uint8Array(rawAssertion.response.signature))), - authenticatorData: btoa(String.fromCharCode.apply(null, new Uint8Array(rawAssertion.response.authenticatorData))), - }; - Q('hwtokenInput').value = JSON.stringify(assertion); - QE('tokenOkButton', true); - Q('tokenOkButton').click(); - }, - function (error) { console.log('credentials-get error', error); } - ); - } + QV('securityKeyButton', (hardwareKeyChallenge != null) && (hardwareKeyChallenge.type == 'webAuthn')); } if (loginMode == '5') { try { if (hardwareKeyChallenge.length > 0) { hardwareKeyChallenge = JSON.parse(hardwareKeyChallenge); } else { hardwareKeyChallenge = null; } } catch (ex) { hardwareKeyChallenge = null } if ((hardwareKeyChallenge != null) && (hardwareKeyChallenge.type == 'webAuthn')) { - hardwareKeyChallenge.challenge = Uint8Array.from(atob(hardwareKeyChallenge.challenge), function (c) { return c.charCodeAt(0) }).buffer; + if (typeof hardwareKeyChallenge.challenge == 'string') { hardwareKeyChallenge.challenge = Uint8Array.from(atob(hardwareKeyChallenge.challenge), function (c) { return c.charCodeAt(0) }).buffer; } publicKeyCredentialRequestOptions = { challenge: hardwareKeyChallenge.challenge, allowCredentials: [], timeout: hardwareKeyChallenge.timeout } for (var i = 0; i < hardwareKeyChallenge.keyIds.length; i++) { @@ -395,6 +375,37 @@ userInterfaceSelectMenu(); } + // Use a hardware security key + function useSecurityKey() { + if ((hardwareKeyChallenge != null) && (hardwareKeyChallenge.type == 'webAuthn')) { + if (typeof hardwareKeyChallenge.challenge == 'string') { hardwareKeyChallenge.challenge = Uint8Array.from(atob(hardwareKeyChallenge.challenge), function (c) { return c.charCodeAt(0) }).buffer; } + + publicKeyCredentialRequestOptions = { challenge: hardwareKeyChallenge.challenge, allowCredentials: [], timeout: hardwareKeyChallenge.timeout } + for (var i = 0; i < hardwareKeyChallenge.keyIds.length; i++) { + publicKeyCredentialRequestOptions.allowCredentials.push( + { id: Uint8Array.from(atob(hardwareKeyChallenge.keyIds[i]), function (c) { return c.charCodeAt(0) }), type: 'public-key', transports: ['usb', 'ble', 'nfc'], } + ); + } + + // New WebAuthn hardware keys + navigator.credentials.get({ publicKey: publicKeyCredentialRequestOptions }).then( + function (rawAssertion) { + var assertion = { + id: btoa(String.fromCharCode.apply(null, new Uint8Array(rawAssertion.rawId))), + clientDataJSON: btoa(String.fromCharCode.apply(null, new Uint8Array(rawAssertion.response.clientDataJSON))), + userHandle: btoa(String.fromCharCode.apply(null, new Uint8Array(rawAssertion.response.userHandle))), + signature: btoa(String.fromCharCode.apply(null, new Uint8Array(rawAssertion.response.signature))), + authenticatorData: btoa(String.fromCharCode.apply(null, new Uint8Array(rawAssertion.response.authenticatorData))), + }; + Q('hwtokenInput').value = JSON.stringify(assertion); + QE('tokenOkButton', true); + Q('tokenOkButton').click(); + }, + function (error) { console.log('credentials-get error', error); } + ); + } + } + function showPassHint(e) { messagebox("Password Hint", passhint); haltEvent(e); diff --git a/views/translations/default-min_fr.handlebars b/views/translations/default-min_fr.handlebars index e504e224..38a0ab0c 100644 --- a/views/translations/default-min_fr.handlebars +++ b/views/translations/default-min_fr.handlebars @@ -864,7 +864,7 @@ if (message.keys && message.keys.length > 0) { for (var i in message.keys) { var key = message.keys[i], type = (key.type == 2)?'OTP':'WebAuthn'; - x += start + '' + key.name + '' + end; + x += start + '' + key.name + '' + end; } } else { x += start + '' + "No Keys Configured" + end; diff --git a/views/translations/default_fr.handlebars b/views/translations/default_fr.handlebars index 668634bb..68f0f976 100644 --- a/views/translations/default_fr.handlebars +++ b/views/translations/default_fr.handlebars @@ -1886,7 +1886,7 @@ if (message.keys && message.keys.length > 0) { for (var i in message.keys) { var key = message.keys[i], type = (key.type == 2)?'OTP':'WebAuthn'; - x += start + '' + key.name + '' + end; + x += start + '' + key.name + '' + end; } } else { x += start + '' + "No Keys Configured" + end; diff --git a/views/translations/login-min_fr.handlebars b/views/translations/login-min_fr.handlebars index eb62b24b..8b423238 100644 --- a/views/translations/login-min_fr.handlebars +++ b/views/translations/login-min_fr.handlebars @@ -1 +1 @@ -{{{title}}} - Login
{{{title}}}
{{{title2}}}

Bienvenue


\ No newline at end of file +{{{title}}} - Login
{{{title}}}
{{{title2}}}

Bienvenue


\ No newline at end of file diff --git a/views/translations/login-mobile-min_fr.handlebars b/views/translations/login-mobile-min_fr.handlebars index cd99da61..71ff755e 100644 --- a/views/translations/login-mobile-min_fr.handlebars +++ b/views/translations/login-mobile-min_fr.handlebars @@ -1 +1 @@ -MeshCentral - Login
{{{title}}}
{{{title2}}}
\ No newline at end of file +MeshCentral - Login
{{{title}}}
{{{title2}}}
\ No newline at end of file diff --git a/views/translations/login-mobile_fr.handlebars b/views/translations/login-mobile_fr.handlebars index 35755e83..5e5e94cb 100644 --- a/views/translations/login-mobile_fr.handlebars +++ b/views/translations/login-mobile_fr.handlebars @@ -154,9 +154,15 @@ + + + + +
+
@@ -263,6 +269,7 @@ if (passRequirements != '') { passRequirements = JSON.parse(decodeURIComponent(passRequirements)); } else { passRequirements = {}; } var passRequirementsEx = ((passRequirements.min != null) || (passRequirements.max != null) || (passRequirements.upper != null) || (passRequirements.lower != null) || (passRequirements.numeric != null) || (passRequirements.nonalpha != null)); var hardwareKeyChallenge = decodeURIComponent('{{{hkey}}}'); + var publicKeyCredentialRequestOptions = null; var currentpanel = 0; // Display the right server message @@ -320,41 +327,15 @@ if (loginMode == '4') { try { if (hardwareKeyChallenge.length > 0) { hardwareKeyChallenge = JSON.parse(hardwareKeyChallenge); } else { hardwareKeyChallenge = null; } } catch (ex) { hardwareKeyChallenge = null } - if ((hardwareKeyChallenge != null) && (hardwareKeyChallenge.type == 'webAuthn')) { - hardwareKeyChallenge.challenge = Uint8Array.from(atob(hardwareKeyChallenge.challenge), function (c) { return c.charCodeAt(0) }).buffer; - - var publicKeyCredentialRequestOptions = { challenge: hardwareKeyChallenge.challenge, allowCredentials: [], timeout: hardwareKeyChallenge.timeout } - for (var i = 0; i < hardwareKeyChallenge.keyIds.length; i++) { - publicKeyCredentialRequestOptions.allowCredentials.push( - { id: Uint8Array.from(atob(hardwareKeyChallenge.keyIds[i]), function (c) { return c.charCodeAt(0) }), type: 'public-key', transports: ['usb', 'ble', 'nfc'], } - ); - } - - // New WebAuthn hardware keys - navigator.credentials.get({ publicKey: publicKeyCredentialRequestOptions }).then( - function (rawAssertion) { - var assertion = { - id: btoa(String.fromCharCode.apply(null, new Uint8Array(rawAssertion.rawId))), - clientDataJSON: btoa(String.fromCharCode.apply(null, new Uint8Array(rawAssertion.response.clientDataJSON))), - userHandle: btoa(String.fromCharCode.apply(null, new Uint8Array(rawAssertion.response.userHandle))), - signature: btoa(String.fromCharCode.apply(null, new Uint8Array(rawAssertion.response.signature))), - authenticatorData: btoa(String.fromCharCode.apply(null, new Uint8Array(rawAssertion.response.authenticatorData))), - }; - Q('hwtokenInput').value = JSON.stringify(assertion); - QE('tokenOkButton', true); - Q('tokenOkButton').click(); - }, - function (error) { console.log('credentials-get error', error); } - ); - } + QV('securityKeyButton', (hardwareKeyChallenge != null) && (hardwareKeyChallenge.type == 'webAuthn')); } if (loginMode == '5') { try { if (hardwareKeyChallenge.length > 0) { hardwareKeyChallenge = JSON.parse(hardwareKeyChallenge); } else { hardwareKeyChallenge = null; } } catch (ex) { hardwareKeyChallenge = null } if ((hardwareKeyChallenge != null) && (hardwareKeyChallenge.type == 'webAuthn')) { - hardwareKeyChallenge.challenge = Uint8Array.from(atob(hardwareKeyChallenge.challenge), function (c) { return c.charCodeAt(0) }).buffer; + if (typeof hardwareKeyChallenge.challenge == 'string') { hardwareKeyChallenge.challenge = Uint8Array.from(atob(hardwareKeyChallenge.challenge), function (c) { return c.charCodeAt(0) }).buffer; } - var publicKeyCredentialRequestOptions = { challenge: hardwareKeyChallenge.challenge, allowCredentials: [], timeout: hardwareKeyChallenge.timeout } + publicKeyCredentialRequestOptions = { challenge: hardwareKeyChallenge.challenge, allowCredentials: [], timeout: hardwareKeyChallenge.timeout } for (var i = 0; i < hardwareKeyChallenge.keyIds.length; i++) { publicKeyCredentialRequestOptions.allowCredentials.push( { id: Uint8Array.from(atob(hardwareKeyChallenge.keyIds[i]), function (c) { return c.charCodeAt(0) }), type: 'public-key', transports: ['usb', 'ble', 'nfc'], } @@ -380,6 +361,37 @@ } } } + + // Use a hardware security key + function useSecurityKey() { + if ((hardwareKeyChallenge != null) && (hardwareKeyChallenge.type == 'webAuthn')) { + if (typeof hardwareKeyChallenge.challenge == 'string') { hardwareKeyChallenge.challenge = Uint8Array.from(atob(hardwareKeyChallenge.challenge), function (c) { return c.charCodeAt(0) }).buffer; } + + publicKeyCredentialRequestOptions = { challenge: hardwareKeyChallenge.challenge, allowCredentials: [], timeout: hardwareKeyChallenge.timeout } + for (var i = 0; i < hardwareKeyChallenge.keyIds.length; i++) { + publicKeyCredentialRequestOptions.allowCredentials.push( + { id: Uint8Array.from(atob(hardwareKeyChallenge.keyIds[i]), function (c) { return c.charCodeAt(0) }), type: 'public-key', transports: ['usb', 'ble', 'nfc'], } + ); + } + + // New WebAuthn hardware keys + navigator.credentials.get({ publicKey: publicKeyCredentialRequestOptions }).then( + function (rawAssertion) { + var assertion = { + id: btoa(String.fromCharCode.apply(null, new Uint8Array(rawAssertion.rawId))), + clientDataJSON: btoa(String.fromCharCode.apply(null, new Uint8Array(rawAssertion.response.clientDataJSON))), + userHandle: btoa(String.fromCharCode.apply(null, new Uint8Array(rawAssertion.response.userHandle))), + signature: btoa(String.fromCharCode.apply(null, new Uint8Array(rawAssertion.response.signature))), + authenticatorData: btoa(String.fromCharCode.apply(null, new Uint8Array(rawAssertion.response.authenticatorData))), + }; + Q('hwtokenInput').value = JSON.stringify(assertion); + QE('tokenOkButton', true); + Q('tokenOkButton').click(); + }, + function (error) { console.log('credentials-get error', error); } + ); + } + } function showPassHint() { if (passRequirements.hint === true) { messagebox("Password Hint", passhint); } diff --git a/views/translations/login_fr.handlebars b/views/translations/login_fr.handlebars index 16d9f8ed..041a6604 100644 --- a/views/translations/login_fr.handlebars +++ b/views/translations/login_fr.handlebars @@ -145,13 +145,19 @@ Login token: - +
+ + + + +
+
@@ -329,39 +335,13 @@ if (loginMode == '4') { try { if (hardwareKeyChallenge.length > 0) { hardwareKeyChallenge = JSON.parse(hardwareKeyChallenge); } else { hardwareKeyChallenge = null; } } catch (ex) { hardwareKeyChallenge = null } - if ((hardwareKeyChallenge != null) && (hardwareKeyChallenge.type == 'webAuthn')) { - hardwareKeyChallenge.challenge = Uint8Array.from(atob(hardwareKeyChallenge.challenge), function (c) { return c.charCodeAt(0) }).buffer; - - publicKeyCredentialRequestOptions = { challenge: hardwareKeyChallenge.challenge, allowCredentials: [], timeout: hardwareKeyChallenge.timeout } - for (var i = 0; i < hardwareKeyChallenge.keyIds.length; i++) { - publicKeyCredentialRequestOptions.allowCredentials.push( - { id: Uint8Array.from(atob(hardwareKeyChallenge.keyIds[i]), function (c) { return c.charCodeAt(0) }), type: 'public-key', transports: ['usb', 'ble', 'nfc'], } - ); - } - - // New WebAuthn hardware keys - navigator.credentials.get({ publicKey: publicKeyCredentialRequestOptions }).then( - function (rawAssertion) { - var assertion = { - id: btoa(String.fromCharCode.apply(null, new Uint8Array(rawAssertion.rawId))), - clientDataJSON: btoa(String.fromCharCode.apply(null, new Uint8Array(rawAssertion.response.clientDataJSON))), - userHandle: btoa(String.fromCharCode.apply(null, new Uint8Array(rawAssertion.response.userHandle))), - signature: btoa(String.fromCharCode.apply(null, new Uint8Array(rawAssertion.response.signature))), - authenticatorData: btoa(String.fromCharCode.apply(null, new Uint8Array(rawAssertion.response.authenticatorData))), - }; - Q('hwtokenInput').value = JSON.stringify(assertion); - QE('tokenOkButton', true); - Q('tokenOkButton').click(); - }, - function (error) { console.log('credentials-get error', error); } - ); - } + QV('securityKeyButton', (hardwareKeyChallenge != null) && (hardwareKeyChallenge.type == 'webAuthn')); } if (loginMode == '5') { try { if (hardwareKeyChallenge.length > 0) { hardwareKeyChallenge = JSON.parse(hardwareKeyChallenge); } else { hardwareKeyChallenge = null; } } catch (ex) { hardwareKeyChallenge = null } if ((hardwareKeyChallenge != null) && (hardwareKeyChallenge.type == 'webAuthn')) { - hardwareKeyChallenge.challenge = Uint8Array.from(atob(hardwareKeyChallenge.challenge), function (c) { return c.charCodeAt(0) }).buffer; + if (typeof hardwareKeyChallenge.challenge == 'string') { hardwareKeyChallenge.challenge = Uint8Array.from(atob(hardwareKeyChallenge.challenge), function (c) { return c.charCodeAt(0) }).buffer; } publicKeyCredentialRequestOptions = { challenge: hardwareKeyChallenge.challenge, allowCredentials: [], timeout: hardwareKeyChallenge.timeout } for (var i = 0; i < hardwareKeyChallenge.keyIds.length; i++) { @@ -393,6 +373,37 @@ userInterfaceSelectMenu(); } + // Use a hardware security key + function useSecurityKey() { + if ((hardwareKeyChallenge != null) && (hardwareKeyChallenge.type == 'webAuthn')) { + if (typeof hardwareKeyChallenge.challenge == 'string') { hardwareKeyChallenge.challenge = Uint8Array.from(atob(hardwareKeyChallenge.challenge), function (c) { return c.charCodeAt(0) }).buffer; } + + publicKeyCredentialRequestOptions = { challenge: hardwareKeyChallenge.challenge, allowCredentials: [], timeout: hardwareKeyChallenge.timeout } + for (var i = 0; i < hardwareKeyChallenge.keyIds.length; i++) { + publicKeyCredentialRequestOptions.allowCredentials.push( + { id: Uint8Array.from(atob(hardwareKeyChallenge.keyIds[i]), function (c) { return c.charCodeAt(0) }), type: 'public-key', transports: ['usb', 'ble', 'nfc'], } + ); + } + + // New WebAuthn hardware keys + navigator.credentials.get({ publicKey: publicKeyCredentialRequestOptions }).then( + function (rawAssertion) { + var assertion = { + id: btoa(String.fromCharCode.apply(null, new Uint8Array(rawAssertion.rawId))), + clientDataJSON: btoa(String.fromCharCode.apply(null, new Uint8Array(rawAssertion.response.clientDataJSON))), + userHandle: btoa(String.fromCharCode.apply(null, new Uint8Array(rawAssertion.response.userHandle))), + signature: btoa(String.fromCharCode.apply(null, new Uint8Array(rawAssertion.response.signature))), + authenticatorData: btoa(String.fromCharCode.apply(null, new Uint8Array(rawAssertion.response.authenticatorData))), + }; + Q('hwtokenInput').value = JSON.stringify(assertion); + QE('tokenOkButton', true); + Q('tokenOkButton').click(); + }, + function (error) { console.log('credentials-get error', error); } + ); + } + } + function showPassHint(e) { messagebox("Password Hint", passhint); haltEvent(e); diff --git a/webserver.js b/webserver.js index be8a3679..92894bfd 100644 --- a/webserver.js +++ b/webserver.js @@ -516,6 +516,18 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) { if ((req != null) && (req.ip != null) && (domain.passwordrequirements != null) && (domain.passwordrequirements.skip2factor != null)) { for (var i in domain.passwordrequirements.skip2factor) { if (require('ipcheck').match(req.ip, domain.passwordrequirements.skip2factor[i]) === true) return false; } } + + // Check if a 2nd factor cookie is present + if (typeof req.headers.cookie == 'string') { + const cookies = req.headers.cookie.split('; '); + for (var i in cookies) { + if (cookies[i].startsWith('twofactor=')) { + var twoFactorCookie = obj.parent.decodeCookie(decodeURIComponent(cookies[i].substring(10)), obj.parent.loginCookieEncryptionKey, (30 * 24 * 60)); // 30 day timeout + if ((twoFactorCookie != null) && ((twoFactorCookie.ip == null) || (twoFactorCookie.ip === cleanRemoteAddr(req.ip))) && (twoFactorCookie.userid == user._id)) { return false; } + } + } + } + // Check if a 2nd factor is present return ((parent.config.settings.no2factorauth !== true) && ((user.otpsecret != null) || ((user.otphkeys != null) && (user.otphkeys.length > 0)))); } @@ -686,16 +698,22 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) { if (direct === true) { handleRootRequestEx(req, res, domain); } else { res.redirect(domain.url + getQueryPortion(req)); } }, randomWaitTime); } else { - // Login succesful - parent.debug('web', 'handleLoginRequest: succesful 2FA login'); + // Check if we need to remember this device + if (req.body.remembertoken === 'on') { + const twoFactorCookie = obj.parent.encodeCookie({ userid: user._id /*, ip: cleanRemoteAddr(req.ip)*/ }, obj.parent.loginCookieEncryptionKey); + res.cookie('twofactor', twoFactorCookie, { maxAge: (30 * 24 * 60 * 60 * 1000), httpOnly: true, sameSite: 'strict', secure: true }); + } + + // Login successful + parent.debug('web', 'handleLoginRequest: successful 2FA login'); completeLoginRequest(req, res, domain, user, userid, xusername, xpassword, direct); } }); return; } - // Login succesful - parent.debug('web', 'handleLoginRequest: succesful login'); + // Login successful + parent.debug('web', 'handleLoginRequest: successful login'); completeLoginRequest(req, res, domain, user, userid, xusername, xpassword, direct); } else { // Login failed, wait a random delay @@ -959,7 +977,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) { if (obj.db.changeStream) { event.noact = 1; } // If DB change stream is active, don't use this event to change the user. Another event will come. obj.parent.DispatchEvent(['*', 'server-users', user._id], obj, event); - // Login succesful + // Login successful parent.debug('web', 'handleResetPasswordRequest: success'); req.session.userid = userid; req.session.domainid = domain.id; @@ -2570,7 +2588,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) { var amtpassword = ((mesh.amt.password == '') ? getRandomAmtPassword() : mesh.amt.password); if (checkAmtPassword(amtpassword) == false) { ws.send(JSON.stringify({ errorText: 'Invalid Intel AMT password' })); ws.close(); return; } // Invalid Intel AMT password, this should never happen. - // Save some state, if activation is succesful, we need this to add the device + // Save some state, if activation is successful, we need this to add the device ws.xxstate = { uuid: cmd.uuid, realm: cmd.realm, tag: cmd.tag, name: cmd.name, hostname: cmd.hostname, pass: amtpassword, flags: activationMode, ver: cmd.ver, sku: cmd.sku }; // Flags: 2 = CCM, 4 = ACM if (activationMode == 4) {