@bedrock/vc-verifier 6.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.eslintrc.cjs +12 -0
- package/.github/workflows/main.yml +77 -0
- package/CHANGELOG.md +123 -0
- package/LICENSE.md +115 -0
- package/README.md +126 -0
- package/lib/challenges.js +199 -0
- package/lib/config.js +41 -0
- package/lib/documentLoader.js +83 -0
- package/lib/http.js +284 -0
- package/lib/index.js +55 -0
- package/lib/status.js +52 -0
- package/package.json +62 -0
- package/schemas/bedrock-vc-verifier.js +59 -0
- package/test/mocha/.eslintrc.cjs +9 -0
- package/test/mocha/10-provision.js +868 -0
- package/test/mocha/20-verify.js +392 -0
- package/test/mocha/30-credential-status.js +488 -0
- package/test/mocha/cert.pem +18 -0
- package/test/mocha/helpers.js +230 -0
- package/test/mocha/key.pem +28 -0
- package/test/mocha/mock-credential.json +39 -0
- package/test/mocha/mock.data.js +21 -0
- package/test/package.json +72 -0
- package/test/test.config.js +40 -0
- package/test/test.js +40 -0
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
* Copyright (c) 2019-2022 Digital Bazaar, Inc. All rights reserved.
|
|
3
|
+
*/
|
|
4
|
+
import * as bedrock from '@bedrock/core';
|
|
5
|
+
import '@bedrock/credentials-context';
|
|
6
|
+
import '@bedrock/did-context';
|
|
7
|
+
import '@bedrock/did-io';
|
|
8
|
+
import '@bedrock/security-context';
|
|
9
|
+
import '@bedrock/vc-status-list-context';
|
|
10
|
+
import '@bedrock/vc-revocation-list-context';
|
|
11
|
+
import '@bedrock/veres-one-context';
|
|
12
|
+
import {createContextDocumentLoader} from '@bedrock/service-context-store';
|
|
13
|
+
import {didIo} from '@bedrock/did-io';
|
|
14
|
+
import {
|
|
15
|
+
documentLoader as brDocLoader,
|
|
16
|
+
httpClientHandler,
|
|
17
|
+
JsonLdDocumentLoader
|
|
18
|
+
} from '@bedrock/jsonld-document-loader';
|
|
19
|
+
|
|
20
|
+
const serviceType = 'vc-verifier';
|
|
21
|
+
let webLoader;
|
|
22
|
+
|
|
23
|
+
bedrock.events.on('bedrock.init', () => {
|
|
24
|
+
// build web loader if configuration calls for it
|
|
25
|
+
const cfg = bedrock.config['vc-verifier'];
|
|
26
|
+
if(cfg.documentLoader.http || cfg.documentLoader.https) {
|
|
27
|
+
const jdl = new JsonLdDocumentLoader();
|
|
28
|
+
|
|
29
|
+
if(cfg.documentLoader.http) {
|
|
30
|
+
jdl.setProtocolHandler({protocol: 'http', handler: httpClientHandler});
|
|
31
|
+
}
|
|
32
|
+
if(cfg.documentLoader.https) {
|
|
33
|
+
jdl.setProtocolHandler({protocol: 'https', handler: httpClientHandler});
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
webLoader = jdl.build();
|
|
37
|
+
}
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Creates a document loader for the verifier instance identified via the
|
|
42
|
+
* given config.
|
|
43
|
+
*
|
|
44
|
+
* @param {object} options - The options to use.
|
|
45
|
+
* @param {object} options.config - The verifier instance config.
|
|
46
|
+
*
|
|
47
|
+
* @returns {Promise<Function>} The document loader.
|
|
48
|
+
*/
|
|
49
|
+
export async function createDocumentLoader({config} = {}) {
|
|
50
|
+
const contextDocumentLoader = await createContextDocumentLoader(
|
|
51
|
+
{config, serviceType});
|
|
52
|
+
|
|
53
|
+
return async function documentLoader(url) {
|
|
54
|
+
// resolve all DID URLs through did-io
|
|
55
|
+
if(url.startsWith('did:')) {
|
|
56
|
+
const document = await didIo.get({url});
|
|
57
|
+
return {
|
|
58
|
+
contextUrl: null,
|
|
59
|
+
documentUrl: url,
|
|
60
|
+
document
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
try {
|
|
65
|
+
// try to resolve URL through built-in doc loader
|
|
66
|
+
return await brDocLoader(url);
|
|
67
|
+
} catch(e) {
|
|
68
|
+
// FIXME: improve to check for `NotFoundError` once `e.name`
|
|
69
|
+
// supports it
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
try {
|
|
73
|
+
// try to resolve URL through context doc loader
|
|
74
|
+
return await contextDocumentLoader(url);
|
|
75
|
+
} catch(e) {
|
|
76
|
+
// use web loader if configured
|
|
77
|
+
if(url.startsWith('http') && e.name === 'NotFoundError' && webLoader) {
|
|
78
|
+
return webLoader(url);
|
|
79
|
+
}
|
|
80
|
+
throw e;
|
|
81
|
+
}
|
|
82
|
+
};
|
|
83
|
+
}
|
package/lib/http.js
ADDED
|
@@ -0,0 +1,284 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
* Copyright (c) 2018-2022 Digital Bazaar, Inc. All rights reserved.
|
|
3
|
+
*/
|
|
4
|
+
import * as bedrock from '@bedrock/core';
|
|
5
|
+
import {createChallenge, verifyChallenge} from './challenges.js';
|
|
6
|
+
import {asyncHandler} from '@bedrock/express';
|
|
7
|
+
import bodyParser from 'body-parser';
|
|
8
|
+
import {checkStatus} from './status.js';
|
|
9
|
+
import cors from 'cors';
|
|
10
|
+
import {
|
|
11
|
+
createChallengeBody,
|
|
12
|
+
verifyCredentialBody,
|
|
13
|
+
verifyPresentationBody
|
|
14
|
+
} from '../schemas/bedrock-vc-verifier.js';
|
|
15
|
+
import {createDocumentLoader} from './documentLoader.js';
|
|
16
|
+
import {createRequire} from 'module';
|
|
17
|
+
import {createValidateMiddleware as validate} from '@bedrock/validation';
|
|
18
|
+
import {metering, middleware} from '@bedrock/service-core';
|
|
19
|
+
const require = createRequire(import.meta.url);
|
|
20
|
+
const {Ed25519Signature2020} =
|
|
21
|
+
require('@digitalbazaar/ed25519-signature-2020');
|
|
22
|
+
const {Ed25519Signature2018} =
|
|
23
|
+
require('@digitalbazaar/ed25519-signature-2018');
|
|
24
|
+
const vc = require('@digitalbazaar/vc');
|
|
25
|
+
|
|
26
|
+
const {util: {BedrockError}} = bedrock;
|
|
27
|
+
|
|
28
|
+
// FIXME: remove and apply at top-level application
|
|
29
|
+
bedrock.events.on('bedrock-express.configure.bodyParser', app => {
|
|
30
|
+
app.use(bodyParser.json({limit: '10MB', type: ['json', '+json']}));
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
export async function addRoutes({app, service} = {}) {
|
|
34
|
+
const {routePrefix} = service;
|
|
35
|
+
|
|
36
|
+
const cfg = bedrock.config['vc-verifier'];
|
|
37
|
+
const baseUrl = `${routePrefix}/:localId`;
|
|
38
|
+
const routes = {
|
|
39
|
+
challenges: `${baseUrl}${cfg.routes.challenges}`,
|
|
40
|
+
credentialsVerify: `${baseUrl}${cfg.routes.credentialsVerify}`,
|
|
41
|
+
presentationsVerify: `${baseUrl}${cfg.routes.presentationsVerify}`
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
const {supportedSuites} = cfg;
|
|
45
|
+
const suite = [];
|
|
46
|
+
if(supportedSuites.includes('Ed25519Signature2018')) {
|
|
47
|
+
suite.push(new Ed25519Signature2018());
|
|
48
|
+
}
|
|
49
|
+
if(supportedSuites.includes('Ed25519Signature2020')) {
|
|
50
|
+
suite.push(new Ed25519Signature2020());
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const getConfigMiddleware = middleware.createGetConfigMiddleware({service});
|
|
54
|
+
|
|
55
|
+
/* Note: CORS is used on all endpoints. This is safe because authorization
|
|
56
|
+
uses HTTP signatures + capabilities or OAuth2, not cookies; CSRF is not
|
|
57
|
+
possible. */
|
|
58
|
+
|
|
59
|
+
// create a challenge
|
|
60
|
+
app.options(routes.challenges, cors());
|
|
61
|
+
app.post(
|
|
62
|
+
routes.challenges,
|
|
63
|
+
cors(),
|
|
64
|
+
validate({bodySchema: createChallengeBody}),
|
|
65
|
+
getConfigMiddleware,
|
|
66
|
+
// FIXME: add middleware to switch between oauth2 / zcap based on headers
|
|
67
|
+
middleware.authorizeConfigZcapInvocation(),
|
|
68
|
+
asyncHandler(async (req, res) => {
|
|
69
|
+
const {config} = req.serviceObject;
|
|
70
|
+
const challenge = await createChallenge({verifierId: config.id});
|
|
71
|
+
res.json({challenge});
|
|
72
|
+
|
|
73
|
+
// meter operation usage
|
|
74
|
+
metering.reportOperationUsage({req});
|
|
75
|
+
}));
|
|
76
|
+
|
|
77
|
+
// verify a credential
|
|
78
|
+
app.options(routes.credentialsVerify, cors());
|
|
79
|
+
app.post(
|
|
80
|
+
routes.credentialsVerify,
|
|
81
|
+
cors(),
|
|
82
|
+
validate({bodySchema: verifyCredentialBody}),
|
|
83
|
+
getConfigMiddleware,
|
|
84
|
+
// FIXME: add middleware to switch between oauth2 / zcap based on headers
|
|
85
|
+
middleware.authorizeConfigZcapInvocation(),
|
|
86
|
+
asyncHandler(async (req, res) => {
|
|
87
|
+
const {config} = req.serviceObject;
|
|
88
|
+
const documentLoader = await createDocumentLoader({config});
|
|
89
|
+
|
|
90
|
+
let response;
|
|
91
|
+
try {
|
|
92
|
+
const {
|
|
93
|
+
options = {},
|
|
94
|
+
verifiableCredential: credential,
|
|
95
|
+
} = req.body;
|
|
96
|
+
|
|
97
|
+
const {checks} = options;
|
|
98
|
+
_validateChecks({checks});
|
|
99
|
+
const result = await vc.verifyCredential({
|
|
100
|
+
credential,
|
|
101
|
+
documentLoader,
|
|
102
|
+
suite,
|
|
103
|
+
// only check credential status when option is set
|
|
104
|
+
checkStatus: checks.includes('credentialStatus') ?
|
|
105
|
+
checkStatus : () => ({verified: true})
|
|
106
|
+
});
|
|
107
|
+
response = _createResponse({credential, result, checks});
|
|
108
|
+
} catch(e) {
|
|
109
|
+
response = _createResponse({error: e});
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
if(response.verified) {
|
|
113
|
+
res.status(200).json(response);
|
|
114
|
+
} else {
|
|
115
|
+
res.status(400).json(response).end();
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// meter operation usage
|
|
119
|
+
metering.reportOperationUsage({req});
|
|
120
|
+
}));
|
|
121
|
+
|
|
122
|
+
// verify a presentation
|
|
123
|
+
app.options(routes.presentationsVerify, cors());
|
|
124
|
+
/**
|
|
125
|
+
* Verifies a Verifiable Presentation.
|
|
126
|
+
*
|
|
127
|
+
* POST /verifiers/z1234/presentations/verify
|
|
128
|
+
* {
|
|
129
|
+
* "verifiablePresentation": {...},
|
|
130
|
+
* "options": {
|
|
131
|
+
* "challenge": "...",
|
|
132
|
+
* "checks": ["proof", "credentialStatus"],
|
|
133
|
+
* "domain": "issuer.example.com"
|
|
134
|
+
* }
|
|
135
|
+
* }.
|
|
136
|
+
*/
|
|
137
|
+
app.post(
|
|
138
|
+
routes.presentationsVerify,
|
|
139
|
+
cors(),
|
|
140
|
+
validate({bodySchema: verifyPresentationBody}),
|
|
141
|
+
getConfigMiddleware,
|
|
142
|
+
// FIXME: add middleware to switch between oauth2 / zcap based on headers
|
|
143
|
+
middleware.authorizeConfigZcapInvocation(),
|
|
144
|
+
asyncHandler(async (req, res) => {
|
|
145
|
+
const {config} = req.serviceObject;
|
|
146
|
+
const documentLoader = await createDocumentLoader({config});
|
|
147
|
+
|
|
148
|
+
let response;
|
|
149
|
+
try {
|
|
150
|
+
const {
|
|
151
|
+
verifiablePresentation,
|
|
152
|
+
options = {}
|
|
153
|
+
} = req.body;
|
|
154
|
+
|
|
155
|
+
const {challenge, checks, domain} = options;
|
|
156
|
+
if(!challenge) {
|
|
157
|
+
throw new BedrockError(
|
|
158
|
+
'"options.challenge" is required.', 'TypeError', {
|
|
159
|
+
httpStatusCode: 400,
|
|
160
|
+
public: true
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
_validateChecks({checks});
|
|
165
|
+
const unsignedPresentation = !checks.includes('proof');
|
|
166
|
+
|
|
167
|
+
// FIXME: allow for `checks` to request whether or not the challenge
|
|
168
|
+
// should be checked; for now, default to checking it
|
|
169
|
+
|
|
170
|
+
// first, check the challenge
|
|
171
|
+
const {verified, uses: challengeUses, error} = await verifyChallenge(
|
|
172
|
+
{challenge, verifierId: config.id});
|
|
173
|
+
if(!verified) {
|
|
174
|
+
throw error;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
const verifyOptions = {
|
|
178
|
+
challenge,
|
|
179
|
+
presentation: verifiablePresentation,
|
|
180
|
+
documentLoader,
|
|
181
|
+
suite,
|
|
182
|
+
unsignedPresentation,
|
|
183
|
+
checkStatus
|
|
184
|
+
};
|
|
185
|
+
const {proof} = verifiablePresentation;
|
|
186
|
+
if(proof && proof.domain) {
|
|
187
|
+
// FIXME: do not set a default
|
|
188
|
+
verifyOptions.domain = domain || 'issuer.example.com';
|
|
189
|
+
}
|
|
190
|
+
const result = await vc.verify(verifyOptions);
|
|
191
|
+
response = _createResponse({result, challengeUses, checks});
|
|
192
|
+
} catch(e) {
|
|
193
|
+
response = _createResponse({error: e});
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
if(response.verified) {
|
|
197
|
+
res.status(200).json(response);
|
|
198
|
+
} else {
|
|
199
|
+
res.status(400).json(response);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
// meter operation usage
|
|
203
|
+
metering.reportOperationUsage({req});
|
|
204
|
+
}));
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
function _validateChecks({checks}) {
|
|
208
|
+
if(!Array.isArray(checks)) {
|
|
209
|
+
throw new BedrockError(
|
|
210
|
+
'"options.checks" must be an array.', 'TypeError', {
|
|
211
|
+
httpStatusCode: 400,
|
|
212
|
+
public: true
|
|
213
|
+
});
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
function _createResponse({credential, result, challengeUses, error, checks}) {
|
|
218
|
+
result = result || {verified: false, error};
|
|
219
|
+
|
|
220
|
+
let response;
|
|
221
|
+
if(result.verified) {
|
|
222
|
+
result.checks = checks;
|
|
223
|
+
response = {...result, challengeUses, checks};
|
|
224
|
+
} else {
|
|
225
|
+
// debugging purposes
|
|
226
|
+
// console.log('RESULT:', JSON.stringify(result, null, 2));
|
|
227
|
+
|
|
228
|
+
// get verification method for presentation/credential
|
|
229
|
+
let verificationMethod;
|
|
230
|
+
const results = result.results ||
|
|
231
|
+
(result.presentationResult && result.presentationResult.results);
|
|
232
|
+
if(results && results.length > 0) {
|
|
233
|
+
const [{proof}] = results;
|
|
234
|
+
({verificationMethod} = proof);
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
if(!result.error) {
|
|
238
|
+
// try to get error from credential results
|
|
239
|
+
const firstCredentialResult = (result.presentationResult &&
|
|
240
|
+
result.credentialResults && result.credentialResults[0]) ||
|
|
241
|
+
result;
|
|
242
|
+
if(!firstCredentialResult.error &&
|
|
243
|
+
firstCredentialResult.statusResult &&
|
|
244
|
+
!firstCredentialResult.statusResult.verified) {
|
|
245
|
+
if(firstCredentialResult.statusResult.error) {
|
|
246
|
+
error = {
|
|
247
|
+
message: 'The credential status could not be checked.',
|
|
248
|
+
cause: firstCredentialResult.statusResult.error.message
|
|
249
|
+
};
|
|
250
|
+
} else {
|
|
251
|
+
// FIXME: surface status type information so it can be included here
|
|
252
|
+
error = {
|
|
253
|
+
message: 'The credential failed a status check.'
|
|
254
|
+
};
|
|
255
|
+
}
|
|
256
|
+
} else {
|
|
257
|
+
error = firstCredentialResult.error;
|
|
258
|
+
}
|
|
259
|
+
error = error || {message: 'Verification error.'};
|
|
260
|
+
} else {
|
|
261
|
+
error = result.error;
|
|
262
|
+
if(!error.message) {
|
|
263
|
+
error.message = 'Verification error.';
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
let message = error.message;
|
|
268
|
+
if(error.errors && error.errors.length > 0) {
|
|
269
|
+
message = error.errors[0].message;
|
|
270
|
+
}
|
|
271
|
+
response = {
|
|
272
|
+
...result,
|
|
273
|
+
verified: false,
|
|
274
|
+
error,
|
|
275
|
+
checks: [{
|
|
276
|
+
check: checks,
|
|
277
|
+
id: credential && credential.id,
|
|
278
|
+
error: message,
|
|
279
|
+
verificationMethod
|
|
280
|
+
}]
|
|
281
|
+
};
|
|
282
|
+
}
|
|
283
|
+
return response;
|
|
284
|
+
}
|
package/lib/index.js
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
* Copyright (c) 2021-2022 Digital Bazaar, Inc. All rights reserved.
|
|
3
|
+
*/
|
|
4
|
+
import * as bedrock from '@bedrock/core';
|
|
5
|
+
import {addRoutes} from './http.js';
|
|
6
|
+
import {
|
|
7
|
+
addRoutes as addContextStoreRoutes
|
|
8
|
+
} from '@bedrock/service-context-store';
|
|
9
|
+
import {createService} from '@bedrock/service-core';
|
|
10
|
+
import {initializeServiceAgent} from '@bedrock/service-agent';
|
|
11
|
+
|
|
12
|
+
// load config defaults
|
|
13
|
+
import './config.js';
|
|
14
|
+
|
|
15
|
+
const serviceType = 'vc-verifier';
|
|
16
|
+
|
|
17
|
+
bedrock.events.on('bedrock.init', async () => {
|
|
18
|
+
// create `vc-verifier` service
|
|
19
|
+
const service = await createService({
|
|
20
|
+
serviceType,
|
|
21
|
+
routePrefix: '/verifiers',
|
|
22
|
+
storageCost: {
|
|
23
|
+
config: 1,
|
|
24
|
+
revocation: 1
|
|
25
|
+
},
|
|
26
|
+
validation: {
|
|
27
|
+
// require these zcaps (by reference ID)
|
|
28
|
+
zcapReferenceIds: [{
|
|
29
|
+
referenceId: 'edv',
|
|
30
|
+
required: true
|
|
31
|
+
}, {
|
|
32
|
+
referenceId: 'hmac',
|
|
33
|
+
required: true
|
|
34
|
+
}, {
|
|
35
|
+
referenceId: 'keyAgreementKey',
|
|
36
|
+
required: true
|
|
37
|
+
}]
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
bedrock.events.on('bedrock-express.configure.routes', async app => {
|
|
42
|
+
await addContextStoreRoutes({app, service});
|
|
43
|
+
await addRoutes({app, service});
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
// initialize vc-verifier service agent early (after database is ready) if
|
|
47
|
+
// KMS system is externalized; otherwise we must wait until KMS system
|
|
48
|
+
// is ready
|
|
49
|
+
const externalKms = !bedrock.config['service-agent'].kms.baseUrl.startsWith(
|
|
50
|
+
bedrock.config.server.baseUri);
|
|
51
|
+
const event = externalKms ? 'bedrock-mongodb.ready' : 'bedrock.ready';
|
|
52
|
+
bedrock.events.on(event, async () => {
|
|
53
|
+
await initializeServiceAgent({serviceType});
|
|
54
|
+
});
|
|
55
|
+
});
|
package/lib/status.js
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
* Copyright (c) 2019-2022 Digital Bazaar, Inc. All rights reserved.
|
|
3
|
+
*/
|
|
4
|
+
import assert from 'assert-plus';
|
|
5
|
+
import {createRequire} from 'module';
|
|
6
|
+
const require = createRequire(import.meta.url);
|
|
7
|
+
const {
|
|
8
|
+
checkStatus: statusListCheckStatus,
|
|
9
|
+
statusTypeMatches: statusListStatusTypeMatches
|
|
10
|
+
} = require('@digitalbazaar/vc-status-list');
|
|
11
|
+
const {
|
|
12
|
+
checkStatus: revocationListCheckStatus,
|
|
13
|
+
statusTypeMatches: revocationListStatusTypeMatches
|
|
14
|
+
} = require('vc-revocation-list');
|
|
15
|
+
|
|
16
|
+
const handlerMap = new Map();
|
|
17
|
+
handlerMap.set('RevocationList2020Status', {
|
|
18
|
+
checkStatus: revocationListCheckStatus,
|
|
19
|
+
statusTypeMatches: revocationListStatusTypeMatches
|
|
20
|
+
});
|
|
21
|
+
handlerMap.set('RevocationList2021Status', {
|
|
22
|
+
checkStatus: statusListCheckStatus,
|
|
23
|
+
statusTypeMatches: statusListStatusTypeMatches
|
|
24
|
+
});
|
|
25
|
+
handlerMap.set('SuspensionList2021Status', {
|
|
26
|
+
checkStatus: statusListCheckStatus,
|
|
27
|
+
statusTypeMatches: statusListStatusTypeMatches
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
export async function checkStatus(options = {}) {
|
|
31
|
+
assert.object(options, 'options');
|
|
32
|
+
assert.object(options.credential, 'options.credential');
|
|
33
|
+
|
|
34
|
+
try {
|
|
35
|
+
const {credential} = options;
|
|
36
|
+
const {credentialStatus} = credential;
|
|
37
|
+
if(!credentialStatus) {
|
|
38
|
+
// no status to check
|
|
39
|
+
return {verified: true};
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const handlers = handlerMap.get(credentialStatus.type);
|
|
43
|
+
if(!(handlers && handlers.statusTypeMatches({credential}))) {
|
|
44
|
+
throw new Error(
|
|
45
|
+
`Unsupported credentialStatus type "${credentialStatus.type}".`);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
return await handlers.checkStatus(options);
|
|
49
|
+
} catch(error) {
|
|
50
|
+
return {verified: false, error};
|
|
51
|
+
}
|
|
52
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@bedrock/vc-verifier",
|
|
3
|
+
"version": "6.0.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"description": "Bedrock VC Verifier",
|
|
6
|
+
"main": "./lib/index.js",
|
|
7
|
+
"scripts": {
|
|
8
|
+
"lint": "eslint ."
|
|
9
|
+
},
|
|
10
|
+
"repository": {
|
|
11
|
+
"type": "git",
|
|
12
|
+
"url": "https://github.com/digitalbazaar/bedrock-vc-verifier.git"
|
|
13
|
+
},
|
|
14
|
+
"author": {
|
|
15
|
+
"name": "Digital Bazaar, Inc.",
|
|
16
|
+
"email": "support@digitalbazaar.com",
|
|
17
|
+
"url": "https://digitalbazaar.com"
|
|
18
|
+
},
|
|
19
|
+
"bugs": {
|
|
20
|
+
"url": "https://github.com/digitalbazaar/bedrock-vc-verifier/issues"
|
|
21
|
+
},
|
|
22
|
+
"homepage": "https://github.com/digitalbazaar/bedrock-vc-verifier",
|
|
23
|
+
"dependencies": {
|
|
24
|
+
"@digitalbazaar/ed25519-signature-2018": "^2.1.0",
|
|
25
|
+
"@digitalbazaar/ed25519-signature-2020": "^3.0.0",
|
|
26
|
+
"@digitalbazaar/vc": "^2.1.0",
|
|
27
|
+
"@digitalbazaar/vc-status-list": "^2.1.0",
|
|
28
|
+
"assert-plus": "^1.0.0",
|
|
29
|
+
"bnid": "^2.1.0",
|
|
30
|
+
"body-parser": "^1.19.0",
|
|
31
|
+
"cors": "^2.8.5",
|
|
32
|
+
"vc-revocation-list": "^3.0.0"
|
|
33
|
+
},
|
|
34
|
+
"peerDependencies": {
|
|
35
|
+
"@bedrock/core": "^5.0.0",
|
|
36
|
+
"@bedrock/credentials-context": "^2.0.0",
|
|
37
|
+
"@bedrock/did-context": "^3.0.0",
|
|
38
|
+
"@bedrock/did-io": "^7.0.0",
|
|
39
|
+
"@bedrock/express": "^7.0.0",
|
|
40
|
+
"@bedrock/https-agent": "^3.0.0",
|
|
41
|
+
"@bedrock/jsonld-document-loader": "^2.0.0",
|
|
42
|
+
"@bedrock/mongodb": "^9.0.0",
|
|
43
|
+
"@bedrock/security-context": "^6.0.0",
|
|
44
|
+
"@bedrock/service-agent": "^3.0.0",
|
|
45
|
+
"@bedrock/service-context-store": "^4.0.0",
|
|
46
|
+
"@bedrock/service-core": "^4.0.0",
|
|
47
|
+
"@bedrock/vc-status-list-context": "^2.0.0",
|
|
48
|
+
"@bedrock/vc-revocation-list-context": "^2.0.0",
|
|
49
|
+
"@bedrock/veres-one-context": "^13.0.0",
|
|
50
|
+
"@bedrock/validation": "^6.0.0"
|
|
51
|
+
},
|
|
52
|
+
"devDependencies": {
|
|
53
|
+
"eslint": "^7.32.0",
|
|
54
|
+
"eslint-config-digitalbazaar": "^2.8.0",
|
|
55
|
+
"eslint-plugin-jsdoc": "^28.6.1",
|
|
56
|
+
"jsdoc": "^3.6.4",
|
|
57
|
+
"jsdoc-to-markdown": "^7.1.1"
|
|
58
|
+
},
|
|
59
|
+
"engines": {
|
|
60
|
+
"node": ">=14"
|
|
61
|
+
}
|
|
62
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
* Copyright (c) 2022 Digital Bazaar, Inc. All rights reserved.
|
|
3
|
+
*/
|
|
4
|
+
const context = {
|
|
5
|
+
title: '@context',
|
|
6
|
+
type: 'array',
|
|
7
|
+
minItems: 1,
|
|
8
|
+
items: {
|
|
9
|
+
type: ['string', 'object']
|
|
10
|
+
}
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
export const createChallengeBody = {
|
|
14
|
+
title: 'Create Challenge Body',
|
|
15
|
+
type: 'object',
|
|
16
|
+
additionalProperties: false,
|
|
17
|
+
// body must be empty
|
|
18
|
+
properties: {}
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
export const verifyCredentialBody = {
|
|
22
|
+
title: 'Verify Credential Body',
|
|
23
|
+
type: 'object',
|
|
24
|
+
required: ['verifiableCredential'],
|
|
25
|
+
additionalProperties: false,
|
|
26
|
+
properties: {
|
|
27
|
+
options: {
|
|
28
|
+
type: 'object'
|
|
29
|
+
},
|
|
30
|
+
verifiableCredential: {
|
|
31
|
+
type: 'object',
|
|
32
|
+
additionalProperties: true,
|
|
33
|
+
required: ['@context'],
|
|
34
|
+
properties: {
|
|
35
|
+
'@context': context
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
export const verifyPresentationBody = {
|
|
42
|
+
title: 'Verify Presentation Body',
|
|
43
|
+
type: 'object',
|
|
44
|
+
required: ['verifiablePresentation'],
|
|
45
|
+
additionalProperties: false,
|
|
46
|
+
properties: {
|
|
47
|
+
options: {
|
|
48
|
+
type: 'object'
|
|
49
|
+
},
|
|
50
|
+
verifiablePresentation: {
|
|
51
|
+
type: 'object',
|
|
52
|
+
additionalProperties: true,
|
|
53
|
+
required: ['@context'],
|
|
54
|
+
properties: {
|
|
55
|
+
'@context': context
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
};
|