@bedrock/vc-delivery 7.1.0 → 7.2.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/lib/helpers.js +30 -0
- package/lib/index.js +4 -5
- package/lib/oid4/oid4vp.js +9 -3
- package/lib/vcapi.js +8 -3
- package/lib/verify.js +27 -12
- package/package.json +2 -3
- package/schemas/bedrock-vc-workflow.js +22 -0
package/lib/helpers.js
CHANGED
|
@@ -188,6 +188,36 @@ export function deepEqual(obj1, obj2) {
|
|
|
188
188
|
return true;
|
|
189
189
|
}
|
|
190
190
|
|
|
191
|
+
export function createVerifyOptions({
|
|
192
|
+
verifyPresentationOptions,
|
|
193
|
+
expectedChallenge,
|
|
194
|
+
verifiablePresentationRequest,
|
|
195
|
+
presentation,
|
|
196
|
+
domain,
|
|
197
|
+
checks
|
|
198
|
+
}) {
|
|
199
|
+
// start with `verifyPresentationOptions`, then overwrite as needed
|
|
200
|
+
const options = {...verifyPresentationOptions};
|
|
201
|
+
|
|
202
|
+
// update `checks` with anything additional from `verifyPresentationOptions`
|
|
203
|
+
const checkSet = new Set(checks);
|
|
204
|
+
if(verifyPresentationOptions.checks) {
|
|
205
|
+
Object.entries(verifyPresentationOptions.checks)
|
|
206
|
+
.forEach(([check, enabled]) => enabled && checkSet.add(check));
|
|
207
|
+
}
|
|
208
|
+
options.checks = [...checkSet];
|
|
209
|
+
|
|
210
|
+
// update `challenge`
|
|
211
|
+
options.challenge = expectedChallenge ??
|
|
212
|
+
verifiablePresentationRequest.challenge ??
|
|
213
|
+
presentation?.proof?.challenge;
|
|
214
|
+
|
|
215
|
+
// update `domain`
|
|
216
|
+
options.domain = domain;
|
|
217
|
+
|
|
218
|
+
return options;
|
|
219
|
+
}
|
|
220
|
+
|
|
191
221
|
export function stripStacktrace(error) {
|
|
192
222
|
// serialize error and allow-list specific properties
|
|
193
223
|
const serialized = serializeError(error);
|
package/lib/index.js
CHANGED
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
/*!
|
|
2
|
-
* Copyright (c) 2022-
|
|
2
|
+
* Copyright (c) 2022-2025 Digital Bazaar, Inc. All rights reserved.
|
|
3
3
|
*/
|
|
4
4
|
import * as bedrock from '@bedrock/core';
|
|
5
5
|
import * as workflowSchemas from '../schemas/bedrock-vc-workflow.js';
|
|
6
6
|
import {createService, schemas} from '@bedrock/service-core';
|
|
7
7
|
import {addRoutes} from './http.js';
|
|
8
8
|
import {initializeServiceAgent} from '@bedrock/service-agent';
|
|
9
|
-
import {klona} from 'klona';
|
|
10
9
|
import {MAX_ISSUER_INSTANCES} from './constants.js';
|
|
11
10
|
import {parseLocalId} from './helpers.js';
|
|
12
11
|
import '@bedrock/express';
|
|
@@ -24,8 +23,8 @@ bedrock.events.on('bedrock.init', async () => {
|
|
|
24
23
|
|
|
25
24
|
async function _initService({serviceType, routePrefix}) {
|
|
26
25
|
// add customizations to config validators...
|
|
27
|
-
const createConfigBody =
|
|
28
|
-
const updateConfigBody =
|
|
26
|
+
const createConfigBody = structuredClone(schemas.createConfigBody);
|
|
27
|
+
const updateConfigBody = structuredClone(schemas.updateConfigBody);
|
|
29
28
|
const schemasToUpdate = [createConfigBody, updateConfigBody];
|
|
30
29
|
const {
|
|
31
30
|
credentialTemplates, steps, initialStep, issuerInstances
|
|
@@ -37,7 +36,7 @@ async function _initService({serviceType, routePrefix}) {
|
|
|
37
36
|
schema.properties.initialStep = initialStep;
|
|
38
37
|
schema.properties.issuerInstances = issuerInstances;
|
|
39
38
|
// allow zcaps by custom reference ID
|
|
40
|
-
schema.properties.zcaps =
|
|
39
|
+
schema.properties.zcaps = structuredClone(schemas.zcaps);
|
|
41
40
|
// max of 4 basic zcaps + max issuer instances
|
|
42
41
|
schema.properties.zcaps.maxProperties = 4 + MAX_ISSUER_INSTANCES;
|
|
43
42
|
schema.properties.zcaps.additionalProperties = schemas.delegatedZcap;
|
package/lib/oid4/oid4vp.js
CHANGED
|
@@ -12,7 +12,6 @@ import {
|
|
|
12
12
|
verifiablePresentation as verifiablePresentationSchema
|
|
13
13
|
} from '../../schemas/bedrock-vc-workflow.js';
|
|
14
14
|
import {compile} from '@bedrock/validation';
|
|
15
|
-
import {klona} from 'klona';
|
|
16
15
|
import {logger} from '../logger.js';
|
|
17
16
|
import {oid4vp} from '@digitalbazaar/oid4-client';
|
|
18
17
|
import {verify} from '../verify.js';
|
|
@@ -103,7 +102,8 @@ export async function getAuthorizationRequest({req}) {
|
|
|
103
102
|
authorizationRequest.client_id_scheme = 'redirect_uri';
|
|
104
103
|
}
|
|
105
104
|
if(client_metadata) {
|
|
106
|
-
authorizationRequest.client_metadata =
|
|
105
|
+
authorizationRequest.client_metadata = structuredClone(
|
|
106
|
+
client_metadata);
|
|
107
107
|
} else if(client_metadata_uri) {
|
|
108
108
|
authorizationRequest.client_metadata_uri = client_metadata_uri;
|
|
109
109
|
} else {
|
|
@@ -193,9 +193,15 @@ export async function processAuthorizationResponse({req}) {
|
|
|
193
193
|
// verify the received VP
|
|
194
194
|
const {verifiablePresentationRequest} = await oid4vp.toVpr(
|
|
195
195
|
{authorizationRequest});
|
|
196
|
-
const {
|
|
196
|
+
const {
|
|
197
|
+
allowUnprotectedPresentation = false,
|
|
198
|
+
verifyPresentationOptions = {},
|
|
199
|
+
verifyPresentationResultSchema
|
|
200
|
+
} = step;
|
|
197
201
|
const verifyResult = await verify({
|
|
198
202
|
workflow,
|
|
203
|
+
verifyPresentationOptions,
|
|
204
|
+
verifyPresentationResultSchema,
|
|
199
205
|
verifiablePresentationRequest,
|
|
200
206
|
presentation,
|
|
201
207
|
allowUnprotectedPresentation,
|
package/lib/vcapi.js
CHANGED
|
@@ -11,7 +11,6 @@ import {
|
|
|
11
11
|
import {exportJWK, generateKeyPair, importJWK} from 'jose';
|
|
12
12
|
import {compile} from '@bedrock/validation';
|
|
13
13
|
import {issue} from './issue.js';
|
|
14
|
-
import {klona} from 'klona';
|
|
15
14
|
import {logger} from './logger.js';
|
|
16
15
|
|
|
17
16
|
const {util: {BedrockError}} = bedrock;
|
|
@@ -141,7 +140,7 @@ export async function processExchange({req, res, workflow, exchangeRecord}) {
|
|
|
141
140
|
|
|
142
141
|
// if no presentation was received in the body...
|
|
143
142
|
if(!receivedPresentation) {
|
|
144
|
-
const verifiablePresentationRequest =
|
|
143
|
+
const verifiablePresentationRequest = structuredClone(
|
|
145
144
|
step.verifiablePresentationRequest);
|
|
146
145
|
if(createChallenge) {
|
|
147
146
|
/* Note: When creating a challenge, the initial step always
|
|
@@ -194,9 +193,15 @@ export async function processExchange({req, res, workflow, exchangeRecord}) {
|
|
|
194
193
|
|
|
195
194
|
// verify the received VP
|
|
196
195
|
const expectedChallenge = isInitialStep ? exchange.id : undefined;
|
|
197
|
-
const {
|
|
196
|
+
const {
|
|
197
|
+
allowUnprotectedPresentation = false,
|
|
198
|
+
verifyPresentationOptions = {},
|
|
199
|
+
verifyPresentationResultSchema
|
|
200
|
+
} = step;
|
|
198
201
|
const verifyResult = await verify({
|
|
199
202
|
workflow,
|
|
203
|
+
verifyPresentationOptions,
|
|
204
|
+
verifyPresentationResultSchema,
|
|
200
205
|
verifiablePresentationRequest: step.verifiablePresentationRequest,
|
|
201
206
|
presentation: receivedPresentation,
|
|
202
207
|
allowUnprotectedPresentation,
|
package/lib/verify.js
CHANGED
|
@@ -4,8 +4,13 @@
|
|
|
4
4
|
import * as bedrock from '@bedrock/core';
|
|
5
5
|
import * as EcdsaMultikey from '@digitalbazaar/ecdsa-multikey';
|
|
6
6
|
import * as Ed25519Multikey from '@digitalbazaar/ed25519-multikey';
|
|
7
|
-
import {
|
|
7
|
+
import {
|
|
8
|
+
createVerifyOptions,
|
|
9
|
+
getZcapClient,
|
|
10
|
+
stripStacktrace
|
|
11
|
+
} from './helpers.js';
|
|
8
12
|
import {importJWK, jwtVerify} from 'jose';
|
|
13
|
+
import {compile} from '@bedrock/validation';
|
|
9
14
|
import {didIo} from '@bedrock/did-io';
|
|
10
15
|
|
|
11
16
|
const {util: {BedrockError}} = bedrock;
|
|
@@ -25,8 +30,9 @@ export async function createChallenge({workflow} = {}) {
|
|
|
25
30
|
}
|
|
26
31
|
|
|
27
32
|
export async function verify({
|
|
28
|
-
workflow,
|
|
29
|
-
allowUnprotectedPresentation = false, expectedChallenge
|
|
33
|
+
workflow, verifyPresentationOptions, verifiablePresentationRequest,
|
|
34
|
+
presentation, allowUnprotectedPresentation = false, expectedChallenge,
|
|
35
|
+
verifyPresentationResultSchema
|
|
30
36
|
} = {}) {
|
|
31
37
|
// create zcap client for verifying
|
|
32
38
|
const {zcapClient, zcaps} = await getZcapClient({workflow});
|
|
@@ -46,17 +52,18 @@ export async function verify({
|
|
|
46
52
|
new URL(workflow.id).origin;
|
|
47
53
|
let result;
|
|
48
54
|
try {
|
|
55
|
+
const options = createVerifyOptions({
|
|
56
|
+
verifyPresentationOptions,
|
|
57
|
+
expectedChallenge,
|
|
58
|
+
verifiablePresentationRequest,
|
|
59
|
+
presentation,
|
|
60
|
+
domain,
|
|
61
|
+
checks
|
|
62
|
+
});
|
|
49
63
|
result = await zcapClient.write({
|
|
50
64
|
capability,
|
|
51
65
|
json: {
|
|
52
|
-
options
|
|
53
|
-
// FIXME: support multi-proof presentations?
|
|
54
|
-
challenge: expectedChallenge ??
|
|
55
|
-
verifiablePresentationRequest.challenge ??
|
|
56
|
-
presentation?.proof?.challenge,
|
|
57
|
-
domain,
|
|
58
|
-
checks
|
|
59
|
-
},
|
|
66
|
+
options,
|
|
60
67
|
verifiablePresentation: presentation
|
|
61
68
|
}
|
|
62
69
|
});
|
|
@@ -120,7 +127,15 @@ export async function verify({
|
|
|
120
127
|
const verificationMethod = presentationResult?.results[0]
|
|
121
128
|
.verificationMethod ?? null;
|
|
122
129
|
|
|
123
|
-
//
|
|
130
|
+
// validate against the verify presentation result schema, if applicable
|
|
131
|
+
if(verifyPresentationResultSchema) {
|
|
132
|
+
const {jsonSchema: schema} = verifyPresentationResultSchema;
|
|
133
|
+
const validate = compile({schema});
|
|
134
|
+
const {valid, error} = validate(result.data);
|
|
135
|
+
if(!valid) {
|
|
136
|
+
throw error;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
124
139
|
|
|
125
140
|
return {
|
|
126
141
|
verified,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bedrock/vc-delivery",
|
|
3
|
-
"version": "7.
|
|
3
|
+
"version": "7.2.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Bedrock Verifiable Credential Delivery",
|
|
6
6
|
"main": "./lib/index.js",
|
|
@@ -47,7 +47,6 @@
|
|
|
47
47
|
"cors": "^2.8.5",
|
|
48
48
|
"jose": "^5.10.0",
|
|
49
49
|
"jsonata": "^2.0.6",
|
|
50
|
-
"klona": "^2.0.6",
|
|
51
50
|
"serialize-error": "^12.0.0"
|
|
52
51
|
},
|
|
53
52
|
"peerDependencies": {
|
|
@@ -72,6 +71,6 @@
|
|
|
72
71
|
"eslint-plugin-unicorn": "^56.0.1"
|
|
73
72
|
},
|
|
74
73
|
"engines": {
|
|
75
|
-
"node": ">=
|
|
74
|
+
"node": ">=20"
|
|
76
75
|
}
|
|
77
76
|
}
|
|
@@ -486,6 +486,28 @@ const step = {
|
|
|
486
486
|
stepTemplate: typedTemplate,
|
|
487
487
|
verifiablePresentationRequest: {
|
|
488
488
|
type: 'object'
|
|
489
|
+
},
|
|
490
|
+
verifyPresentationOptions: {
|
|
491
|
+
type: 'object',
|
|
492
|
+
properties: {
|
|
493
|
+
checks: {
|
|
494
|
+
type: 'object'
|
|
495
|
+
}
|
|
496
|
+
},
|
|
497
|
+
additionalProperties: true
|
|
498
|
+
},
|
|
499
|
+
verifyPresentationResultSchema: {
|
|
500
|
+
type: 'object',
|
|
501
|
+
required: ['type', 'jsonSchema'],
|
|
502
|
+
additionalProperties: false,
|
|
503
|
+
properties: {
|
|
504
|
+
type: {
|
|
505
|
+
type: 'string'
|
|
506
|
+
},
|
|
507
|
+
jsonSchema: {
|
|
508
|
+
type: 'object'
|
|
509
|
+
}
|
|
510
|
+
}
|
|
489
511
|
}
|
|
490
512
|
}
|
|
491
513
|
};
|