Connect to your home or office devices from anywhere in the world using MeshCentral, the real time, open source remote monitoring and management web site. You will need to download and install a management agent on your computers. Once installed, computers will show up in the "My Devices" section of this web site and you will be able to monitor them and take control of them.
X
\ No newline at end of file
+{{{title}}} - Login
{{{title}}}
{{{title2}}}
♦
Welcome
Connect to your home or office devices from anywhere in the world using MeshCentral, the real time, open source remote monitoring and management web site. You will need to download and install a management agent on your computers. Once installed, computers will show up in the "My Devices" section of this web site and you will be able to monitor them and take control of them.
X
\ 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}}}
X
\ No newline at end of file
+MeshCentral - Login
{{{title}}}
{{{title2}}}
X
\ 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 + '
Connect to your home or office devices from anywhere in the world using MeshCentral, the real time, open source remote monitoring and management web site. You will need to download and install a management agent on your computers. Once installed, computers will show up in the "My Devices" section of this web site and you will be able to monitor them and take control of them.
X
\ No newline at end of file
+{{{title}}} - Login
{{{title}}}
{{{title2}}}
♦
Bienvenue
Connect to your home or office devices from anywhere in the world using MeshCentral, the real time, open source remote monitoring and management web site. You will need to download and install a management agent on your computers. Once installed, computers will show up in the "My Devices" section of this web site and you will be able to monitor them and take control of them.
X
\ 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}}}
X
\ No newline at end of file
+MeshCentral - Login
{{{title}}}
{{{title2}}}
X
\ 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) {