mirror of
https://github.com/Ylianst/MeshCentral.git
synced 2024-11-22 12:52:50 +03:00
Complete replacement of the WebAuthn 2 factor support.
This commit is contained in:
parent
36ac790452
commit
e00cbf33d3
@ -130,6 +130,7 @@
|
||||
<Compile Include="public\scripts\filesaver.1.1.20151003.js" />
|
||||
<Compile Include="public\scripts\meshcentral.js" />
|
||||
<Compile Include="redirserver.js" />
|
||||
<Compile Include="webauthn.js" />
|
||||
<Compile Include="webserver.js" />
|
||||
<Compile Include="winservice.js" />
|
||||
<Content Include="agents\compressModules.bat" />
|
||||
|
263
Webauthn.js
Normal file
263
Webauthn.js
Normal file
@ -0,0 +1,263 @@
|
||||
/**
|
||||
* @description MeshCentral WebAuthn module
|
||||
* @version v0.0.1
|
||||
*/
|
||||
|
||||
//
|
||||
|
||||
'use strict'
|
||||
|
||||
const crypto = require('crypto')
|
||||
const cbor = require('cbor')
|
||||
//const iso_3166_1 = require('iso-3166-1')
|
||||
//const Certificate = null; //require('@fidm/x509')
|
||||
|
||||
module.exports.CreateWebAuthnModule = function () {
|
||||
var obj = {};
|
||||
|
||||
obj.generateRegistrationChallenge = function (rpName, user) {
|
||||
return {
|
||||
rp: { name: rpName },
|
||||
user: user,
|
||||
challenge: crypto.randomBytes(64).toString('base64'),
|
||||
pubKeyCredParams: [{ type: 'public-key', alg: -7 }],
|
||||
timeout: 60000,
|
||||
attestation: 'none'
|
||||
}
|
||||
}
|
||||
|
||||
obj.verifyAuthenticatorAttestationResponse = function (webauthnResponse) {
|
||||
const attestationBuffer = Buffer.from(webauthnResponse.attestationObject, 'base64');
|
||||
const ctapMakeCredResp = cbor.decodeAllSync(attestationBuffer)[0];
|
||||
const authrDataStruct = parseMakeCredAuthData(ctapMakeCredResp.authData);
|
||||
//console.log('***CTAP_RESPONSE', ctapMakeCredResp)
|
||||
//console.log('***AUTHR_DATA_STRUCT', authrDataStruct)
|
||||
|
||||
const response = { 'verified': false };
|
||||
|
||||
if (ctapMakeCredResp.fmt === 'none') {
|
||||
if (!(authrDataStruct.flags & 0x01)) { throw new Error('User was NOT presented during authentication!'); } // U2F_USER_PRESENTED
|
||||
|
||||
const publicKey = COSEECDHAtoPKCS(authrDataStruct.COSEPublicKey)
|
||||
response.verified = true;
|
||||
|
||||
if (response.verified) {
|
||||
response.authrInfo = {
|
||||
fmt: 'none',
|
||||
publicKey: ASN1toPEM(publicKey),
|
||||
counter: authrDataStruct.counter,
|
||||
keyId: authrDataStruct.credID.toString('base64')
|
||||
}
|
||||
}
|
||||
}
|
||||
/*
|
||||
else if (ctapMakeCredResp.fmt === 'fido-u2f') {
|
||||
if (!(authrDataStruct.flags & 0x01)) // U2F_USER_PRESENTED
|
||||
throw new Error('User was NOT presented during authentication!');
|
||||
|
||||
const clientDataHash = hash(webauthnResponse.clientDataJSON)
|
||||
const reservedByte = Buffer.from([0x00]);
|
||||
const publicKey = COSEECDHAtoPKCS(authrDataStruct.COSEPublicKey)
|
||||
const signatureBase = Buffer.concat([reservedByte, authrDataStruct.rpIdHash, clientDataHash, authrDataStruct.credID, publicKey]);
|
||||
|
||||
const PEMCertificate = ASN1toPEM(ctapMakeCredResp.attStmt.x5c[0]);
|
||||
const signature = ctapMakeCredResp.attStmt.sig;
|
||||
|
||||
response.verified = verifySignature(signature, signatureBase, PEMCertificate)
|
||||
|
||||
if (response.verified) {
|
||||
response.authrInfo = {
|
||||
fmt: 'fido-u2f',
|
||||
publicKey: ASN1toPEM(publicKey),
|
||||
counter: authrDataStruct.counter,
|
||||
keyId: authrDataStruct.credID.toString('base64')
|
||||
}
|
||||
}
|
||||
} else if (ctapMakeCredResp.fmt === 'packed' && ctapMakeCredResp.attStmt.hasOwnProperty('x5c')) {
|
||||
if (!(authrDataStruct.flags & 0x01)) // U2F_USER_PRESENTED
|
||||
throw new Error('User was NOT presented durring authentication!');
|
||||
|
||||
const clientDataHash = hash(webauthnResponse.clientDataJSON)
|
||||
const publicKey = COSEECDHAtoPKCS(authrDataStruct.COSEPublicKey)
|
||||
const signatureBase = Buffer.concat([ctapMakeCredResp.authData, clientDataHash]);
|
||||
|
||||
const PEMCertificate = ASN1toPEM(ctapMakeCredResp.attStmt.x5c[0]);
|
||||
const signature = ctapMakeCredResp.attStmt.sig;
|
||||
|
||||
const pem = Certificate.fromPEM(PEMCertificate);
|
||||
|
||||
// Getting requirements from https://www.w3.org/TR/webauthn/#packed-attestation
|
||||
const aaguid_ext = pem.getExtension('1.3.6.1.4.1.45724.1.1.4')
|
||||
|
||||
response.verified = // Verify that sig is a valid signature over the concatenation of authenticatorData
|
||||
// and clientDataHash using the attestation public key in attestnCert with the algorithm specified in alg.
|
||||
verifySignature(signature, signatureBase, PEMCertificate) &&
|
||||
// version must be 3 (which is indicated by an ASN.1 INTEGER with value 2)
|
||||
pem.version == 3 &&
|
||||
// ISO 3166 valid country
|
||||
typeof iso_3166_1.whereAlpha2(pem.subject.countryName) !== 'undefined' &&
|
||||
// Legal name of the Authenticator vendor (UTF8String)
|
||||
pem.subject.organizationName &&
|
||||
// Literal string “Authenticator Attestation” (UTF8String)
|
||||
pem.subject.organizationalUnitName === 'Authenticator Attestation' &&
|
||||
// A UTF8String of the vendor’s choosing
|
||||
pem.subject.commonName &&
|
||||
// The Basic Constraints extension MUST have the CA component set to false
|
||||
!pem.extensions.isCA &&
|
||||
// If attestnCert contains an extension with OID 1.3.6.1.4.1.45724.1.1.4 (id-fido-gen-ce-aaguid)
|
||||
// verify that the value of this extension matches the aaguid in authenticatorData.
|
||||
// The extension MUST NOT be marked as critical.
|
||||
(aaguid_ext != null ?
|
||||
(authrDataStruct.hasOwnProperty('aaguid') ?
|
||||
!aaguid_ext.critical && aaguid_ext.value.slice(2).equals(authrDataStruct.aaguid) : false)
|
||||
: true);
|
||||
|
||||
if (response.verified) {
|
||||
response.authrInfo = {
|
||||
fmt: 'fido-u2f',
|
||||
publicKey: publicKey,
|
||||
counter: authrDataStruct.counter,
|
||||
keyId: authrDataStruct.credID.toString('base64')
|
||||
}
|
||||
}
|
||||
|
||||
// Self signed
|
||||
} else if (ctapMakeCredResp.fmt === 'packed') {
|
||||
if (!(authrDataStruct.flags & 0x01)) // U2F_USER_PRESENTED
|
||||
throw new Error('User was NOT presented durring authentication!');
|
||||
|
||||
const clientDataHash = hash(webauthnResponse.clientDataJSON)
|
||||
const publicKey = COSEECDHAtoPKCS(authrDataStruct.COSEPublicKey)
|
||||
const signatureBase = Buffer.concat([ctapMakeCredResp.authData, clientDataHash]);
|
||||
const PEMCertificate = ASN1toPEM(publicKey);
|
||||
|
||||
const { attStmt: { sig: signature, alg } } = ctapMakeCredResp
|
||||
|
||||
response.verified = // Verify that sig is a valid signature over the concatenation of authenticatorData
|
||||
// and clientDataHash using the attestation public key in attestnCert with the algorithm specified in alg.
|
||||
verifySignature(signature, signatureBase, PEMCertificate) && alg === -7
|
||||
|
||||
if (response.verified) {
|
||||
response.authrInfo = {
|
||||
fmt: 'fido-u2f',
|
||||
publicKey: ASN1toPEM(publicKey),
|
||||
counter: authrDataStruct.counter,
|
||||
keyId: authrDataStruct.credID.toString('base64')
|
||||
}
|
||||
}
|
||||
|
||||
} else if (ctapMakeCredResp.fmt === 'android-safetynet') {
|
||||
console.log("Android safetynet request\n")
|
||||
console.log(ctapMakeCredResp)
|
||||
|
||||
const authrDataStruct = parseMakeCredAuthData(ctapMakeCredResp.authData);
|
||||
console.log('AUTH_DATA', authrDataStruct)
|
||||
//console.log('CLIENT_DATA_JSON ', webauthnResponse.clientDataJSON)
|
||||
|
||||
const publicKey = COSEECDHAtoPKCS(authrDataStruct.COSEPublicKey)
|
||||
|
||||
let [header, payload, signature] = ctapMakeCredResp.attStmt.response.toString('utf8').split('.')
|
||||
const signatureBase = Buffer.from([header, payload].join('.'))
|
||||
|
||||
header = JSON.parse(header)
|
||||
payload = JSON.parse(payload)
|
||||
|
||||
console.log('JWS HEADER', header)
|
||||
console.log('JWS PAYLOAD', payload)
|
||||
console.log('JWS SIGNATURE', signature)
|
||||
|
||||
const PEMCertificate = ASN1toPEM(Buffer.from(header.x5c[0], 'base64'))
|
||||
|
||||
const pem = Certificate.fromPEM(PEMCertificate)
|
||||
|
||||
console.log('PEM', pem)
|
||||
|
||||
response.verified = // Verify that sig is a valid signature over the concatenation of authenticatorData
|
||||
// and clientDataHash using the attestation public key in attestnCert with the algorithm specified in alg.
|
||||
verifySignature(signature, signatureBase, PEMCertificate) &&
|
||||
// version must be 3 (which is indicated by an ASN.1 INTEGER with value 2)
|
||||
pem.version == 3 &&
|
||||
pem.subject.commonName === 'attest.android.com'
|
||||
|
||||
if (response.verified) {
|
||||
response.authrInfo = {
|
||||
fmt: 'fido-u2f',
|
||||
publicKey: ASN1toPEM(publicKey),
|
||||
counter: authrDataStruct.counter,
|
||||
keyId: authrDataStruct.credID.toString('base64')
|
||||
}
|
||||
}
|
||||
|
||||
console.log('RESPONSE', response)
|
||||
} */
|
||||
else {
|
||||
throw new Error(`Unsupported attestation format: ${ctapMakeCredResp.fmt}`);
|
||||
}
|
||||
|
||||
return response
|
||||
}
|
||||
|
||||
obj.verifyAuthenticatorAssertionResponse = function (webauthnResponse, authr) {
|
||||
const response = { 'verified': false }
|
||||
if (['fido-u2f'].includes(authr.fmt)) {
|
||||
const authrDataStruct = parseGetAssertAuthData(webauthnResponse.authenticatorData)
|
||||
if (!(authrDataStruct.flags & 0x01)) { throw new Error('User was not presented durring authentication!') } // U2F_USER_PRESENTED
|
||||
response.counter = authrDataStruct.counter;
|
||||
response.verified = verifySignature(webauthnResponse.signature, Buffer.concat([authrDataStruct.rpIdHash, authrDataStruct.flagsBuf, authrDataStruct.counterBuf, hash(webauthnResponse.clientDataJSON)]), authr.publicKey);
|
||||
}
|
||||
return response;
|
||||
}
|
||||
|
||||
function hash(data) { return crypto.createHash('sha256').update(data).digest() }
|
||||
function verifySignature(signature, data, publicKey) { return crypto.createVerify('SHA256').update(data).verify(publicKey, signature); }
|
||||
|
||||
function parseGetAssertAuthData(buffer) {
|
||||
const rpIdHash = buffer.slice(0, 32)
|
||||
buffer = buffer.slice(32)
|
||||
const flagsBuf = buffer.slice(0, 1)
|
||||
buffer = buffer.slice(1)
|
||||
const flags = flagsBuf[0]
|
||||
const counterBuf = buffer.slice(0, 4)
|
||||
buffer = buffer.slice(4)
|
||||
const counter = counterBuf.readUInt32BE(0)
|
||||
return { rpIdHash, flagsBuf, flags, counter, counterBuf }
|
||||
}
|
||||
|
||||
function parseMakeCredAuthData(buffer) {
|
||||
const rpIdHash = buffer.slice(0, 32)
|
||||
buffer = buffer.slice(32)
|
||||
const flagsBuf = buffer.slice(0, 1)
|
||||
buffer = buffer.slice(1)
|
||||
const flags = flagsBuf[0]
|
||||
const counterBuf = buffer.slice(0, 4)
|
||||
buffer = buffer.slice(4)
|
||||
const counter = counterBuf.readUInt32BE(0)
|
||||
const aaguid = buffer.slice(0, 16)
|
||||
buffer = buffer.slice(16)
|
||||
const credIDLenBuf = buffer.slice(0, 2)
|
||||
buffer = buffer.slice(2)
|
||||
const credIDLen = credIDLenBuf.readUInt16BE(0)
|
||||
const credID = buffer.slice(0, credIDLen)
|
||||
buffer = buffer.slice(credIDLen)
|
||||
const COSEPublicKey = buffer
|
||||
return { rpIdHash, flagsBuf, flags, counter, counterBuf, aaguid, credID, COSEPublicKey }
|
||||
}
|
||||
|
||||
function COSEECDHAtoPKCS(COSEPublicKey) {
|
||||
const coseStruct = cbor.decodeAllSync(COSEPublicKey)[0];
|
||||
return Buffer.concat([Buffer.from([0x04]), coseStruct.get(-2), coseStruct.get(-3)])
|
||||
}
|
||||
|
||||
function ASN1toPEM(pkBuffer) {
|
||||
if (!Buffer.isBuffer(pkBuffer)) { throw new Error("ASN1toPEM: pkBuffer must be Buffer."); }
|
||||
let type
|
||||
if (pkBuffer.length == 65 && pkBuffer[0] == 0x04) { pkBuffer = Buffer.concat([ new Buffer.from("3059301306072a8648ce3d020106082a8648ce3d030107034200", "hex"), pkBuffer ]); type = 'PUBLIC KEY' } else { type = 'CERTIFICATE' }
|
||||
const b64cert = pkBuffer.toString('base64')
|
||||
let PEMKey = ''
|
||||
for (let i = 0; i < Math.ceil(b64cert.length / 64); i++) { const start = 64 * i; PEMKey += b64cert.substr(start, 64) + '\n'; }
|
||||
PEMKey = `-----BEGIN ${type}-----\n` + PEMKey + `-----END ${type}-----\n`
|
||||
return PEMKey
|
||||
}
|
||||
|
||||
return obj;
|
||||
}
|
@ -1748,8 +1748,7 @@ function mainStart(args) {
|
||||
if (config.settings.no2factorauth !== true) {
|
||||
// Setup YubiKey OTP if configured
|
||||
if (yubikey == true) { modules.push('yubikeyotp'); } // Add YubiKey OTP support
|
||||
// if not all SSPI, WebAuthn/FIDO2 or U2F support depending on the NodeJS version. FIDO2 does not work below NodeJS 8.x
|
||||
if (allsspi == false) { modules.push('otplib'); if (nodeVersion >= 8) { modules.push('@davedoesdev/fido2-lib'); } else { modules.push('authdog'); } }
|
||||
if (allsspi == false) { modules.push('otplib'); } // Google Authenticator support
|
||||
}
|
||||
|
||||
// Install any missing modules and launch the server
|
||||
|
107
meshuser.js
107
meshuser.js
@ -2253,64 +2253,6 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
|
||||
}
|
||||
});
|
||||
|
||||
break;
|
||||
}
|
||||
case 'otp-hkey-setup-request':
|
||||
{
|
||||
if (parent.parent.config.settings.no2factorauth === true) return;
|
||||
|
||||
var authdoglib = null;
|
||||
try { authdoglib = require('authdog'); } catch (ex) { }
|
||||
|
||||
// Check is 2-step login is supported
|
||||
const twoStepLoginSupported = ((domain.auth != 'sspi') && (parent.parent.certificates.CommonName.indexOf('.') != -1) && (args.lanonly !== true) && (args.nousers !== true));
|
||||
if ((authdoglib == null) || (twoStepLoginSupported == false)) break;
|
||||
|
||||
// Build list of known keys
|
||||
var knownKeys = [];
|
||||
if (user.otphkeys != null) { for (var i = 0; i < user.otphkeys.length; i++) { if (user.otphkeys[i].type == 1) { knownKeys.push(user.otphkeys[i]); } } }
|
||||
|
||||
// Build a key registration request and send it over
|
||||
authdoglib.startRegistration('https://' + parent.parent.certificates.CommonName, knownKeys, { requestId: 556, timeoutSeconds: 100 }).then(function (registrationRequest) {
|
||||
// Save registration request to session for later use
|
||||
obj.hardwareKeyRegistrationRequest = registrationRequest;
|
||||
|
||||
// Send registration request to client
|
||||
ws.send(JSON.stringify({ action: 'otp-hkey-setup-request', request: registrationRequest, name: command.name }));
|
||||
}, function (error) {
|
||||
// Handle registration request error
|
||||
ws.send(JSON.stringify({ action: 'otp-hkey-setup-request', request: null, error: error, name: command.name }));
|
||||
});
|
||||
break;
|
||||
}
|
||||
case 'otp-hkey-setup-response':
|
||||
{
|
||||
if (parent.parent.config.settings.no2factorauth === true) return;
|
||||
|
||||
var authdoglib = null;
|
||||
try { authdoglib = require('authdog'); } catch (ex) { }
|
||||
|
||||
// Check is 2-step login is supported
|
||||
const twoStepLoginSupported = ((domain.auth != 'sspi') && (parent.parent.certificates.CommonName.indexOf('.') != -1) && (args.lanonly !== true) && (args.nousers !== true));
|
||||
if ((authdoglib == null) || (twoStepLoginSupported == false) || (command.response == null) || (command.name == null) || (obj.hardwareKeyRegistrationRequest == null)) break;
|
||||
|
||||
// Check the key registration request
|
||||
authdoglib.finishRegistration(obj.hardwareKeyRegistrationRequest, command.response).then(function (registrationStatus) {
|
||||
var keyIndex = parent.crypto.randomBytes(4).readUInt32BE(0);
|
||||
ws.send(JSON.stringify({ action: 'otp-hkey-setup-response', result: true, name: command.name, index: keyIndex }));
|
||||
if (user.otphkeys == null) { user.otphkeys = []; }
|
||||
user.otphkeys.push({ name: command.name, type: 1, publicKey: registrationStatus.publicKey, keyHandle: registrationStatus.keyHandle, certificate: registrationStatus.certificate, keyIndex: keyIndex });
|
||||
parent.db.SetUser(user);
|
||||
delete obj.hardwareKeyRegistrationRequest;
|
||||
|
||||
// Notify change
|
||||
var targets = ['*', 'server-users', user._id];
|
||||
if (user.groups) { for (var i in user.groups) { targets.push('server-users:' + i); } }
|
||||
parent.parent.DispatchEvent(targets, obj, { etype: 'user', username: user.name, account: parent.CloneSafeUser(user), action: 'accountchange', msg: 'Added security key.', domain: domain.id });
|
||||
}, function (error) {
|
||||
ws.send(JSON.stringify({ action: 'otp-hkey-setup-response', result: false, error: error, name: command.name, index: keyIndex }));
|
||||
delete obj.hardwareKeyRegistrationRequest;
|
||||
});
|
||||
break;
|
||||
}
|
||||
case 'webauthn-startregister':
|
||||
@ -2319,47 +2261,28 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
|
||||
|
||||
// Check is 2-step login is supported
|
||||
const twoStepLoginSupported = ((domain.auth != 'sspi') && (parent.parent.certificates.CommonName.indexOf('.') != -1) && (args.lanonly !== true) && (args.nousers !== true));
|
||||
if ((twoStepLoginSupported == false) || (command.name == null) || (parent.f2l == null)) break;
|
||||
if ((twoStepLoginSupported == false) || (command.name == null)) break;
|
||||
|
||||
parent.f2l.attestationOptions().then(function (registrationOptions) {
|
||||
// Convert the challenge to base64 and add user information
|
||||
registrationOptions.challenge = Buffer(registrationOptions.challenge).toString('base64');
|
||||
registrationOptions.user.id = Buffer(user._id, 'binary').toString('base64');
|
||||
registrationOptions.user.name = user._id;
|
||||
registrationOptions.user.displayName = user._id.split('/')[2];
|
||||
|
||||
// Send the registration request
|
||||
obj.webAuthnReqistrationRequest = { action: 'webauthn-startregister', keyname: command.name, request: registrationOptions };
|
||||
ws.send(JSON.stringify(obj.webAuthnReqistrationRequest));
|
||||
//console.log(obj.webAuthnReqistrationRequest);
|
||||
}, function (error) {
|
||||
console.log('webauthn-startregister-error', error);
|
||||
});
|
||||
// Send the registration request
|
||||
var registrationOptions = parent.webauthn.generateRegistrationChallenge("Anonymous Service", { id: Buffer(user._id, 'binary').toString('base64'), name: user._id, displayName: user._id.split('/')[2] });
|
||||
obj.webAuthnReqistrationRequest = { action: 'webauthn-startregister', keyname: command.name, request: registrationOptions };
|
||||
ws.send(JSON.stringify(obj.webAuthnReqistrationRequest));
|
||||
break;
|
||||
}
|
||||
case 'webauthn-endregister':
|
||||
{
|
||||
if (parent.parent.config.settings.no2factorauth === true) return;
|
||||
if ((obj.webAuthnReqistrationRequest == null) || (parent.f2l == null)) return;
|
||||
if (obj.webAuthnReqistrationRequest == null) return;
|
||||
|
||||
// Figure out the origin
|
||||
var httpport = ((args.aliasport != null) ? args.aliasport : args.port);
|
||||
var origin = "https://" + (domain.dns ? domain.dns : parent.certificates.CommonName);
|
||||
if (httpport != 443) { origin += ':' + httpport; }
|
||||
|
||||
var attestationExpectations = {
|
||||
challenge: obj.webAuthnReqistrationRequest.request.challenge.split('+').join('-').split('/').join('_').split('=').join(''), // Convert to Base64URL
|
||||
origin: origin,
|
||||
factor: "either"
|
||||
};
|
||||
|
||||
var clientAttestationResponse = command.response;
|
||||
clientAttestationResponse.id = clientAttestationResponse.rawId;
|
||||
clientAttestationResponse.rawId = new Uint8Array(Buffer.from(clientAttestationResponse.rawId, 'base64')).buffer;
|
||||
clientAttestationResponse.response.attestationObject = new Uint8Array(Buffer.from(clientAttestationResponse.response.attestationObject, 'base64')).buffer;
|
||||
clientAttestationResponse.response.clientDataJSON = new Uint8Array(Buffer.from(clientAttestationResponse.response.clientDataJSON, 'base64')).buffer;
|
||||
|
||||
parent.f2l.attestationResult(clientAttestationResponse, attestationExpectations).then(function (regResult) {
|
||||
// Use internal WebAuthn module to check the response
|
||||
var regResult = null;
|
||||
try { regResult = parent.webauthn.verifyAuthenticatorAttestationResponse(command.response.response); } catch (ex) { regResult = { verified: false, error: ex }; }
|
||||
if (regResult.verified === true) {
|
||||
// Since we are registering a WebAuthn/FIDO2 key, remove all U2F keys (Type 1).
|
||||
var otphkeys2 = [];
|
||||
if (user.otphkeys && Array.isArray(user.otphkeys)) { for (var i = 0; i < user.otphkeys.length; i++) { if (user.otphkeys[i].type != 1) { otphkeys2.push(user.otphkeys[i]); } } }
|
||||
@ -2368,7 +2291,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
|
||||
// Add the new WebAuthn/FIDO2 keys
|
||||
var keyIndex = parent.crypto.randomBytes(4).readUInt32BE(0);
|
||||
if (user.otphkeys == null) { user.otphkeys = []; }
|
||||
user.otphkeys.push({ name: obj.webAuthnReqistrationRequest.keyname, type: 3, publicKey: regResult.authnrData.get('credentialPublicKeyPem'), counter: regResult.authnrData.get('counter'), keyIndex: keyIndex, keyId: clientAttestationResponse.id });
|
||||
user.otphkeys.push({ name: obj.webAuthnReqistrationRequest.keyname, type: 3, publicKey: regResult.authrInfo.publicKey, counter: regResult.authrInfo.counter, keyIndex: keyIndex, keyId: regResult.authrInfo.keyId });
|
||||
parent.db.SetUser(user);
|
||||
ws.send(JSON.stringify({ action: 'otp-hkey-setup-response', result: true, name: command.name, index: keyIndex }));
|
||||
|
||||
@ -2376,10 +2299,10 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
|
||||
var targets = ['*', 'server-users', user._id];
|
||||
if (user.groups) { for (var i in user.groups) { targets.push('server-users:' + i); } }
|
||||
parent.parent.DispatchEvent(targets, obj, { etype: 'user', username: user.name, account: parent.CloneSafeUser(user), action: 'accountchange', msg: 'Added security key.', domain: domain.id });
|
||||
}, function (error) {
|
||||
console.log('webauthn-endregister-error', error);
|
||||
ws.send(JSON.stringify({ action: 'otp-hkey-setup-response', result: false, error: error, name: command.name, index: keyIndex }));
|
||||
});
|
||||
} else {
|
||||
//console.log('webauthn-endregister-error', regResult.error);
|
||||
ws.send(JSON.stringify({ action: 'otp-hkey-setup-response', result: false, error: regResult.error, name: command.name, index: keyIndex }));
|
||||
}
|
||||
|
||||
delete obj.hardwareKeyRegistrationRequest;
|
||||
break;
|
||||
|
264
package-lock.json
generated
264
package-lock.json
generated
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "meshcentral",
|
||||
"version": "0.3.1-h",
|
||||
"version": "0.3.4-l",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
@ -9,7 +9,7 @@
|
||||
"resolved": "https://registry.npmjs.org/@davedoesdev/fido2-lib/-/fido2-lib-2.4.0.tgz",
|
||||
"integrity": "sha512-qBNCio4amWMxDe7e8VG3wOo3iu/cFxzLpZ+vzRhLDYbQpZVBhJxFyYXCak33+1qPkDHJOSLZBU7x5jyruU1RMA==",
|
||||
"requires": {
|
||||
"@peculiar/webcrypto": "1.0.8",
|
||||
"@peculiar/webcrypto": "1.0.10",
|
||||
"asn1js": "2.0.22",
|
||||
"cbor": "4.1.5",
|
||||
"cose-to-jwk": "1.1.0",
|
||||
@ -19,6 +19,27 @@
|
||||
"psl": "1.1.31"
|
||||
}
|
||||
},
|
||||
"@fidm/asn1": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@fidm/asn1/-/asn1-1.0.4.tgz",
|
||||
"integrity": "sha512-esd1jyNvRb2HVaQGq2Gg8Z0kbQPXzV9Tq5Z14KNIov6KfFD6PTaRIO8UpcsYiTNzOqJpmyzWgVTrUwFV3UF4TQ=="
|
||||
},
|
||||
"@fidm/x509": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/@fidm/x509/-/x509-1.2.1.tgz",
|
||||
"integrity": "sha512-nwc2iesjyc9hkuzcrMCBXQRn653XuAUKorfWM8PZyJawiy1QzLj4vahwzaI25+pfpwOLvMzbJ0uKpWLDNmo16w==",
|
||||
"requires": {
|
||||
"@fidm/asn1": "1.0.4",
|
||||
"tweetnacl": "1.0.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"tweetnacl": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-1.0.1.tgz",
|
||||
"integrity": "sha512-kcoMoKTPYnoeS50tzoqjPY3Uv9axeuuFAZY9M/9zFnhoVvRfxz9K29IMPD7jGmt2c8SW7i3gT9WqDl2+nV7p4A=="
|
||||
}
|
||||
}
|
||||
},
|
||||
"@peculiar/asn1-schema": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@peculiar/asn1-schema/-/asn1-schema-1.0.3.tgz",
|
||||
@ -37,22 +58,31 @@
|
||||
}
|
||||
},
|
||||
"@peculiar/webcrypto": {
|
||||
"version": "1.0.8",
|
||||
"resolved": "https://registry.npmjs.org/@peculiar/webcrypto/-/webcrypto-1.0.8.tgz",
|
||||
"integrity": "sha512-zOCwDimHbQeUgSdBCHnw/69wbA1ks+aUt1MPAttcWNOlXaZjbUppc8shtwX/mzDQYctzNGFlI8Xrayl7lBUe5A==",
|
||||
"version": "1.0.10",
|
||||
"resolved": "https://registry.npmjs.org/@peculiar/webcrypto/-/webcrypto-1.0.10.tgz",
|
||||
"integrity": "sha512-Ohkfu3xgk/Y/6+40Uq8juAO1zPuuAuSJ/BLpP4xovjU3+u3J7Rc/dbcWB4/Z2sj+0L4hZXh6p4rZsvzDFk22QQ==",
|
||||
"requires": {
|
||||
"@peculiar/asn1-schema": "1.0.3",
|
||||
"@peculiar/json-schema": "1.1.5",
|
||||
"asn1js": "2.0.22",
|
||||
"pvtsutils": "1.0.4",
|
||||
"tslib": "1.9.3",
|
||||
"webcrypto-core": "1.0.11"
|
||||
"webcrypto-core": "1.0.12"
|
||||
}
|
||||
},
|
||||
"@types/node": {
|
||||
"version": "10.14.4",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-10.14.4.tgz",
|
||||
"integrity": "sha512-DT25xX/YgyPKiHFOpNuANIQIVvYEwCWXgK2jYYwqgaMrYE6+tq+DtmMwlD3drl6DJbUwtlIDnn0d7tIn/EbXBg=="
|
||||
"version": "10.14.6",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-10.14.6.tgz",
|
||||
"integrity": "sha512-Fvm24+u85lGmV4hT5G++aht2C5I4Z4dYlWZIh62FAfFO/TfzXtPpoLI6I7AuBWkIFqZCnhFOoTT7RjjaIL5Fjg=="
|
||||
},
|
||||
"abstract-leveldown": {
|
||||
"version": "6.0.3",
|
||||
"resolved": "https://registry.npmjs.org/abstract-leveldown/-/abstract-leveldown-6.0.3.tgz",
|
||||
"integrity": "sha512-jzewKKpZbaYUa6HTThnrl+GrJhzjEAeuc7hTVpZdzg7kupXZFoqQDFwyOwLNbmJKJlmzw8yiipMPkDiuKkT06Q==",
|
||||
"requires": {
|
||||
"level-concat-iterator": "2.0.1",
|
||||
"xtend": "4.0.1"
|
||||
}
|
||||
},
|
||||
"accepts": {
|
||||
"version": "1.3.5",
|
||||
@ -430,9 +460,9 @@
|
||||
}
|
||||
},
|
||||
"combined-stream": {
|
||||
"version": "1.0.7",
|
||||
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.7.tgz",
|
||||
"integrity": "sha512-brWl9y6vOB1xYPZcpZde3N9zDByXTosAeMDo4p1wzo6UMOX4vumB+TP1RZ76sfE6Md68Q0NJSrE/gbezd4Ul+w==",
|
||||
"version": "1.0.8",
|
||||
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
|
||||
"integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
|
||||
"requires": {
|
||||
"delayed-stream": "1.0.0"
|
||||
}
|
||||
@ -652,6 +682,15 @@
|
||||
"integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=",
|
||||
"optional": true
|
||||
},
|
||||
"deferred-leveldown": {
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/deferred-leveldown/-/deferred-leveldown-5.0.1.tgz",
|
||||
"integrity": "sha512-BXohsvTedWOLkj2n/TY+yqVlrCWa2Zs8LSxh3uCAgFOru7/pjxKyZAexGa1j83BaKloER4PqUyQ9rGPJLt9bqA==",
|
||||
"requires": {
|
||||
"abstract-leveldown": "6.0.3",
|
||||
"inherits": "2.0.3"
|
||||
}
|
||||
},
|
||||
"define-properties": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.2.tgz",
|
||||
@ -724,6 +763,17 @@
|
||||
"resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
|
||||
"integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k="
|
||||
},
|
||||
"encoding-down": {
|
||||
"version": "6.0.2",
|
||||
"resolved": "https://registry.npmjs.org/encoding-down/-/encoding-down-6.0.2.tgz",
|
||||
"integrity": "sha512-oAEANslmNb64AF4kvHXjTxB7KecwD7X0qf8MffMfhpjP6gjGcnCTOkRgps/1yUNeR4Bhe6ckN6aAzZz+RIYgTw==",
|
||||
"requires": {
|
||||
"abstract-leveldown": "6.0.3",
|
||||
"inherits": "2.0.3",
|
||||
"level-codec": "9.0.1",
|
||||
"level-errors": "2.0.1"
|
||||
}
|
||||
},
|
||||
"end-of-stream": {
|
||||
"version": "1.4.1",
|
||||
"resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.1.tgz",
|
||||
@ -732,6 +782,14 @@
|
||||
"once": "1.4.0"
|
||||
}
|
||||
},
|
||||
"errno": {
|
||||
"version": "0.1.7",
|
||||
"resolved": "https://registry.npmjs.org/errno/-/errno-0.1.7.tgz",
|
||||
"integrity": "sha512-MfrRBDWzIWifgq6tJj60gkAwtLNb6sQPlcFrSOflcP1aFmmruKQ2wRnze/8V6kgyz7H3FF8Npzv78mZ7XLLflg==",
|
||||
"requires": {
|
||||
"prr": "1.0.1"
|
||||
}
|
||||
},
|
||||
"es6-promise": {
|
||||
"version": "3.2.1",
|
||||
"resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-3.2.1.tgz",
|
||||
@ -872,6 +930,11 @@
|
||||
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz",
|
||||
"integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk="
|
||||
},
|
||||
"fast-future": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/fast-future/-/fast-future-1.0.2.tgz",
|
||||
"integrity": "sha1-hDWpqqAteSSNF9cE52JZMB2ZKAo="
|
||||
},
|
||||
"fast-json-stable-stringify": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz",
|
||||
@ -915,7 +978,7 @@
|
||||
"integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==",
|
||||
"requires": {
|
||||
"asynckit": "0.4.0",
|
||||
"combined-stream": "1.0.7",
|
||||
"combined-stream": "1.0.8",
|
||||
"mime-types": "2.1.21"
|
||||
}
|
||||
},
|
||||
@ -1104,6 +1167,11 @@
|
||||
"resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
|
||||
"integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE="
|
||||
},
|
||||
"iso-3166-1": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/iso-3166-1/-/iso-3166-1-1.1.0.tgz",
|
||||
"integrity": "sha1-gGrfYoPV96pAXRY3qKUC+R0sVHw="
|
||||
},
|
||||
"isstream": {
|
||||
"version": "0.1.2",
|
||||
"resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz",
|
||||
@ -1185,6 +1253,107 @@
|
||||
"readable-stream": "2.3.6"
|
||||
}
|
||||
},
|
||||
"level": {
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/level/-/level-5.0.1.tgz",
|
||||
"integrity": "sha512-wcak5OQeA4rURGacqS62R/xNHjCYnJSQDBOlm4KNUGJVE9bWv2B04TclqReYejN+oD65PzD4FsqeWoI5wNC5Lg==",
|
||||
"requires": {
|
||||
"level-js": "4.0.1",
|
||||
"level-packager": "5.0.1",
|
||||
"leveldown": "5.0.3",
|
||||
"opencollective-postinstall": "2.0.2"
|
||||
}
|
||||
},
|
||||
"level-codec": {
|
||||
"version": "9.0.1",
|
||||
"resolved": "https://registry.npmjs.org/level-codec/-/level-codec-9.0.1.tgz",
|
||||
"integrity": "sha512-ajFP0kJ+nyq4i6kptSM+mAvJKLOg1X5FiFPtLG9M5gCEZyBmgDi3FkDrvlMkEzrUn1cWxtvVmrvoS4ASyO/q+Q=="
|
||||
},
|
||||
"level-concat-iterator": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/level-concat-iterator/-/level-concat-iterator-2.0.1.tgz",
|
||||
"integrity": "sha512-OTKKOqeav2QWcERMJR7IS9CUo1sHnke2C0gkSmcR7QuEtFNLLzHQAvnMw8ykvEcv0Qtkg0p7FOwP1v9e5Smdcw=="
|
||||
},
|
||||
"level-errors": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/level-errors/-/level-errors-2.0.1.tgz",
|
||||
"integrity": "sha512-UVprBJXite4gPS+3VznfgDSU8PTRuVX0NXwoWW50KLxd2yw4Y1t2JUR5In1itQnudZqRMT9DlAM3Q//9NCjCFw==",
|
||||
"requires": {
|
||||
"errno": "0.1.7"
|
||||
}
|
||||
},
|
||||
"level-iterator-stream": {
|
||||
"version": "4.0.1",
|
||||
"resolved": "https://registry.npmjs.org/level-iterator-stream/-/level-iterator-stream-4.0.1.tgz",
|
||||
"integrity": "sha512-pSZWqXK6/yHQkZKCHrR59nKpU5iqorKM22C/BOHTb/cwNQ2EOZG+bovmFFGcOgaBoF3KxqJEI27YwewhJQTzsw==",
|
||||
"requires": {
|
||||
"inherits": "2.0.3",
|
||||
"readable-stream": "3.3.0",
|
||||
"xtend": "4.0.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"readable-stream": {
|
||||
"version": "3.3.0",
|
||||
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.3.0.tgz",
|
||||
"integrity": "sha512-EsI+s3k3XsW+fU8fQACLN59ky34AZ14LoeVZpYwmZvldCFo0r0gnelwF2TcMjLor/BTL5aDJVBMkss0dthToPw==",
|
||||
"requires": {
|
||||
"inherits": "2.0.3",
|
||||
"string_decoder": "1.1.1",
|
||||
"util-deprecate": "1.0.2"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"level-js": {
|
||||
"version": "4.0.1",
|
||||
"resolved": "https://registry.npmjs.org/level-js/-/level-js-4.0.1.tgz",
|
||||
"integrity": "sha512-m5JRIyHZn5VnCCFeRegJkn5bQd3MJK5qZX12zg3Oivc8+BUIS2yFS6ANMMeHX2ieGxucNvEn6/ZnyjmZQLLUWw==",
|
||||
"requires": {
|
||||
"abstract-leveldown": "6.0.3",
|
||||
"immediate": "3.2.3",
|
||||
"inherits": "2.0.3",
|
||||
"ltgt": "2.2.1",
|
||||
"typedarray-to-buffer": "3.1.5"
|
||||
},
|
||||
"dependencies": {
|
||||
"immediate": {
|
||||
"version": "3.2.3",
|
||||
"resolved": "https://registry.npmjs.org/immediate/-/immediate-3.2.3.tgz",
|
||||
"integrity": "sha1-0UD6j2FGWb1lQSMwl92qwlzdmRw="
|
||||
}
|
||||
}
|
||||
},
|
||||
"level-packager": {
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/level-packager/-/level-packager-5.0.1.tgz",
|
||||
"integrity": "sha512-tigB8g7xnFE5es2d/OmGJvcJC9S+FQfJnnULSLbPr53/ABPIaCarCxofkwdBhnJxjLZCNvj/Je6luFj/XeuaUQ==",
|
||||
"requires": {
|
||||
"encoding-down": "6.0.2",
|
||||
"levelup": "4.0.1"
|
||||
}
|
||||
},
|
||||
"leveldown": {
|
||||
"version": "5.0.3",
|
||||
"resolved": "https://registry.npmjs.org/leveldown/-/leveldown-5.0.3.tgz",
|
||||
"integrity": "sha512-isfWtOQIXbGbQRI8nmU9FqCZM0klmqTAOFi0vF6G/D0O1ZgxLrSh6Xd4Zj9iVQfGt6+8jpYwkRbN07VLrxRM8w==",
|
||||
"requires": {
|
||||
"abstract-leveldown": "6.0.3",
|
||||
"fast-future": "1.0.2",
|
||||
"napi-macros": "1.8.2",
|
||||
"node-gyp-build": "3.8.0"
|
||||
}
|
||||
},
|
||||
"levelup": {
|
||||
"version": "4.0.1",
|
||||
"resolved": "https://registry.npmjs.org/levelup/-/levelup-4.0.1.tgz",
|
||||
"integrity": "sha512-l7KXOkINXHgNqmz0v9bxvRnMCUG4gmShFrzFSZXXhcqFnfvKAW8NerVsTICpZtVhGOMAmhY6JsVoVh/tUPBmdg==",
|
||||
"requires": {
|
||||
"deferred-leveldown": "5.0.1",
|
||||
"level-errors": "2.0.1",
|
||||
"level-iterator-stream": "4.0.1",
|
||||
"xtend": "4.0.1"
|
||||
}
|
||||
},
|
||||
"lie": {
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/lie/-/lie-3.1.1.tgz",
|
||||
@ -1251,6 +1420,11 @@
|
||||
"resolved": "https://registry.npmjs.org/longest/-/longest-1.0.1.tgz",
|
||||
"integrity": "sha1-MKCy2jj3N3DoKUoNIuZiXtd9AJc="
|
||||
},
|
||||
"ltgt": {
|
||||
"version": "2.2.1",
|
||||
"resolved": "https://registry.npmjs.org/ltgt/-/ltgt-2.2.1.tgz",
|
||||
"integrity": "sha1-81ypHEk/e3PaDgdJUwTxezH4fuU="
|
||||
},
|
||||
"media-typer": {
|
||||
"version": "0.3.0",
|
||||
"resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
|
||||
@ -1373,7 +1547,7 @@
|
||||
"mongojs": {
|
||||
"version": "2.6.0",
|
||||
"resolved": "https://registry.npmjs.org/mongojs/-/mongojs-2.6.0.tgz",
|
||||
"integrity": "sha512-r6tj71DjYcaRTi2jpa+CA6Iq72cTZclB2JKy+Zub+0JPTEq/l2plsAYfF2eHqSYBtZbKNcObvhGYk9E9UKZWJg==",
|
||||
"integrity": "sha1-Mz9YBiGg3GSACGXjjjam5h/xaYk=",
|
||||
"requires": {
|
||||
"each-series": "1.0.0",
|
||||
"mongodb": "2.2.36",
|
||||
@ -1433,6 +1607,11 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"napi-macros": {
|
||||
"version": "1.8.2",
|
||||
"resolved": "https://registry.npmjs.org/napi-macros/-/napi-macros-1.8.2.tgz",
|
||||
"integrity": "sha512-Tr0DNY4RzTaBG2W2m3l7ZtFuJChTH6VZhXVhkGGjF/4cZTt+i8GcM9ozD+30Lmr4mDoZ5Xx34t2o4GJqYWDGcg=="
|
||||
},
|
||||
"nedb": {
|
||||
"version": "1.8.0",
|
||||
"resolved": "https://registry.npmjs.org/nedb/-/nedb-1.8.0.tgz",
|
||||
@ -1455,6 +1634,11 @@
|
||||
"resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.7.6.tgz",
|
||||
"integrity": "sha1-/fO0GK7h+U8O9kLNY0hsd8qXJKw="
|
||||
},
|
||||
"node-gyp-build": {
|
||||
"version": "3.8.0",
|
||||
"resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-3.8.0.tgz",
|
||||
"integrity": "sha512-bYbpIHyRqZ7sVWXxGpz8QIRug5JZc/hzZH4GbdT9HTZi6WmKCZ8GLvP8OZ9TTiIBvwPFKgtGrlWQSXDAvYdsPw=="
|
||||
},
|
||||
"node-jose": {
|
||||
"version": "1.1.3",
|
||||
"resolved": "https://registry.npmjs.org/node-jose/-/node-jose-1.1.3.tgz",
|
||||
@ -1464,7 +1648,7 @@
|
||||
"es6-promise": "4.2.6",
|
||||
"lodash": "4.17.11",
|
||||
"long": "4.0.0",
|
||||
"node-forge": "0.8.2",
|
||||
"node-forge": "0.8.3",
|
||||
"uuid": "3.3.2"
|
||||
},
|
||||
"dependencies": {
|
||||
@ -1474,9 +1658,9 @@
|
||||
"integrity": "sha512-aRVgGdnmW2OiySVPUC9e6m+plolMAJKjZnQlCwNSuK5yQ0JN61DZSO1X1Ufd1foqWRAlig0rhduTCHe7sVtK5Q=="
|
||||
},
|
||||
"node-forge": {
|
||||
"version": "0.8.2",
|
||||
"resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.8.2.tgz",
|
||||
"integrity": "sha512-mXQ9GBq1N3uDCyV1pdSzgIguwgtVpM7f5/5J4ipz12PKWElmPpVWLDuWl8iXmhysr21+WmX/OJ5UKx82wjomgg=="
|
||||
"version": "0.8.3",
|
||||
"resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.8.3.tgz",
|
||||
"integrity": "sha512-5lv9UKmvTBog+m4AWL8XpZnr3WbNKxYL2M77i903ylY/huJIooSTDHyUWQ/OppFuKQpAGMk6qNtDymSJNRIEIg=="
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -1541,6 +1725,11 @@
|
||||
"wrappy": "1.0.2"
|
||||
}
|
||||
},
|
||||
"opencollective-postinstall": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/opencollective-postinstall/-/opencollective-postinstall-2.0.2.tgz",
|
||||
"integrity": "sha512-pVOEP16TrAO2/fjej1IdOyupJY8KDUM1CvsaScRbw6oddvpQoOfGk4ywha0HKKVAD6RkW4x6Q+tNBwhf3Bgpuw=="
|
||||
},
|
||||
"optimist": {
|
||||
"version": "0.6.1",
|
||||
"resolved": "https://registry.npmjs.org/optimist/-/optimist-0.6.1.tgz",
|
||||
@ -1637,6 +1826,11 @@
|
||||
"ipaddr.js": "1.8.0"
|
||||
}
|
||||
},
|
||||
"prr": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz",
|
||||
"integrity": "sha1-0/wRS6BplaRexok/SEzrHXj19HY="
|
||||
},
|
||||
"psl": {
|
||||
"version": "1.1.31",
|
||||
"resolved": "https://registry.npmjs.org/psl/-/psl-1.1.31.tgz",
|
||||
@ -1652,7 +1846,7 @@
|
||||
"resolved": "https://registry.npmjs.org/pvtsutils/-/pvtsutils-1.0.4.tgz",
|
||||
"integrity": "sha512-lBDyLfPIWZjxHr6Nnl83/iaZgVLczDcpEqWdqRnghzBKXifRU/7D5T6JPYWUAm0sJdFeF9+sNTKto6dj/3P/Kg==",
|
||||
"requires": {
|
||||
"@types/node": "10.14.4",
|
||||
"@types/node": "10.14.6",
|
||||
"tslib": "1.9.3"
|
||||
}
|
||||
},
|
||||
@ -1739,7 +1933,7 @@
|
||||
"aws-sign2": "0.7.0",
|
||||
"aws4": "1.8.0",
|
||||
"caseless": "0.12.0",
|
||||
"combined-stream": "1.0.7",
|
||||
"combined-stream": "1.0.8",
|
||||
"extend": "3.0.2",
|
||||
"forever-agent": "0.6.1",
|
||||
"form-data": "2.3.3",
|
||||
@ -1958,6 +2152,14 @@
|
||||
"mime-types": "2.1.21"
|
||||
}
|
||||
},
|
||||
"typedarray-to-buffer": {
|
||||
"version": "3.1.5",
|
||||
"resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz",
|
||||
"integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==",
|
||||
"requires": {
|
||||
"is-typedarray": "1.0.0"
|
||||
}
|
||||
},
|
||||
"uglify-js": {
|
||||
"version": "2.8.29",
|
||||
"resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-2.8.29.tgz",
|
||||
@ -2022,7 +2224,7 @@
|
||||
"uuid": {
|
||||
"version": "3.3.2",
|
||||
"resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz",
|
||||
"integrity": "sha1-G0r0lV6zB3xQHCOHL8ZROBFYcTE="
|
||||
"integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA=="
|
||||
},
|
||||
"vary": {
|
||||
"version": "1.1.2",
|
||||
@ -2039,10 +2241,24 @@
|
||||
"extsprintf": "1.3.0"
|
||||
}
|
||||
},
|
||||
"webauthn": {
|
||||
"version": "0.1.1",
|
||||
"resolved": "https://registry.npmjs.org/webauthn/-/webauthn-0.1.1.tgz",
|
||||
"integrity": "sha512-UEMV9M1MKK1EscfTKNU8/7Y+C31e+2bkgV3Pp66k4deHvBw8qDD4TNCAtaRKdPlPy8g0aZ/8sI1Q4obMPPwbEQ==",
|
||||
"requires": {
|
||||
"@fidm/x509": "1.2.1",
|
||||
"base64url": "3.0.1",
|
||||
"cbor": "4.1.5",
|
||||
"express": "4.16.4",
|
||||
"iso-3166-1": "1.1.0",
|
||||
"level": "5.0.1",
|
||||
"lodash": "4.17.11"
|
||||
}
|
||||
},
|
||||
"webcrypto-core": {
|
||||
"version": "1.0.11",
|
||||
"resolved": "https://registry.npmjs.org/webcrypto-core/-/webcrypto-core-1.0.11.tgz",
|
||||
"integrity": "sha512-cHxOMKNzRkPlCcQyGTuyaZmAVZlbby0lH9ASaFk5w8x/i3Q809sISA28AMVnkebW2ekcQ2+UtdaVBFUf58xjyA==",
|
||||
"version": "1.0.12",
|
||||
"resolved": "https://registry.npmjs.org/webcrypto-core/-/webcrypto-core-1.0.12.tgz",
|
||||
"integrity": "sha512-U+qBSJY4u+Ev5KNiCOPZ5MwiCSDdoM9gxeYLdnr3q8LWnX6GOBJqcowhl/kOplbhHtaa4IVZK7R2zNC4fJ7/RQ==",
|
||||
"requires": {
|
||||
"pvtsutils": "1.0.4",
|
||||
"tslib": "1.9.3"
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "meshcentral",
|
||||
"version": "0.3.4-k",
|
||||
"version": "0.3.4-l",
|
||||
"keywords": [
|
||||
"Remote Management",
|
||||
"Intel AMT",
|
||||
@ -29,6 +29,7 @@
|
||||
"dependencies": {
|
||||
"archiver": "^3.0.0",
|
||||
"body-parser": "^1.18.2",
|
||||
"cbor": "^4.1.5",
|
||||
"compression": "^1.7.3",
|
||||
"connect-redis": "^3.4.0",
|
||||
"cookie-session": "^2.0.0-beta.3",
|
||||
@ -38,10 +39,10 @@
|
||||
"ipcheck": "^0.1.0",
|
||||
"meshcentral": "*",
|
||||
"minimist": "^1.2.0",
|
||||
"mongojs": "^2.6.0",
|
||||
"multiparty": "^4.2.1",
|
||||
"nedb": "^1.8.0",
|
||||
"node-forge": "^0.7.6",
|
||||
"node-windows": "^0.1.14",
|
||||
"ws": "^6.1.2",
|
||||
"xmldom": "^0.1.27",
|
||||
"yauzl": "^2.10.0"
|
||||
|
File diff suppressed because one or more lines are too long
@ -1561,7 +1561,6 @@
|
||||
}
|
||||
x += "</div>";
|
||||
x += "<div><input type=button value='Close' onclick=setDialogMode(0) style=float:right></input>";
|
||||
if ((features & 0x00020000) == 0) { x += "<input id=d2addkey1 type=button value='Add Key' onclick='account_addhkey(1);'></input>"; }
|
||||
if ((features & 0x00020000) != 0) { x += "<input id=d2addkey3 type=button value='Add Key' onclick='account_addhkey(3);'></input>"; }
|
||||
if ((features & 0x00004000) != 0) { x += "<input id=d2addkey2 type=button value='Add YubiKey® OTP' onclick='account_addhkey(2);'></input>"; }
|
||||
x += "</div><br />";
|
||||
@ -1577,23 +1576,6 @@
|
||||
}
|
||||
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 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', response: registrationResponse, name: Q('dp1keyname').value });
|
||||
setDialogMode(2, "Add Security Key", 0, null, '<br />Checking...<br /><br /><br />', 'otpauth-hardware-manage');
|
||||
} else {
|
||||
if (xxdialogMode && (xxdialogTag != 'otpauth-hardware-manage')) {
|
||||
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': {
|
||||
if (xxdialogMode && (xxdialogTag != 'otpauth-hardware-manage')) return;
|
||||
if (message.result == true) {
|
||||
@ -5777,7 +5759,7 @@
|
||||
}
|
||||
|
||||
function account_addhkey(type) {
|
||||
if (type == 1 || type == 3) {
|
||||
if (type == 3) {
|
||||
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) {
|
||||
@ -5796,9 +5778,7 @@
|
||||
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) {
|
||||
if (type == 2) {
|
||||
meshserver.send({ action: 'otp-hkey-yubikey-add', name: name, otp: Q('dp1key').value });
|
||||
setDialogMode(2, "Add Security Key", 0, null, "<br />Checking...<br /><br /><br />", 'otpauth-hardware-manage');
|
||||
} else if (type == 3) {
|
||||
|
148
webserver.js
148
webserver.js
@ -63,7 +63,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
|
||||
const constants = (obj.crypto.constants ? obj.crypto.constants : require('constants')); // require('constants') is deprecated in Node 11.10, use require('crypto').constants instead.
|
||||
|
||||
// Setup WebAuthn / FIDO2
|
||||
try { const { Fido2Lib } = require("@davedoesdev/fido2-lib"); obj.f2l = new Fido2Lib({ attestation: "none" }); } catch (ex) { }
|
||||
obj.webauthn = require("./webauthn.js").CreateWebAuthnModule();
|
||||
|
||||
// Variables
|
||||
obj.parent = parent;
|
||||
@ -503,66 +503,50 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
|
||||
if (user.otphkeys && (user.otphkeys.length > 0) && (typeof (hwtoken) == 'string') && (hwtoken.length > 0)) {
|
||||
var authResponse = null;
|
||||
try { authResponse = JSON.parse(hwtoken); } catch (ex) { }
|
||||
if (authResponse != null) {
|
||||
if ((obj.f2l != null) && (authResponse.clientDataJSON)) {
|
||||
// Get all WebAuthn keys
|
||||
var webAuthnKeys = [];
|
||||
for (var i = 0; i < user.otphkeys.length; i++) { if (user.otphkeys[i].type == 3) { webAuthnKeys.push(user.otphkeys[i]); } }
|
||||
if (webAuthnKeys.length > 0) {
|
||||
// Decode authentication response
|
||||
var clientAssertionResponse = { response: {} };
|
||||
clientAssertionResponse.id = authResponse.id;
|
||||
clientAssertionResponse.rawId = new Uint8Array(Buffer.from(authResponse.id, 'base64')).buffer;
|
||||
clientAssertionResponse.response.authenticatorData = new Uint8Array(Buffer.from(authResponse.authenticatorData, 'base64')).buffer;
|
||||
clientAssertionResponse.response.clientDataJSON = new Uint8Array(Buffer.from(authResponse.clientDataJSON, 'base64')).buffer;
|
||||
clientAssertionResponse.response.signature = new Uint8Array(Buffer.from(authResponse.signature, 'base64')).buffer;
|
||||
clientAssertionResponse.response.userHandle = new Uint8Array(Buffer.from(authResponse.userHandle, 'base64')).buffer;
|
||||
if ((authResponse != null) && (authResponse.clientDataJSON)) {
|
||||
// Get all WebAuthn keys
|
||||
var webAuthnKeys = [];
|
||||
for (var i = 0; i < user.otphkeys.length; i++) { if (user.otphkeys[i].type == 3) { webAuthnKeys.push(user.otphkeys[i]); } }
|
||||
if (webAuthnKeys.length > 0) {
|
||||
// Decode authentication response
|
||||
var clientAssertionResponse = { response: {} };
|
||||
clientAssertionResponse.id = authResponse.id;
|
||||
clientAssertionResponse.rawId = Buffer.from(authResponse.id, 'base64');
|
||||
clientAssertionResponse.response.authenticatorData = Buffer.from(authResponse.authenticatorData, 'base64');
|
||||
clientAssertionResponse.response.clientDataJSON = Buffer.from(authResponse.clientDataJSON, 'base64');
|
||||
clientAssertionResponse.response.signature = Buffer.from(authResponse.signature, 'base64');
|
||||
clientAssertionResponse.response.userHandle = Buffer.from(authResponse.userHandle, 'base64');
|
||||
|
||||
// Look for the key with clientAssertionResponse.id
|
||||
var webAuthnKey = null;
|
||||
for (var i = 0; i < webAuthnKeys.length; i++) { if (webAuthnKeys[i].keyId == clientAssertionResponse.id) { webAuthnKey = webAuthnKeys[i]; } }
|
||||
// Look for the key with clientAssertionResponse.id
|
||||
var webAuthnKey = null;
|
||||
for (var i = 0; i < webAuthnKeys.length; i++) { if (webAuthnKeys[i].keyId == clientAssertionResponse.id) { webAuthnKey = webAuthnKeys[i]; } }
|
||||
|
||||
// If we found a valid key to use, let's validate the response
|
||||
if (webAuthnKey != null) {
|
||||
// Figure out the origin
|
||||
var httpport = ((args.aliasport != null) ? args.aliasport : args.port);
|
||||
var origin = "https://" + (domain.dns ? domain.dns : parent.certificates.CommonName);
|
||||
if (httpport != 443) { origin += ':' + httpport; }
|
||||
// If we found a valid key to use, let's validate the response
|
||||
if (webAuthnKey != null) {
|
||||
// Figure out the origin
|
||||
var httpport = ((args.aliasport != null) ? args.aliasport : args.port);
|
||||
var origin = "https://" + (domain.dns ? domain.dns : parent.certificates.CommonName);
|
||||
if (httpport != 443) { origin += ':' + httpport; }
|
||||
|
||||
var assertionExpectations = {
|
||||
challenge: req.session.u2fchallenge,
|
||||
origin: origin,
|
||||
factor: "either",
|
||||
publicKey: webAuthnKey.publicKey,
|
||||
prevCounter: webAuthnKey.counter,
|
||||
userHandle: Buffer(user._id, 'binary').toString('base64')
|
||||
};
|
||||
var assertionExpectations = {
|
||||
challenge: req.session.u2fchallenge,
|
||||
origin: origin,
|
||||
factor: "either",
|
||||
fmt: "fido-u2f",
|
||||
publicKey: webAuthnKey.publicKey,
|
||||
prevCounter: webAuthnKey.counter,
|
||||
userHandle: Buffer(user._id, 'binary').toString('base64')
|
||||
};
|
||||
|
||||
obj.f2l.assertionResult(clientAssertionResponse, assertionExpectations).then(
|
||||
function (authnResult) {
|
||||
// Update the hardware key counter and accept the 2nd factor
|
||||
webAuthnKey.counter = authnResult.authnrData.get('counter');
|
||||
obj.db.SetUser(user);
|
||||
func(true);
|
||||
},
|
||||
function (error) {
|
||||
//console.log('attestationResult-error', error);
|
||||
func(false);
|
||||
}
|
||||
);
|
||||
return;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Get all U2F keys
|
||||
var u2fKeys = [];
|
||||
for (var i = 0; i < user.otphkeys.length; i++) { if (user.otphkeys[i].type == 1) { u2fKeys.push(user.otphkeys[i]); } }
|
||||
if (u2fKeys.length > 0) {
|
||||
// Check authentication response
|
||||
var authdoglib = null;
|
||||
try { authdoglib = require('authdog'); } catch (ex) { }
|
||||
if (authdoglib == null) { func(false); } else {
|
||||
authdoglib.finishAuthentication(req.session.u2fchallenge, authResponse, u2fKeys).then(function (authenticationStatus) { func(true); }, function (error) { console.log(error); func(false); });
|
||||
var webauthnResponse = null;
|
||||
try { webauthnResponse = obj.webauthn.verifyAuthenticatorAssertionResponse(clientAssertionResponse.response, assertionExpectations); } catch (ex) { console.log(ex); }
|
||||
if ((webauthnResponse != null) && (webauthnResponse.verified === true)) {
|
||||
// Update the hardware key counter and accept the 2nd factor
|
||||
webAuthnKey.counter = webauthnResponse.counter;
|
||||
obj.db.SetUser(user);
|
||||
func(true);
|
||||
} else {
|
||||
func(false);
|
||||
}
|
||||
return;
|
||||
}
|
||||
@ -607,46 +591,14 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
|
||||
if (req.session.u2fchallenge) { delete req.session.u2fchallenge; };
|
||||
if (user.otphkeys && (user.otphkeys.length > 0)) {
|
||||
// Get all WebAuthn keys
|
||||
if (obj.f2l != null) {
|
||||
var webAuthnKeys = [];
|
||||
for (var i = 0; i < user.otphkeys.length; i++) { if (user.otphkeys[i].type == 3) { webAuthnKeys.push(user.otphkeys[i]); } }
|
||||
if (webAuthnKeys.length > 0) {
|
||||
obj.f2l.assertionOptions().then(function (authnOptions) {
|
||||
authnOptions.type = 'webAuthn';
|
||||
authnOptions.keyIds = [];
|
||||
for (var i = 0; i < webAuthnKeys.length; i++) { authnOptions.keyIds.push(webAuthnKeys[i].keyId); }
|
||||
req.session.u2fchallenge = authnOptions.challenge = Buffer(authnOptions.challenge).toString('base64');
|
||||
func(JSON.stringify(authnOptions));
|
||||
}, function (error) {
|
||||
console.log('assertionOptions-Error', error);
|
||||
func('');
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
var authdoglib = null;
|
||||
try { authdoglib = require('authdog'); } catch (ex) { }
|
||||
if (authdoglib != null) {
|
||||
// Get all U2F keys
|
||||
var u2fKeys = [];
|
||||
for (var i = 0; i < user.otphkeys.length; i++) { if (user.otphkeys[i].type == 1) { u2fKeys.push(user.otphkeys[i]); } }
|
||||
|
||||
// Generate a U2F challenge
|
||||
if (u2fKeys.length > 0) {
|
||||
authdoglib.startAuthentication('https://' + obj.parent.certificates.CommonName, u2fKeys, { requestId: 0, timeoutSeconds: 60 }).then(function (registrationRequest) {
|
||||
// Save authentication request to session for later use
|
||||
req.session.u2fchallenge = registrationRequest;
|
||||
|
||||
// Send authentication request to client
|
||||
func(JSON.stringify(registrationRequest));
|
||||
}, function (error) {
|
||||
// Handle authentication request error
|
||||
func('');
|
||||
});
|
||||
} else {
|
||||
func('');
|
||||
}
|
||||
var webAuthnKeys = [];
|
||||
for (var i = 0; i < user.otphkeys.length; i++) { if (user.otphkeys[i].type == 3) { webAuthnKeys.push(user.otphkeys[i]); } }
|
||||
if (webAuthnKeys.length > 0) {
|
||||
// Generate a Webauthn challenge, this is really easy, no need to call any modules to do this.
|
||||
var authnOptions = { type: 'webAuthn', keyIds: [], timeout: 60000, challenge: obj.crypto.randomBytes(64).toString('base64') };
|
||||
for (var i = 0; i < webAuthnKeys.length; i++) { authnOptions.keyIds.push(webAuthnKeys[i].keyId); }
|
||||
req.session.u2fchallenge = authnOptions.challenge;
|
||||
func(JSON.stringify(authnOptions));
|
||||
}
|
||||
} else {
|
||||
func('');
|
||||
@ -1338,7 +1290,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
|
||||
if ((parent.config.settings.no2factorauth !== true) && domain.yubikey && domain.yubikey.id && domain.yubikey.secret) { features += 0x00004000; } // Indicates Yubikey support
|
||||
if (domain.geolocation == true) { features += 0x00008000; } // Enable geo-location features
|
||||
if ((domain.passwordrequirements != null) && (domain.passwordrequirements.hint === true)) { features += 0x00010000; } // Enable password hints
|
||||
if ((parent.config.settings.no2factorauth !== true) && (obj.f2l != null)) { features += 0x00020000; } // Enable WebAuthn/FIDO2 support
|
||||
if (parent.config.settings.no2factorauth !== true) { features += 0x00020000; } // Enable WebAuthn/FIDO2 support
|
||||
if ((obj.args.nousers != true) && (domain.passwordrequirements != null) && (domain.passwordrequirements.force2factor === true)) { features += 0x00040000; } // Force 2-factor auth
|
||||
if ((domain.auth == 'sspi') || (domain.auth == 'ldap')) { features += 0x00080000; } // LDAP or SSPI in use, warn that users must login first before adding a user to a group.
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user