@identity.com/verifiable-presentations
Version:
Utility Library to securely handle verifiable presentations
252 lines (251 loc) • 12.9 kB
JavaScript
;
var __assign = (this && this.__assign) || function () {
__assign = Object.assign || function(t) {
for (var s, i = 1, n = arguments.length; i < n; i++) {
s = arguments[i];
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
t[p] = s[p];
}
return t;
};
return __assign.apply(this, arguments);
};
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __generator = (this && this.__generator) || function (thisArg, body) {
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
function verb(n) { return function (v) { return step([n, v]); }; }
function step(op) {
if (f) throw new TypeError("Generator is already executing.");
while (_) try {
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
if (y = 0, t) op = [op[0] & 2, t.value];
switch (op[0]) {
case 0: case 1: t = op; break;
case 4: _.label++; return { value: op[1], done: false };
case 5: _.label++; y = op[1]; op = [0]; continue;
case 7: op = _.ops.pop(); _.trys.pop(); continue;
default:
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
if (t[2]) _.ops.pop();
_.trys.pop(); continue;
}
op = body.call(thisArg, _);
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
}
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.PIIFactory = void 0;
var R = __importStar(require("ramda"));
var uuid_1 = require("uuid");
var VerifiablePresentationManager_1 = require("./VerifiablePresentationManager");
var dsr_1 = __importDefault(require("@identity.com/dsr"));
var ScopeRequest = dsr_1.default.ScopeRequest;
/**
* Get the names of the evidence documents promised by a DSR response
*
* EvidenceProofs in a DSR response look like this:
* "evidenceProofs": [{
* "name": "credential-cvc:IdDocument-v2",
* "proofs": {
* "idDocumentFront": {
* "data": "195f9bf62b1a807abe26828c13f29e443169cdc5f60b22b470bfa50eef55a5a4"
* }
* }
* }]
* For each entry in the list of evidence proofs, extract the actual document names
* @return {Array<String>}
*/
var evidenceProofsToDSRDocumentNames = function (evidenceProofs) {
if (evidenceProofs === void 0) { evidenceProofs = []; }
return R.pipe(R.pluck('proofs'), R.map(Object.keys), R.flatten)(evidenceProofs);
};
/** returns the evidences document names from the provider DSR */
var dsrRequestedDocuments = function (dsrRequest) { return Object.keys(R.pathOr([], ['payload', 'channels', 'evidences'], dsrRequest)); };
/**
* Traverse the provided presentation credentials and creates a map of document proofs for each
* credential identifier
* @param {Array<Credential>} presentations: an array of credentials
* @param {Array<String>} requestedDocuments: documents that the DSR asks for
* @returns {Array}: e.g. {
* * name: 'credential-cvc:IdDocument-v2',
* * proofs: {
* * idDocumentFront: { data: '195f9bf62b1a807abe26828c13f29e443169cdc5f60b22b470bfa50eef55a5a4' },
* * }
* * }
*/
var evidenceProofsFromCredentials = function (presentations, requestedDocuments) {
if (requestedDocuments === void 0) { requestedDocuments = []; }
return presentations
.map(function (credential) {
var evidenceClaims = R.pathOr([], ['claim', 'document', 'evidences'], credential);
var filteredEvidenceClaims = R.pick(requestedDocuments, evidenceClaims);
return { name: credential.identifier, proofs: R.mergeAll(filteredEvidenceClaims) };
})
// only return evidence proofs for credentials that require evidence
.filter(function (evidenceProofsByCredential) { return R.not(R.isEmpty(evidenceProofsByCredential.proofs)); });
};
/**
* Return the list of evidence documents that the DSR response promises
* @param {Object} dsrResponse
* @param {Object} dsrRequest
*/
var expectedEvidenceProofs = function (dsrResponse, dsrRequest) {
var presentations = dsrResponse.verifiableData.map(R.prop('credential'));
return evidenceProofsFromCredentials(presentations, dsrRequestedDocuments(dsrRequest));
};
/**
* Add an upload url to an evidence channel, so the client knows where to send the evidence documents.
* The url will be generated by the client to be unique
* @param {Function} urlGeneratorFn: A function to generate a unique URL based on the evidence name provided
* @return {function(*, *): {url: *}}
*/
var addEvidenceUrl = function (urlGeneratorFn) { return function (evidenceChannelConfiguration, evidenceName) {
var url = urlGeneratorFn(evidenceName);
return __assign(__assign({}, evidenceChannelConfiguration), { url: url });
}; };
/**
* A class for extracting PII from a DSR Response based on a specific dsrRequest implementation, with a given mapping and formatters,
* specific to that DSR
*/
var PIIFactory = /** @class */ (function () {
/**
* @param {Object} dsrRequest
* @param {ClaimCriteriaMap} mapping
* @param {Formatters} formatters
*/
function PIIFactory(dsrRequest, mapping, formatters) {
this.dsrRequest = dsrRequest;
this.mapping = mapping;
this.formatters = formatters;
}
;
/**
* The client is asked for a list of documents that the provider is interested in (requestedDocuments)
* It returns in the credential, a list of documents that it can provide.
* Return the intersection between these two, to identify which documents are expected.
*
* @param evidenceProofs
* @return {string[]}
*/
PIIFactory.prototype.expectedDocumentsGivenEvidenceProofs = function (evidenceProofs) {
var promisedDocuments = evidenceProofsToDSRDocumentNames(evidenceProofs);
return R.intersection(dsrRequestedDocuments(this.dsrRequest), promisedDocuments);
};
;
/**
* Validate the dsr response, extract the PII, and format it for the provider.
* @param dsrResponse
* @return {Promise<{evidenceProofs: *, formattedClaims: *}>}
*/
PIIFactory.prototype.extractPII = function (dsrResponse) {
return __awaiter(this, void 0, void 0, function () {
var presentations, evidenceProofs, artifacts, verifiablePresentation, mappedClaimValues, formatIfFormatterExists, formattedClaims, error_1;
var _this = this;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
presentations = dsrResponse.verifiableData.map(R.prop('credential'));
evidenceProofs = expectedEvidenceProofs(dsrResponse, this.dsrRequest);
artifacts = {
presentations: presentations,
evidences: [],
};
_a.label = 1;
case 1:
_a.trys.push([1, 4, , 5]);
verifiablePresentation = new VerifiablePresentationManager_1.VerifiablePresentationManager({
skipAddVerify: true, skipGetVerify: true, allowGetUnverified: true,
});
// this throws an error if the DSR response is invalid
return [4 /*yield*/, verifiablePresentation.addCredentialArtifacts(artifacts)];
case 2:
// this throws an error if the DSR response is invalid
_a.sent();
return [4 /*yield*/, verifiablePresentation.mapClaimValues(this.mapping)];
case 3:
mappedClaimValues = _a.sent();
formatIfFormatterExists = function (value, key) { return (value && _this.formatters[key] ? _this.formatters[key].format(value) : value); };
formattedClaims = R.mapObjIndexed(formatIfFormatterExists, mappedClaimValues);
return [2 /*return*/, { formattedClaims: formattedClaims, evidenceProofs: evidenceProofs }];
case 4:
error_1 = _a.sent();
throw new Error('The dsr response on the requirements is invalid');
case 5: return [2 /*return*/];
}
});
});
};
;
/**
* Generate a DSR based on the template and evidence functions provided
* @param {String} eventsURL
* @param {String} idvDid
* @param {Object} dsrResolver
* @param {Function} urlGeneratorFn
*/
PIIFactory.prototype.generateDSR = function (eventsURL, idvDid, dsrResolver, urlGeneratorFn) {
return __awaiter(this, void 0, void 0, function () {
var uuid, requestedItems, updatedRequestedItems, evidenceChannelTemplate, evidences, channelsConfig, appConfig, scopeRequest;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
if (!this.dsrRequest) {
throw new Error('DSR not provided');
}
uuid = uuid_1.v4();
requestedItems = R.pathOr([], ['payload', 'credentialItems'], this.dsrRequest);
updatedRequestedItems = R.map(R.assocPath((['constraints', 'meta', 'issuer', 'is', '$eq']), idvDid))(requestedItems);
evidenceChannelTemplate = R.pathOr([], ['payload', 'channels', 'evidences'], this.dsrRequest);
evidences = R.mapObjIndexed(addEvidenceUrl(urlGeneratorFn), evidenceChannelTemplate);
channelsConfig = {
eventsURL: eventsURL,
evidences: evidences,
};
appConfig = R.pathOr([], ['payload', 'requesterInfo', 'app'], this.dsrRequest);
return [4 /*yield*/, ScopeRequest.ScopeRequest.create(uuid, updatedRequestedItems, channelsConfig, appConfig, dsrResolver)];
case 1:
scopeRequest = _a.sent();
return [2 /*return*/, ScopeRequest.buildSignedRequestBody(scopeRequest)];
}
});
});
};
;
return PIIFactory;
}());
exports.PIIFactory = PIIFactory;