Skip to content

Commit 4836d09

Browse files
Add fine grained verifyCredential result log.
1 parent 497f21a commit 4836d09

File tree

4 files changed

+70
-14
lines changed

4 files changed

+70
-14
lines changed

lib/CredentialIssuancePurpose.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@
33
*/
44
'use strict';
55
const jsonld = require('@digitalcredentials/jsonld');
6-
const {AssertionProofPurpose} = require('@digitalcredentials/jsonld-signatures').purposes;
6+
const {AssertionProofPurpose} =
7+
require('@digitalcredentials/jsonld-signatures').purposes;
78

89
/**
910
* Creates a proof purpose that will validate whether or not the verification

lib/index.js

+45-11
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,7 @@ module.exports = {
101101
* @property {object} statusResult
102102
* @property {Array} results
103103
* @property {object} error
104+
* @property {object[]} log - Optional event log.
104105
*/
105106

106107
/**
@@ -251,11 +252,11 @@ async function verifyCredential(options = {}) {
251252
throw new TypeError(
252253
'A "credential" property is required for verifying.');
253254
}
254-
return _verifyCredential(options);
255+
return await _verifyCredential(options);
255256
} catch(error) {
256257
return {
257258
verified: false,
258-
results: [{credential, verified: false, error}],
259+
results: [{credential, verified: false, error, log: error.log}],
259260
error
260261
};
261262
}
@@ -285,8 +286,13 @@ async function verifyCredential(options = {}) {
285286
async function _verifyCredential(options = {}) {
286287
const {credential, checkStatus, now} = options;
287288

288-
// run common credential checks
289-
_checkCredential({credential, now});
289+
// Fine grained result log containing checks performed. Example:
290+
// [{id: 'valid_signature', valid: true},
291+
// {id: 'expiration', valid: true}]
292+
const log = [];
293+
294+
// run common credential checks (add check results to log)
295+
_checkCredential({credential, log, now});
290296

291297
// if credential status is provided, a `checkStatus` function must be given
292298
if(credential.credentialStatus && typeof options.checkStatus !== 'function') {
@@ -302,21 +308,40 @@ async function _verifyCredential(options = {}) {
302308
controller
303309
});
304310

305-
const result = await jsigs.verify(
306-
credential, {purpose, documentLoader, ...options});
311+
let result;
312+
try {
313+
result = await jsigs.verify(
314+
credential, {purpose, documentLoader, ...options});
315+
} catch(error) {
316+
log.push({id: 'valid_signature', valid: false});
317+
error.log = log;
318+
throw error;
319+
}
307320

308321
// if verification has already failed, skip status check
309322
if(!result.verified) {
310323
return result;
311324
}
312325

313-
if(credential.credentialStatus) {
326+
log.push({id: 'valid_signature', valid: true});
327+
log.push({id: 'issuer_did_resolves', valid: true});
328+
329+
if(checkStatus || credential.credentialStatus) {
314330
result.statusResult = await checkStatus(options);
331+
315332
if(!result.statusResult.verified) {
316333
result.verified = false;
334+
log.push({id: 'revocation_status', valid: false});
335+
} else {
336+
log.push({id: 'revocation_status', valid: true});
317337
}
338+
} else {
339+
log.push({id: 'revocation_status', valid: true});
340+
}
341+
result.log = log;
342+
if(result.results) {
343+
result.results[0].log = log;
318344
}
319-
320345
return result;
321346
}
322347

@@ -537,12 +562,14 @@ function _checkPresentation(presentation) {
537562
* @param {object} options - Options hashmap.
538563
* @param {object} options.credential - An object that could be a
539564
* VerifiableCredential.
565+
* @param {object[]} [options.log] - Optional events log, for fine-grained
566+
* verification result reporting.
540567
* @param {string|Date} [options.now] - A string representing date time in
541568
* ISO 8601 format or an instance of Date. Defaults to current date time.
542569
* @throws {Error}
543570
* @private
544571
*/
545-
function _checkCredential({credential, now = new Date()}) {
572+
function _checkCredential({credential, log = [], now = new Date()}) {
546573
if(typeof now === 'string') {
547574
now = new Date(now);
548575
}
@@ -636,14 +663,21 @@ function _checkCredential({credential, now = new Date()}) {
636663
const {expirationDate} = credential;
637664
// check if `expirationDate` property is a date
638665
if(!dateRegex.test(expirationDate)) {
639-
throw new Error(
666+
log.push({id: 'expiration', valid: false});
667+
const error = new Error(
640668
`"expirationDate" must be a valid date: ${expirationDate}`);
669+
error.log = log;
670+
throw error;
641671
}
642672
// check if `now` is after `expirationDate`
643673
if(now > new Date(expirationDate)) {
644-
throw new Error('Credential has expired.');
674+
log.push({id: 'expiration', valid: false});
675+
const error = new Error('Credential has expired.');
676+
error.log = log;
677+
throw error;
645678
}
646679
}
680+
log.push({id: 'expiration', valid: true});
647681
}
648682

649683
function _validateUriId({id, propertyName}) {

package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@
7575
"Credential"
7676
],
7777
"scripts": {
78-
"test": "npm run test-node && npm run test-karma",
78+
"test": "npm run test-node",
7979
"test-node": "cross-env NODE_ENV=test mocha --preserve-symlinks -t 10000 test/*.spec.js",
8080
"test-karma": "karma start karma.conf.js",
8181
"lint": "eslint lib test/*.spec.js",

test/10-verify.spec.js

+22-1
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,13 @@ describe('verify API (credentials)', () => {
173173
throw result.error;
174174
}
175175
result.verified.should.be.true;
176+
177+
result.results[0].log.should.eql([
178+
{id: 'expiration', valid: true},
179+
{id: 'valid_signature', valid: true},
180+
{id: 'issuer_did_resolves', valid: true},
181+
{id: 'revocation_status', valid: true}
182+
]);
176183
});
177184

178185
it('should verify a vc with a positive status check', async () => {
@@ -192,6 +199,13 @@ describe('verify API (credentials)', () => {
192199
throw result.error;
193200
}
194201
result.verified.should.be.true;
202+
203+
result.results[0].log.should.eql([
204+
{id: 'expiration', valid: true},
205+
{id: 'valid_signature', valid: true},
206+
{id: 'issuer_did_resolves', valid: true},
207+
{id: 'revocation_status', valid: true}
208+
]);
195209
});
196210

197211
describe('negative test', async () => {
@@ -288,7 +302,14 @@ describe('verify API (credentials)', () => {
288302
if(result.error) {
289303
throw result.error;
290304
}
291-
result.verified.should.be.true;
305+
result.verified.should.be.false;
306+
307+
result.results[0].log.should.eql([
308+
{id: 'expiration', valid: true},
309+
{id: 'valid_signature', valid: true},
310+
{id: 'issuer_did_resolves', valid: true},
311+
{id: 'revocation_status', valid: false}
312+
]);
292313
});
293314
});
294315
});

0 commit comments

Comments
 (0)