UNPKG

oidc-lib

Version:

A library for creating OIDC Service Providers

1,346 lines (1,233 loc) 105 kB
"use strict" Object.defineProperty(exports, "__esModule", { value: true }); const HUBINSTANCEMONIKER = 'hub-instance-info'; const HUBFLOCKMONIKER = 'hub-flock-info'; const HUBFLOCKRECORD = 'hub-flock-record'; const FLOCKNOTPRESENT = 'flock-not-present'; const DIDNOTPRESENT = 'did-not-present'; module.exports = { FLOCKNOTPRESENT: FLOCKNOTPRESENT, HUBFLOCKMONIKER: HUBFLOCKMONIKER, HUBINSTANCEMONIKER: HUBINSTANCEMONIKER, authenticationBundleFactory: authenticationBundleFactory, backgroundSyncControllerFactory: backgroundSyncControllerFactory, decryptDbRecord: decryptDbRecord, deviceManagerFactory: deviceManagerFactory, encryptDbRecord: encryptDbRecord, flockMembershipFactory: flockMembershipFactory, hubExport: hubExport, hubRequest: hubRequest, loadHubInfo: loadHubInfo, registerEndpoints: registerEndpoints, simpleResolverFactory: simpleResolverFactory, syncInForeground: syncInForeground, updateHub: updateHub } // tell browserify we are running on client var window = {}; var pk = null; var hubConfig; function registerEndpoints(pkSource) { pk = pkSource; hubConfig = pk.util.config.hub; } const ECKey = require('ec-key'); var indexed_db_module = require('../claimer_sts/dbs/indexed_db'); var moduleName = 'hub_sw_mgmt'; var resolverCache = {}; ////////////////////////////////////////////////////////////////////////////////////// var fetch_func; if (typeof window === 'undefined'){ const node_fetch_1 = __importDefault(require("node-fetch")); fetch_func = node_fetch_1; } else { fetch_func = fetch_proxy; } function fetch_proxy(request, options){ return fetch (request, options); } function loadHubInfo(moniker, options, initializedPk){ var createIfAbsent = false; var generateDid = false; var delta = false; var currentKeyCount = 0; var currentKeyRoot = '#key-'; if (options){ if (options.createIfAbsent){ createIfAbsent = true; } if (options.generateDid){ generateDid = true; } } return new Promise((resolve, reject) => { var privateKeys = []; var updatedInfo; var hubInfo = new hubInfoFactory(moniker); hubInfo.load() .then(() => { if (hubInfo.privateKeys !== undefined){ return Promise.resolve(true); } else if (createIfAbsent === false){ return resolve(hubInfo); } else{ delta = true; var genParams; var keystore = pk.jose.JWK.createKeyStore(); var date = new Date(); var currentKeyId = currentKeyRoot + (currentKeyCount++).toString(); genParams = { alg: 'RS256', key_ops: ['sign', 'verify', 'wrapKey', 'unwrapKey', 'encrypt', 'decrypt'], kid: currentKeyId }; return keystore.generate("RSA", 2048, genParams); } }, err => { reject(err); }) .then( key => { if (key !== undefined){ if (key === true){ privateKeys = hubInfo.privateKeys; } else{ privateKeys.push(key.toJSON(true)); // Now add ECKey var currentKeyId = currentKeyRoot + (currentKeyCount++).toString(); var ecKeyPair = generateECKeyPair(currentKeyId); privateKeys.push(ecKeyPair.jwkPrivate); } if (hubInfo.instanceDid){ return hubInfo.instanceDids; } else{ if (generateDid === false){ return DIDNOTPRESENT; } else { if (initializedPk === undefined){ initializedPk = pk; } hubInfo.privateKeys = privateKeys; // TODO FIX: this is until hub update accepts long form or whatever hubInfo.didLocalOnly = true; delta = true; return genDid(initializedPk, privateKeys, hubInfo.selectKey({ kty: 'EC'}, true)); } } } }, function(err){ reject(err); }) .then(didOrDidForms => { if (didOrDidForms){ if (!delta){ resolve(hubInfo); return; } else{ var hOptions = { "privateKeys": privateKeys, "instanceDidForms": didOrDidForms, "flockDidForms": hubInfo.flockDidForms, "didLocalOnly": hubInfo.didLocalOnly } updatedInfo = new hubInfoFactory(moniker, hOptions); return updatedInfo.load(); } } }, err => { if (pk.util.config.hub.hackDid){ var didOrDidForms = '{"id":"did:ion:test:EiB5Y1dmdtVDXzwAZ-onv_eekR3ReTKzjhiVwyxHPBmKKA","idLong":"did:ion-test:eyJAY29udGV4dCI6Imh0dHBzOi8vdzNpZC5vcmcvZGlkL3YxIiwicHVibGljS2V5IjpbeyJpZCI6IiNrZXktMCIsInB1YmxpY0tleUp3ayI6eyJrdHkiOiJSU0EiLCJhbGciOiJSUzI1NiIsImtpZCI6IiNrZXktMCIsImUiOiJBUUFCIiwia2V5X29wcyI6WyJzaWduIiwidmVyaWZ5Iiwid3JhcEtleSIsInVud3JhcEtleSIsImVuY3J5cHQiLCJkZWNyeXB0Il0sIm4iOiIwMVRaTzF5TnRaVGd1cXh3YXJoTTdGczZSSTVtU2FwU3RKbG9tbnl1VXJCZm5zT2Q1dHlOUTlpbGhSaFA5bkx6T0RuamdaUnU2eUFPSGZzbGdsRUM4dlI5VHFvRUJURHZDSUo2UElnd2twbFNUUm5BYUhUMllWZkUzY0QtOVhfbm5MYml0S2dHSVd6YU9GekVIX21FbVVNd0tyWWZtRHZud3RJaXI1SnpUZnZsUlRITXhjazNPME4yT2hDNENhdDdNSEE1UVF4WXhqS1ZXVFh4am5WS195NEUwNnlwbWF3M2o3Ukl5S1pfOXhZbjVrdjVZcGs0VzZKTFpQRkhWX2o1RnM1UjE5V0laUXFNR2xNb3IybmoxQklhRFp3TUlmMW9YUExwMG5BaDAzUUxrUUFhVzNZbktxVEh6Z2RwSm81T3VkWlZQVkxIWWE0Nl83WXFBY1FWQ1EifSwidHlwZSI6IlJzYVZlcmlmaWNhdGlvbktleTIwMTgifSx7ImlkIjoiI2tleS0xIiwicHVibGljS2V5SndrIjp7Imt0eSI6IkVDIiwia2lkIjoiI2tleS0xIiwiZGVmYXVsdEVuY3J5cHRpb25BbGdvcml0aG0iOiJub25lIiwieCI6Ijc3djJlRXBMbHlfVEpXMC15Nks3NkliTU9vT3dCNVc4V1dvMUx0c21OTFkiLCJ5IjoicDgyNklFVHZrU0YxbV9pRFBrZlJKb2VGNGNQbWQ1WnplWGJqV2doMlZ3YyIsImNydiI6IlAtMjU2SyIsImtleV9vcHMiOlsic2lnbiIsInZlcmlmeSJdLCJkZWZhdWx0U2lnbkFsZ29yaXRobSI6IkVTMjU2SyJ9LCJ0eXBlIjoiU2VjcDI1NmsxVmVyaWZpY2F0aW9uS2V5MjAxOCJ9XSwic2VydmljZSI6W3siaWQiOiJJZGVudGl0eUh1YiIsInR5cGUiOiJJZGVudGl0eUh1YiIsInNlcnZpY2VFbmRwb2ludCI6eyJAY29udGV4dCI6InNjaGVtYS5pZGVudGl0eS5mb3VuZGF0aW9uL2h1YiIsIkB0eXBlIjoiVXNlclNlcnZpY2VFbmRwb2ludCIsImluc3RhbmNlIjpbImRpZDp0ZXN0Omh1Yi5pZCJdfX1dLCJpZCI6ImRpZDppb246dGVzdDpFaUI1WTFkbWR0VkRYendBWi1vbnZfZWVrUjNSZVRLempoaVZ3eXhIUEJtS0tBIn0"}'; var hOptions = { "privateKeys": privateKeys, "instanceDidForms": didOrDidForms, "flockDidForms": hubInfo.flockDidForms, "didLocalOnly": hubInfo.didLocalOnly } updatedInfo = new hubInfoFactory(moniker, hOptions); return updatedInfo.load(); } else{ reject(err); } }) .then(doUpdated => { if (doUpdated){ return updatedInfo.save(); } }, err => { reject(err); }) .then(updateSaved => { if (updateSaved){ resolve(updatedInfo); } }, err => { reject(err); }) }); } function simpleResolverFactory(discoveryEndpoint){ //TODO: the version should be in the config var _endpointPath = discoveryEndpoint + '/1.0/identifiers/'; var _resolveCache = {}; Object.defineProperty(this, "resolve", { value: function(did) { return new Promise((resolve, reject) => { if (!did){ reject('resolver invoked with undefined or empty did'); } var cacheHit = resolverCache[did]; if (cacheHit) { return resolve(cacheHit); } fetch_func(_endpointPath + did, { method: 'GET', headers: { 'Accept': '*/*' } }) .then(function(response){ return response.json(); }, function(err){ reject(err); }) .then(function(result){ if (result){ var didDocument = result.document; if (!didDocument.publicKey) { reject ('Could not find public keys for ' + recipient); return; } var jwk = didDocument.publicKey[0].publicKeyJwk; result.verify = cloneJwk(jwk, { key_ops: ['verify'], alg: 'RS256' }); result.encrypt = cloneJwk(jwk, { key_ops: ['encrypt'], alg: 'RSA-OAEP' }); resolverCache[did] = result; return resolve(result); } }, function(err){ reject(err); }) }) } }); } function shakespeareHandler(){ if ('caches' in window) { /* * Check if the service worker has already cached this city's weather * data. If the service worker has the data, then display the cached * data while the app fetches the latest data. */ caches.match(url).then(function(response) { if (response) { response.json().then(function updateFromCache(json) { var results = json.query.results; results.key = key; results.label = label; results.created = json.query.created; app.updateForecastCard(results); }); } }); } } function cloneJwk(jwk, delta){ var clone = {}; for(var prop in jwk){ if (delta[prop] === undefined){ clone[prop] = jwk[prop]; } } for (var prop in delta){ clone[prop] = delta[prop]; } return clone; } function toPublic(jwk){ var publicKey; switch (jwk.kty){ case 'RSA': publicKey = { kty: jwk.kty, alg: jwk.alg, kid: jwk.kid, e: jwk.e, key_ops: jwk.key_ops, n: jwk.n } break; case "EC": if (jwk.crv !=='P-256K'){ throw 'toPublic only supports EC with crv of P-256K'; } publicKey = { kty: jwk.kty, alg: jwk.alg, kid: jwk.kid, defaultEncryptionAlgorithm: jwk.defaultEncryptionAlgorithm, x: jwk.x, y: jwk.y, crv: jwk.crv, key_ops: jwk.key_ops, defaultSignAlgorithm: jwk.defaultSignAlgorithm } break; default: throw 'toPublic only supports RSA'; } return publicKey; } function backgroundSyncControllerFactory(){ const SYNC_TIMER_INTERLUDE = 2; // interval between sync of collections in seconds const RESYNC_INTERLUDE = 4 * 3600; // resync interlude in seconds const UPDATE_HUB_INTERLUDE = 60; const _resyncThreshold = (RESYNC_INTERLUDE) / SYNC_TIMER_INTERLUDE; // 30 => 5 minutes const _beginningDelay = (20 / SYNC_TIMER_INTERLUDE) + 1; const _updateHubDelay = (UPDATE_HUB_INTERLUDE/SYNC_TIMER_INTERLUDE) + 1; const MODE_SYNCHRONIZING = 1; const MODE_WAITING = 0; var _syncQueue = []; var _appRegistrations = {}; var _syncIndex = 0; var _interval = 0; var _timer; var _mode = MODE_WAITING; var _resyncCounter = 0; var _updateHubThreshold = _resyncThreshold; var _checkForDeltas = false; var _inTick = false; var tempCounter = 0; Object.defineProperty(this, "syncQueue", { get: function() { return _syncQueue; }, enumerable: true }); Object.defineProperty(this, "registerAppCallback", { value: function(collectionName, callback) { _appRegistrations[collectionName] = true; } }); Object.defineProperty(this, "begin", { value: function() { _syncIndex = 0; _interval = SYNC_TIMER_INTERLUDE * 1000; _timer = setInterval(tick, _interval); _resyncCounter = _resyncThreshold - _beginningDelay; } }); function tick(){ var syncStart; var syncEnd; var syncDuration; var taskId; var task; var syncConfigPromise; if (tempCounter++ === 0){ pk.util.masterNotification('hub notification test'); } if (pk.util.offline || pk.util.hubInstance.flockDid === FLOCKNOTPRESENT){ return; } var hubDbInfo = pk.dbs['sts']; if (!hubDbInfo || !hubDbInfo.authenticationBundle || hubDbInfo.authenticationBundle.flockDid === FLOCKNOTPRESENT){ return; } if (_inTick){ return; } else{ _inTick = true; } // pk.util.log_debug('tick info', _mode, _resyncCounter, _resyncThreshold, _updateHubThreshold); if (_mode === MODE_WAITING){ if (_resyncCounter < _resyncThreshold){ if (_updateHubThreshold === 0 || _resyncCounter++ < _updateHubThreshold){ _inTick = false; return; } else{ _updateHubThreshold = 0; _syncQueue = []; _syncIndex = 0; for (var key in pk.dbs){ var dbInfo = pk.dbs[key]; for (var collectionName in dbInfo.collections){ var collectionInfo = dbInfo.collections[collectionName]; if (collectionInfo.highWater > 0){ _syncQueue.push({db: key, collection: collectionName, status: null, syncDuration: 0}); collectionInfo.highWater = 0; } } } if (_syncQueue.length > 0){ syncConfigPromise = Promise.resolve('SyncLocalChanges'); _mode = MODE_SYNCHRONIZING; } else{ var regInfo = { flockDid: pk.util.hubInstance.flockDid, instanceDid: pk.util.hubInstance.instanceDid }; syncConfigPromise = syncStateReceiver(regInfo); } } } else{ _syncQueue = []; _syncIndex = 0; for (var key in pk.dbs){ var dbInfo = pk.dbs[key]; for (var collectionName in dbInfo.collections){ _syncQueue.push({db: key, collection: collectionName, status: null, syncDuration: 0}); } } _resyncCounter = 0; syncConfigPromise = Promise.resolve('SyncAllCollections') _mode = MODE_SYNCHRONIZING; } } else{ syncConfigPromise = Promise.resolve('SyncInProgress'); } syncConfigPromise.then(syncControl => { if (syncControl){ if (typeof syncControl === 'object'){ if (syncControl.delta){ for (var key in pk.dbs){ var dbInfo = pk.dbs[key]; for (var collectionName in dbInfo.collections){ if (syncControl.delta[collectionName] !== undefined){ _syncQueue.push({db: key, collection: collectionName, status: null, syncDuration: 0}); } } } if (_syncQueue.length > 0){ syncControl = 'SyncRemoteDeltas'; _mode = MODE_SYNCHRONIZING; } else { _updateHubThreshold = _resyncCounter + _updateHubDelay; return; } } } return syncControl; } }, err => { reject(err); }) .then(processSync => { if (processSync){ task = _syncQueue[_syncIndex]; taskId = _syncIndex; syncStart = new Date().getTime(); var registered = _appRegistrations[_syncQueue[taskId].collection]; return syncCollection(task, registered); } }, err => { reject(err); }) .then(result => { if (typeof result === 'number'){ syncEnd = new Date().getTime(); syncDuration = syncEnd - syncStart; _syncQueue[taskId] = {db: task.db, collection: task.collection, status: result, syncDuration: syncDuration}; _syncIndex++; if (_syncIndex >= _syncQueue.length){ _mode = MODE_WAITING; var changeInfo = { flockDid: pk.util.hubInstance.flockDid, instanceDid: pk.util.hubInstance.instanceDid, changeInfo: { highWater: {} } }; for (var key in _syncQueue){ var info = _syncQueue[key]; changeInfo.changeInfo.highWater[info.collection] = info.status; } // sync cycle is complete pk.util.sync_info.last_sync_time = syncEnd; return syncStateSender(changeInfo); } } }, err => { syncEnd = new Date().getTime(); syncDuration = syncEnd - syncStart; _syncQueue[_syncIndex] = {db: task.db, collection: task.collection, status: err, syncDuration: syncDuration}; _mode = MODE_WAITING; }) .then(sendResult => { if (sendResult){ _updateHubThreshold = _resyncCounter + _updateHubDelay; } _inTick = false; }, err => { pk.util.log_detail("[ServiceWorker] Tick Error so increasing hub delay: ", err); _updateHubThreshold = _resyncCounter + (_updateHubDelay *10); _inTick = false; }) } function syncStateSender(stateObj){ return new Promise((resolve, reject) => { var requestString = JSON.stringify(stateObj); fetch_func(hubConfig.endpoints.changeNotifications, { method: 'POST', headers: { 'Content-Type': 'application/json', 'Content-Length': requestString.length.toString() }, body: requestString }) .then(result => { if (result){ if (result.status !== 200) { var msg = 'syncStateSender returns ' + result.status.toString() + ': ' + result.statusText; reject(msg); } else { resolve(result.json()); } } }, err => { reject(err); }) }); } function syncStateReceiver(stateObj){ return new Promise((resolve, reject) => { var uri = hubConfig.endpoints.changeNotifications + pk.util.createParameterString(stateObj); fetch_func(uri) .then(result => { if (result){ if (result.status !== 200) { var msg = '[ServiceWorker] syncStateReceiver returns ' + result.status.toString() + ': ' + result.statusText; pk.util.log_debug(msg); } else { return result.json(); } } }, err => { reject(err); pk.util.log_detail('[ServiceWorker] Error retrieving changeNotification', err); }) .then(syncResponse => { if (syncResponse){ resolve(syncResponse); } }, err => { reject(err); pk.util.log_detail('[ServiceWorker] Error retrieving syncResponse', err); }) }); } } function syncCollection(task, registered){ var hubDbInfo = pk.dbs[task.db]; var changePromises = []; var highWater = 0; return new Promise((resolve, reject) => { if (pk.util.offline || !hubDbInfo.authenticationBundle || hubDbInfo.authenticationBundle.instanceDid === undefined){ return resolve(true); } var collectionName = task.collection; var keyPath = hubDbInfo.collections[collectionName].keyPath; var hubCollection = {}; var localCollection = {}; hubDbInfo.provider.queryHubCollection(hubDbInfo, collectionName, {}, { decrypt: false }) .then(collectionElements => { for (var j=0; j < collectionElements.length; j++){ var hubElement = collectionElements[j]; hubCollection[hubElement.didMeta.object_id] = hubElement; var upd = Date.parse(hubElement.didMeta.updated_at); if (upd > highWater){ highWater = upd; } } return hubDbInfo.dbInfo.provider.queryCollection(hubDbInfo.dbInfo, collectionName, {}); }, err => { reject(err); }) .then(dbCollection => { var sync_id; var unsynced_count = 0; for (var j=0; j < dbCollection.length; j++){ var localElement = dbCollection[j]; var upd = Date.parse(localElement.didMeta.updated_at); if (upd > highWater){ highWater = upd; } if (localElement.didMeta.object_id === undefined){ sync_id = '_' + (unsynced_count++).toString(); } else{ sync_id = localElement.didMeta.object_id; } localCollection[sync_id] = localElement; } var localUpdatePromises = []; hubDbInfo.suppressHubUpdate = true; for (var element in hubCollection){ var hubElement = hubCollection[element]; var localElement = localCollection[element]; if (localElement === undefined || Date.parse(localElement.didMeta.updated_at) < Date.parse(hubElement.didMeta.updated_at)){ if (registered){ changePromises.push(decryptDbRecord(hubDbInfo, collectionName, hubElement)); } localUpdatePromises.push(hubDbInfo.dbInfo.provider.createOrUpdateDocument(hubDbInfo.dbInfo, collectionName, hubElement)); } } return Promise.all(localUpdatePromises); }, err => { reject(err); }) .then(localUpdateResults => { hubDbInfo.suppressHubUpdate = false; var hubUpdatePromises = [] for (var elementId in localCollection){ var element = localCollection[elementId]; if (element.didMeta.object_id === undefined){ if (registered){ changePromises.push(decryptDbRecord(hubDbInfo, collectionName, element)); } hubUpdatePromises.push(updateHub(hubDbInfo, collectionName, element, { encrypt: false })); } } return Promise.all(hubUpdatePromises); }, err => { hubDbInfo.suppressHubUpdate = false; reject(err); }) .then(hubUpdateResults => { if (hubUpdateResults){ return Promise.all(changePromises) } }, err => { reject(err); }) .then(changes => { if (changes){ if (registered && changes.length > 0){ var payload = { action: 'syncChange', changes: changes, } pk.util.send_message_to_all_clients(payload); } resolve(highWater); } }, err => { reject(err); }) }); } function hubInfoFactory(personaId, options){ var _personaId = personaId; var _didMode, _privateKeys, _suppressHubUpdate, _didLocalOnly, _instanceDidForms, _flockDidForms; if (options === undefined){ options = {}; } _didMode = options.didMode | 'id'; _instanceDidForms = setDidForm(options.instanceDidForms); _flockDidForms = setDidForm(options.flockDidForms); _privateKeys = options.privateKeys; _suppressHubUpdate = options.suppressHubUpdate; _didLocalOnly = options.didLocalOnly; var _kidRoot, _signKey, _verifyKey, _encryptKey, _decryptKey; Object.defineProperty(this, "load", { value: function() { return new Promise((resolve, reject) => { const use_new_key = '**USENEWKEY**'; var storagePromise; if (_privateKeys){ storagePromise = Promise.resolve(use_new_key); } else{ storagePromise = pk.util.local_store.get(personaId, 'utf-8'); } storagePromise .then(instanceState => { if (instanceState === undefined){ // no previous storage resolve(); return; } else if (instanceState === use_new_key){ return true; } else { var hubInstanceStorage = JSON.parse(instanceState); _didMode = hubInstanceStorage.didMode; _instanceDidForms = hubInstanceStorage.instanceDidForms; _flockDidForms = hubInstanceStorage.flockDidForms; _didLocalOnly = hubInstanceStorage.didLocalOnly; _privateKeys = hubInstanceStorage.privateKeys; return true; } }, err => { reject(err); }) .then(setUpKeys => { if (setUpKeys){ _kidRoot = this.instanceDid; // set up _privateSigningKey var jwk = _privateKeys[0]; var instanceKid = _kidRoot + jwk.kid; _signKey = cloneJwk(jwk, { alg: 'RS256', key_ops: ["sign"], kid: instanceKid }) _verifyKey = cloneJwk(toPublic(jwk), { alg: 'RS256', key_ops: ["verify"], kid: instanceKid }); _decryptKey = cloneJwk(jwk, { alg: 'RSA-OAEP', key_ops: ["decrypt"], kid: instanceKid }) _encryptKey = cloneJwk(toPublic(jwk), { alg: 'RSA-OAEP', key_ops: ["encrypt"], kid: instanceKid }); resolve(true); } }, err => { reject(err); }) }) } }); Object.defineProperty(this, "selectKey", { value: function(selector, isPrivate, overRide) { for (var i=0; i < _privateKeys.length; i++){ var candidate = _privateKeys[i]; var ok = true; for (var prop in selector){ if (selector[prop] !== candidate[prop]){ ok = false; break; } } if (ok){ var jwk; if (isPrivate === true){ jwk = candidate; } else{ jwk = toPublic(jwk); } if (overRide){ jwk = cloneJwk(jwk, overRide); } return jwk; } } } }); Object.defineProperty(this, "personaId", { get: function() { return _personaId; }, enumerable: true }); Object.defineProperty(this, "instanceDid", { get: function() { return _instanceDidForms[_didMode] || _instanceDidForms.id; }, set: function(value) { _instanceDidForms[_didMode] = value; }, enumerable: true }); Object.defineProperty(this, "instanceDids", { get: function() { return _instanceDidForms; }, set: function(value) { _instanceDidForms = value; }, enumerable: true }); Object.defineProperty(this, "flockDid", { get: function() { return _flockDidForms[_didMode] || FLOCKNOTPRESENT; }, set: function(value) { _flockDidForms[_didMode] = value; }, enumerable: true }); Object.defineProperty(this, "flockDids", { get: function() { return _flockDidForms; }, set: function(value) { _flockDidForms = value; }, enumerable: true }); Object.defineProperty(this, "didMode", { get: function() { return _didMode; }, set: function(value) { _didMode = value; }, enumerable: true }); Object.defineProperty(this, "didLocalOnly", { get: function() { return _didLocalOnly; }, set: function(value) { _didLocalOnly = value; }, enumerable: true }); Object.defineProperty(this, "privateKeys", { get: function() { return _privateKeys; }, set: function(value) { _privateKeys = value; }, enumerable: false }); Object.defineProperty(this, "signKey", { get: function() { return _signKey; }, enumerable: false }); Object.defineProperty(this, "verifyKey", { get: function() { return _verifyKey; }, enumerable: false }); Object.defineProperty(this, "decryptKey", { get: function() { return _decryptKey; }, enumerable: false }); Object.defineProperty(this, "encryptKey", { get: function() { return _encryptKey; }, enumerable: false }); Object.defineProperty(this, "save", { value: function(changedPersonaId) { return new Promise((resolve, reject) => { if (changedPersonaId){ _personaId = changedPersonaId; } var saveBundle = { didMode: _didMode, didLocalOnly: _didLocalOnly, instanceDidForms: _instanceDidForms, flockDidForms: _flockDidForms, personaId: _personaId, privateKeys: _privateKeys } pk.util.local_store.set(_personaId, JSON.stringify(saveBundle)) .then(result => { if (result){ resolve(true); } }, err => { reject(err); }) }) } }); function setDidForm(value){ var retVal = {}; if (value){ if (typeof value === 'string'){ retVal[_didMode] = value; } else{ retVal = value; } } return retVal; } } ///////////////////////////////////////////////////////// function hubRequest(hubMessage, authenticationBundle, hubDid, caller){ return new Promise((resolve, reject) => { var hubMessageString = JSON.stringify(hubMessage); var hubRequestor = new hubRequestorFactory(); hubRequestor.execute(hubMessageString, authenticationBundle, hubDid) .then(function(responseString){ if (responseString){ if (caller === undefined){ caller = 'caller unspecified'; } var response = JSON.parse(responseString); if (response && response['@type'] === 'ErrorResponse'){ var msg = 'Hub comms error [' + caller + '] (' + response.error_code; if (response.target){ msg += ' - target: ' + response.target; } if (response.inner_error){ msg += ' inner_error: ' + JSON.stringify(response.inner_error); } msg += ')'; if (response.developer_message){ msg += ' - ' + response.developer_message; } hubRequestor.log(hubMessage, caller, msg) reject(msg); } else{ // hubRequestor.log(hubMessage, caller) resolve(response); } } }, function(err){ pk.util.log_debug('hubRequestError', hubMessage); reject(err); }) }); } function hubRequestorFactory() { var _decryptKey; Object.defineProperty(this, "execute", { value: function(message, authenticationBundle, partnerDid) { return new Promise((resolve, reject) => { _decryptKey = authenticationBundle.decryptKey; /* // access_token no longer used // authenticationBundle.getAccessToken(partnerDid) // .then(accessToken => { // if (accessToken){ // return getAuthenticatedRequest(message, authenticationBundle.signKey, partnerDid, accessToken); // } // }, err => { // reject(err); // }) */ getAuthenticatedRequest(message, authenticationBundle.signKey, partnerDid) .then(authenticatedRequest => { return completeRequest(authenticatedRequest, partnerDid); }, err => { reject(err); }) .then(verifiedPayload => { resolve(verifiedPayload); }, err => { reject(err); }) }) } }); Object.defineProperty(this, "getAccessToken", { value: function(instanceInfo, partnerDid) { return new Promise((resolve, reject) => { _decryptKey= instanceInfo.decryptKey; getAuthenticatedRequest('', instanceInfo.signKey, partnerDid, null) .then(authenticatedRequest => { return completeRequest(authenticatedRequest, partnerDid); }, err => { reject(err); }) .then(verifiedPayload => { resolve(verifiedPayload); }, err => { reject(err); }) }); } }); Object.defineProperty(this, "log", { value: function(hubMessage, caller, err) { if (err){ pk.util.log_debug('*********** Hub Error ***********'); pk.util.log_debug(err); } else{ pk.util.log_debug('Success: ' + caller); } var dispMsg = {}; for (var key in hubMessage){ if (key === 'commit'){ dispMsg.payload = JSON.parse(pk.base64url.decode(hubMessage.commit.payload)); dispMsg.protected = JSON.parse(pk.base64url.decode(hubMessage.commit.protected)); } else{ dispMsg[key] = hubMessage[key]; } } pk.util.log_debug(dispMsg); pk.util.log_debug(); } }); function completeRequest(requestString, partnerDid){ return new Promise((resolve, reject) => { fetch_func(hubConfig.endpoints.hub, { method: 'POST', headers: { 'Content-Type': 'application/jose', // 'Content-Type': 'text/plain;charset=utf-8', 'Content-Length': requestString.length.toString() }, body: requestString }) .then(function(res){ if (res){ if (res.status !== 200) { var msg = 'hubRequestor returns ' + res.status.toString() + ': ' + res.statusText; reject(new Error(msg)); return; } else { return res.text(); } } }, function(err){ reject(new Error('Error posting hubRequestor', err)); }) .then(function(response){ if (response){ return getVerifiedResponse(_decryptKey, response, partnerDid, false); } }, function(err){ reject(new Error('Error extracting json from getAuthentication response')); }) .then(function(verifiedRequestResponse){ if (verifiedRequestResponse){ resolve(verifiedRequestResponse.payload); } }, function(err){ reject(new Error('Error in getVerifiedResponse response')); }) }); }; } function getAuthenticatedRequest(content, privateSigningKey, recipient, accessToken) { var nonce, didDocument, recipientPublicKey, jwsHeaderParameters; return new Promise((resolve, reject) => { pk.simple_crypto.randomString() .then(random => { nonce = random; return pk.util.simpleResolver.resolve(recipient); }) .then(result => { if (result){ return pk.jose.JWK.asKey(result.encrypt, 'json'); } }, err => { reject(err); }) .then(recipientKey => { recipientPublicKey = recipientKey; jwsHeaderParameters = { 'did-requester-nonce': nonce }; /* if (accessToken) { jwsHeaderParameters['did-access-token'] = accessToken; } */ return pk.jose.JWS.createSign({ format: 'compact', fields: jwsHeaderParameters }, privateSigningKey).update(content, "utf8").final(); }, err => { reject(err); }) .then(jwt => { // return pk.jose.JWE.createEncrypt({ format: 'compact', "contentAlg":"A128GCM" }, recipientPublicKey).update(jwt).final(); return pk.jose.JWE.createEncrypt({ format: 'compact', "alg":"RSA-OAEP", "contentAlg": "A128GCM" }, recipientPublicKey).update(jwt).final(); }, err => { reject(err); }) .then(jwe => { resolve(jwe); }, err => { reject(err); }) }); } function getVerifiedResponse(decryptKey, protectedMessage, responderDid, accessTokenCheck = true){ return new Promise((resolve, reject) => { var responderKeystore = pk.jose.JWK.createKeyStore(); var hubInstanceKeystore = pk.jose.JWK.createKeyStore(); hubInstanceKeystore.add(decryptKey) .then(function(){ return pk.util.simpleResolver.resolve(responderDid); }, function(err){ reject('Error adding hubJwk to keystore'); }) .then(result => { if (result){ return responderKeystore.add(result.verify); } }, err => { reject('Error resolving responderDid: ' + responderDid); }) .then(addedKey => { if (addedKey){ return pk.jose.JWE.createDecrypt(hubInstanceKeystore).decrypt(protectedMessage); } }, function(err){ reject('Error adding verifier to responder keystore'); }) .then(result => { if (result){ var jws = Buffer(result.plaintext).toString('utf8'); return pk.jose.JWS.createVerify(responderKeystore).verify(jws); } }, err => { reject(err); }) .then(result => { if (result){ var protectedHeader = {}; for (var i=0, v=result.protected; i < v.length; i++){ var key = v[i]; protectedHeader[key] = result.header[key]; } var payload = Buffer(result.payload).toString('utf8'); resolve({ protectedHeader: protectedHeader, payload: payload}); } }) }); } function updateHub(hubDbInfo, collectionName, objectToWrite, options){ return new Promise((resolve, reject) => { var record; var didMeta; var updateISO; var operation; var hub_payload; var collectionInfo; var messageTransmitted; var hubToAccess; var sig; encryptDbRecord(hubDbInfo, collectionName, objectToWrite, options) .then(result => { if (result){ record = result; collectionInfo = hubDbInfo.collections[collectionName]; var date = new Date(); updateISO = date.toISOString(); var time = Date.parse(updateISO); if (collectionInfo.highWater < time){ collectionInfo.highWater = time; } didMeta = record.didMeta; if (didMeta === undefined){ didMeta = {}; } if (hubDbInfo.didLocalOnly || hubDbInfo.suppressHubUpdate || pk.util.offline || !hubDbInfo.authenticationBundle){ if (didMeta.created_at === undefined){ didMeta.created_at = updateISO; } record.didMeta = didMeta; return hubDbInfo.dbInfo.provider.createOrUpdateDocument(hubDbInfo.dbInfo, collectionName, record) } else{ return true; } } }, err => { reject(err); }) .then(result => { if (typeof result === 'object'){ objectToWrite.didMeta = result.didMeta; resolve(objectToWrite); } else if (result === true){ hub_payload = { '@context': collectionInfo.hubContext, '@type': collectionInfo.hubType, 'didMeta': didMeta }; var suppressKeys = ['didMeta']; for (var key in record){ if (suppressKeys.indexOf(key) < 0){ hub_payload[key] = record[key]; } } if (didMeta.object_id === undefined){ operation = 'create'; if (didMeta.created_at === undefined){ didMeta.created_at = updateISO; } else{ // this object was created locally // but has not yet been submitted updateISO = didMeta.created_at; } } else{ operation = 'update'; } // if a flock has been set up, the // hubToAccess is that of the flock, otherwise // it is that of the devce instance if (hubDbInfo.authenticationBundle.flockDid === FLOCKNOTPRESENT){ hubToAccess = hubDbInfo.authenticationBundle.instanceDid; } else { hubToAccess = hubDbInfo.authenticationBundle.flockDid; } var hub_protected = { alg: hubDbInfo.authenticationBundle.signKey.alg, interface: collectionInfo.hubInterface, context: collectionInfo.hubContext, type: collectionInfo.hubType, operation: operation, committed_at: updateISO, commit_strategy: "basic", sub: hubToAccess, kid: hubDbInfo.authenticationBundle.signKey.kid, }; if (didMeta.hubMeta){ hub_protected.meta = didMeta.hubMeta; } if (operation === 'update'){ hub_protected.object_id = didMeta.object_id; } var payloadString = JSON.stringify(hub_payload); return pk.jose.JWS.createSign({ format: 'flattened', fields: hub_protected }, hubDbInfo.authenticationBundle.signKey).update(payloadString).final(); } }, err => { reject(err); }) .then(jws => { if (jws){ sig = jws; return pk.simple_crypto.digestSha256(sig.protected + '.' + sig.payload); } }, err => { reject(err); }) .then(rev => { if (rev){ messageTransmitted = { "@context": "https://schema.identity.foundation/0.1", "@type": "WriteRequest", "iss": hubDbInfo.authenticationBundle.instanceDid, "aud": hubConfig.did, "sub": hubToAccess, "commit": { "protected": sig.protected, "payload": sig.payload, "signature": sig.signature, "header": { "rev": rev, "iss": hubDbInfo.authenticationBundle.instanceDid } } }; return hubRequest(messageTransmitted, hubDbInfo.authenticationBundle, hubConfig.did, 'updateHub'); } }, err => { reject(err); }) .then(function(response){ if (response){ switch (response['@type']){ case 'WriteResponse': if (record.didMeta === undefined){ record.didMeta = {}; } if (operation === 'create'){ hub_payload.didMeta.object_id = respon