oidc-lib
Version:
A library for creating OIDC Service Providers
1,346 lines (1,233 loc) • 105 kB
JavaScript
"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