@bedrock/vc-delivery 7.14.1 → 7.15.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 +1 -1
- package/lib/oid4/oid4vci.js +22 -13
- package/lib/oid4/oid4vciDraft13.js +28 -19
- package/lib/verify.js +47 -0
- package/package.json +2 -2
- package/schemas/bedrock-vc-workflow.js +52 -20
package/lib/helpers.js
CHANGED
|
@@ -304,7 +304,7 @@ export function createVerifyOptions({
|
|
|
304
304
|
|
|
305
305
|
// update `checks` with anything additional from `verifyPresentationOptions`
|
|
306
306
|
const checkSet = new Set(checks);
|
|
307
|
-
if(verifyPresentationOptions
|
|
307
|
+
if(verifyPresentationOptions?.checks) {
|
|
308
308
|
Object.entries(verifyPresentationOptions.checks)
|
|
309
309
|
.forEach(([check, enabled]) => enabled && checkSet.add(check));
|
|
310
310
|
}
|
package/lib/oid4/oid4vci.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*!
|
|
2
|
-
* Copyright (c) 2022-2026 Digital Bazaar, Inc.
|
|
2
|
+
* Copyright (c) 2022-2026 Digital Bazaar, Inc.
|
|
3
3
|
*/
|
|
4
4
|
import * as bedrock from '@bedrock/core';
|
|
5
5
|
import * as draft13 from './oid4vciDraft13.js';
|
|
@@ -14,7 +14,7 @@ import {checkAccessToken} from '@bedrock/oauth2-verifier';
|
|
|
14
14
|
import {compile} from '@bedrock/validation';
|
|
15
15
|
import {ExchangeProcessor} from '../ExchangeProcessor.js';
|
|
16
16
|
import {getStepAuthorizationRequest} from './oid4vp.js';
|
|
17
|
-
import {
|
|
17
|
+
import {verifyCredentialRequestProof} from '../verify.js';
|
|
18
18
|
|
|
19
19
|
const {util: {BedrockError}} = bedrock;
|
|
20
20
|
|
|
@@ -430,23 +430,32 @@ export async function processCredentialRequests({req, res, isBatchRequest}) {
|
|
|
430
430
|
}
|
|
431
431
|
|
|
432
432
|
// check to see if step requires a DID proof
|
|
433
|
-
if(step.jwtDidProofRequest) {
|
|
434
|
-
// handle OID4VCI specialized JWT DID Proof request...
|
|
433
|
+
if(step.divpDidProofRequest || step.jwtDidProofRequest) {
|
|
434
|
+
// handle OID4VCI specialized DI VP / JWT DID Proof request...
|
|
435
435
|
|
|
436
436
|
// `proof` must be in every credential request; if any request is
|
|
437
437
|
// missing `proof` then request a DID proof
|
|
438
|
-
|
|
438
|
+
const acceptableProofTypes = new Set();
|
|
439
|
+
if(step.divpDidProofRequest) {
|
|
440
|
+
acceptableProofTypes.add('di_vp');
|
|
441
|
+
}
|
|
442
|
+
if(step.jwtDidProofRequest) {
|
|
443
|
+
acceptableProofTypes.add('jwt');
|
|
444
|
+
}
|
|
445
|
+
const hasAcceptableProofType =
|
|
446
|
+
cr => Object.keys(cr.proofs ?? {}).some(
|
|
447
|
+
k => acceptableProofTypes.has(k));
|
|
448
|
+
if(credentialRequests.some(cr => !hasAcceptableProofType(cr))) {
|
|
439
449
|
didProofRequired = true;
|
|
440
450
|
return _requestDidProof({res, exchangeRecord});
|
|
441
451
|
}
|
|
442
452
|
|
|
443
453
|
// verify every DID proof and get resulting DIDs
|
|
444
454
|
const results = await Promise.all(
|
|
445
|
-
credentialRequests.map(async
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
return did;
|
|
455
|
+
credentialRequests.map(async credentialRequest => {
|
|
456
|
+
return verifyCredentialRequestProof({
|
|
457
|
+
credentialRequest, workflow, exchange
|
|
458
|
+
});
|
|
450
459
|
}));
|
|
451
460
|
// require `did` to be the same for every proof
|
|
452
461
|
// FIXME: determine if this needs to be more flexible
|
|
@@ -497,7 +506,7 @@ export async function processCredentialRequests({req, res, isBatchRequest}) {
|
|
|
497
506
|
}
|
|
498
507
|
|
|
499
508
|
// otherwise, input is required if:
|
|
500
|
-
// 1. a `jwtDidProofRequest` is required and hasn't been provided
|
|
509
|
+
// 1. a `divp|jwtDidProofRequest` is required and hasn't been provided
|
|
501
510
|
// 2. OID4VP is enabled and no OID4VP result has been stored yet
|
|
502
511
|
return didProofRequired || (step.openId && !exchange.variables
|
|
503
512
|
.results[exchange.step]?.openId?.authorizationRequest);
|
|
@@ -617,7 +626,7 @@ function _createSupportedCredentialRequests({
|
|
|
617
626
|
if(isDraft13) {
|
|
618
627
|
supportedCredentialRequests =
|
|
619
628
|
draft13.createSupportedCredentialRequests({
|
|
620
|
-
workflow, exchange, issueRequestsParams
|
|
629
|
+
workflow, exchange, issueRequestsParams, step
|
|
621
630
|
});
|
|
622
631
|
} else {
|
|
623
632
|
supportedCredentialRequests = [];
|
|
@@ -712,7 +721,7 @@ function _getSupportedCredentialConfigurations({workflow, exchange, step}) {
|
|
|
712
721
|
|
|
713
722
|
// no explicit IDs; create legacy supported credential configurations
|
|
714
723
|
return draft13.createSupportedCredentialConfigurations({
|
|
715
|
-
exchange, issuerInstances
|
|
724
|
+
exchange, issuerInstances, step
|
|
716
725
|
});
|
|
717
726
|
}
|
|
718
727
|
|
|
@@ -8,7 +8,7 @@ import {randomUUID as uuid} from 'node:crypto';
|
|
|
8
8
|
const {util: {BedrockError}} = bedrock;
|
|
9
9
|
|
|
10
10
|
export function createSupportedCredentialConfigurations({
|
|
11
|
-
exchange, issuerInstances
|
|
11
|
+
exchange, issuerInstances, step
|
|
12
12
|
} = {}) {
|
|
13
13
|
// get legacy `expectedCredentialRequests`
|
|
14
14
|
const {
|
|
@@ -27,7 +27,7 @@ export function createSupportedCredentialConfigurations({
|
|
|
27
27
|
// supported credential configuration
|
|
28
28
|
for(const credentialRequest of expectedCredentialRequests) {
|
|
29
29
|
const configurations = _createCredentialConfigurations({
|
|
30
|
-
credentialRequest, supportedFormats
|
|
30
|
+
credentialRequest, supportedFormats, step
|
|
31
31
|
});
|
|
32
32
|
for(const {id, configuration} of configurations) {
|
|
33
33
|
supported.set(id, configuration);
|
|
@@ -38,11 +38,11 @@ export function createSupportedCredentialConfigurations({
|
|
|
38
38
|
}
|
|
39
39
|
|
|
40
40
|
export function createSupportedCredentialRequests({
|
|
41
|
-
workflow, exchange, issueRequestsParams
|
|
41
|
+
workflow, exchange, issueRequestsParams, step
|
|
42
42
|
} = {}) {
|
|
43
43
|
const issuerInstances = getWorkflowIssuerInstances({workflow});
|
|
44
44
|
const supported = createSupportedCredentialConfigurations({
|
|
45
|
-
exchange, issuerInstances
|
|
45
|
+
exchange, issuerInstances, step
|
|
46
46
|
});
|
|
47
47
|
|
|
48
48
|
// for each `issueRequest` params, create a duplicate for each supported
|
|
@@ -130,12 +130,15 @@ function _matchCredentialRequests({
|
|
|
130
130
|
type: 'openid_credential',
|
|
131
131
|
credential_configuration_id
|
|
132
132
|
};
|
|
133
|
-
// only proof type supported for draft 13 is `jwt` per JSON schema that
|
|
134
|
-
// has already run
|
|
135
133
|
if(cr.proof) {
|
|
136
|
-
newRequest.proofs = {
|
|
137
|
-
|
|
138
|
-
|
|
134
|
+
newRequest.proofs = {};
|
|
135
|
+
for(const [key, value] of Object.entries(cr.proof)) {
|
|
136
|
+
if(key === 'proof_type') {
|
|
137
|
+
newRequest.proof_type = value;
|
|
138
|
+
continue;
|
|
139
|
+
}
|
|
140
|
+
newRequest.proofs[key] = [value];
|
|
141
|
+
}
|
|
139
142
|
}
|
|
140
143
|
return newRequest;
|
|
141
144
|
}
|
|
@@ -149,7 +152,7 @@ function _matchCredentialRequests({
|
|
|
149
152
|
}
|
|
150
153
|
|
|
151
154
|
function _createCredentialConfigurations({
|
|
152
|
-
credentialRequest, supportedFormats
|
|
155
|
+
credentialRequest, supportedFormats, step
|
|
153
156
|
}) {
|
|
154
157
|
const configurations = [];
|
|
155
158
|
|
|
@@ -164,17 +167,23 @@ function _createCredentialConfigurations({
|
|
|
164
167
|
format, credential_definition
|
|
165
168
|
});
|
|
166
169
|
const configuration = {format, credential_definition};
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
170
|
+
if(step.divpDidProofRequest) {
|
|
171
|
+
configuration.proof_types_supported = {
|
|
172
|
+
di_vp: {
|
|
173
|
+
proof_signing_alg_values_supported: [
|
|
174
|
+
'ecdsa-rdfc-2019', 'eddsa-rdfc-2022'
|
|
175
|
+
]
|
|
176
|
+
}
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
if(step.jwtDidProofRequest) {
|
|
180
|
+
if(!configuration.proof_types_supported) {
|
|
181
|
+
configuration.proof_types_supported = {};
|
|
175
182
|
}
|
|
183
|
+
configuration.proof_types_supported.jwt = {
|
|
184
|
+
proof_signing_alg_values_supported: ['ES256']
|
|
185
|
+
};
|
|
176
186
|
}
|
|
177
|
-
*/
|
|
178
187
|
configurations.push({id, configuration});
|
|
179
188
|
}
|
|
180
189
|
|
package/lib/verify.js
CHANGED
|
@@ -148,6 +148,53 @@ export async function verify({
|
|
|
148
148
|
};
|
|
149
149
|
}
|
|
150
150
|
|
|
151
|
+
export async function verifyCredentialRequestProof({
|
|
152
|
+
credentialRequest, workflow, exchange
|
|
153
|
+
} = {}) {
|
|
154
|
+
// FIXME: do not support more than one proof of each type at this time
|
|
155
|
+
const jwt = credentialRequest.proofs.jwt?.[0];
|
|
156
|
+
const diVp = credentialRequest.proofs.di_vp?.[0];
|
|
157
|
+
|
|
158
|
+
let _did;
|
|
159
|
+
const dids = [];
|
|
160
|
+
if(diVp) {
|
|
161
|
+
const {did} = await verifyDidProofDiVp({workflow, exchange, diVp});
|
|
162
|
+
dids.push(did);
|
|
163
|
+
_did = did;
|
|
164
|
+
}
|
|
165
|
+
if(jwt) {
|
|
166
|
+
const {did} = await verifyDidProofJwt({workflow, exchange, jwt});
|
|
167
|
+
dids.push(did);
|
|
168
|
+
if(_did === undefined) {
|
|
169
|
+
_did = did;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
if(dids.some(d => d !== _did)) {
|
|
174
|
+
// FIXME: improve error
|
|
175
|
+
throw new Error('every DID must be the same');
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
return _did;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
export async function verifyDidProofDiVp({workflow, exchange, diVp} = {}) {
|
|
182
|
+
// domain is always the `exchangeId` and cannot be configured; this
|
|
183
|
+
// prevents attacks where access tokens could otherwise be generated
|
|
184
|
+
// if the AS keys were compromised; the `exchangeId` must also be known
|
|
185
|
+
const exchangeId = `${workflow.id}/exchanges/${exchange.id}`;
|
|
186
|
+
const verifyResult = await verify({
|
|
187
|
+
workflow,
|
|
188
|
+
presentation: diVp,
|
|
189
|
+
// challenge is always local exchange ID; which is what is returned from
|
|
190
|
+
// nonce endpoint; VCALM exchanges are short-lived and are capability URLs
|
|
191
|
+
expectedChallenge: exchange.id,
|
|
192
|
+
expectedDomain: exchangeId
|
|
193
|
+
});
|
|
194
|
+
const did = verifyResult.verificationMethod.controller;
|
|
195
|
+
return {verified: true, did, verifyResult};
|
|
196
|
+
}
|
|
197
|
+
|
|
151
198
|
export async function verifyDidProofJwt({workflow, exchange, jwt} = {}) {
|
|
152
199
|
// optional oauth2 options
|
|
153
200
|
const {oauth2} = exchange.openId;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bedrock/vc-delivery",
|
|
3
|
-
"version": "7.
|
|
3
|
+
"version": "7.15.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Bedrock Verifiable Credential Delivery",
|
|
6
6
|
"main": "./lib/index.js",
|
|
@@ -40,7 +40,7 @@
|
|
|
40
40
|
"@digitalbazaar/ed25519-signature-2020": "^5.4.0",
|
|
41
41
|
"@digitalbazaar/ezcap": "^4.1.0",
|
|
42
42
|
"@digitalbazaar/http-client": "^4.2.0",
|
|
43
|
-
"@digitalbazaar/oid4-client": "^5.
|
|
43
|
+
"@digitalbazaar/oid4-client": "^5.12.1",
|
|
44
44
|
"@digitalbazaar/vc": "^7.2.0",
|
|
45
45
|
"@digitalbazaar/webkms-client": "^14.2.0",
|
|
46
46
|
"assert-plus": "^1.0.0",
|
|
@@ -195,6 +195,20 @@ const credentialDefinition = {
|
|
|
195
195
|
}
|
|
196
196
|
};
|
|
197
197
|
|
|
198
|
+
const supportedProofTypeConfiguration = {
|
|
199
|
+
title: 'OID4VCI Supported Proof Type Configuration',
|
|
200
|
+
type: 'object',
|
|
201
|
+
required: ['proof_signing_alg_values_supported'],
|
|
202
|
+
additionalProperties: false,
|
|
203
|
+
properties: {
|
|
204
|
+
proof_signing_alg_values_supported: {
|
|
205
|
+
type: 'array',
|
|
206
|
+
minItems: 1,
|
|
207
|
+
items: {type: 'string'}
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
};
|
|
211
|
+
|
|
198
212
|
function credentialConfiguration() {
|
|
199
213
|
return {
|
|
200
214
|
title: 'OID4VCI Credential Configuration',
|
|
@@ -209,14 +223,9 @@ function credentialConfiguration() {
|
|
|
209
223
|
},
|
|
210
224
|
proof_types_supported: {
|
|
211
225
|
type: 'object',
|
|
212
|
-
required: ['proof_signing_al_values_supported'],
|
|
213
|
-
additionalProperties: false,
|
|
214
226
|
properties: {
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
minItems: 1,
|
|
218
|
-
items: {type: 'string'}
|
|
219
|
-
}
|
|
227
|
+
di_vp: supportedProofTypeConfiguration,
|
|
228
|
+
jwt: supportedProofTypeConfiguration
|
|
220
229
|
}
|
|
221
230
|
}
|
|
222
231
|
}
|
|
@@ -584,6 +593,31 @@ function computedStep() {
|
|
|
584
593
|
}
|
|
585
594
|
}
|
|
586
595
|
},
|
|
596
|
+
divpDidProofRequest: {
|
|
597
|
+
type: 'object',
|
|
598
|
+
additionalProperties: false,
|
|
599
|
+
properties: {
|
|
600
|
+
acceptedMethods: {
|
|
601
|
+
title: 'Accepted DID Methods',
|
|
602
|
+
type: 'array',
|
|
603
|
+
minItems: 1,
|
|
604
|
+
items: {
|
|
605
|
+
title: 'Accepted DID Method',
|
|
606
|
+
type: 'object',
|
|
607
|
+
additionalProperties: false,
|
|
608
|
+
properties: {
|
|
609
|
+
method: {type: 'string'}
|
|
610
|
+
}
|
|
611
|
+
}
|
|
612
|
+
},
|
|
613
|
+
allowedCryptosuites: {
|
|
614
|
+
title: 'Allowed DI Cryptosuites',
|
|
615
|
+
type: 'array',
|
|
616
|
+
minItems: 1,
|
|
617
|
+
items: {type: 'string'}
|
|
618
|
+
}
|
|
619
|
+
}
|
|
620
|
+
},
|
|
587
621
|
nextStep: {type: 'string'},
|
|
588
622
|
// required to support OID4VP
|
|
589
623
|
openId: {
|
|
@@ -717,13 +751,17 @@ function openIdCredentialRequestDraft13() {
|
|
|
717
751
|
title: 'DID Authn Proof JWT',
|
|
718
752
|
type: 'object',
|
|
719
753
|
additionalProperties: false,
|
|
720
|
-
|
|
754
|
+
anyOf: [
|
|
755
|
+
{required: ['proof_type', 'jwt']},
|
|
756
|
+
{required: ['proof_type', 'di_vp']}
|
|
757
|
+
],
|
|
721
758
|
properties: {
|
|
722
759
|
proof_type: {
|
|
723
760
|
type: 'string',
|
|
724
|
-
enum: ['jwt']
|
|
761
|
+
enum: ['jwt', 'di_vp']
|
|
725
762
|
},
|
|
726
|
-
jwt: {type: 'string'}
|
|
763
|
+
jwt: {type: 'string'},
|
|
764
|
+
di_vp: verifiablePresentation()
|
|
727
765
|
}
|
|
728
766
|
}
|
|
729
767
|
}
|
|
@@ -735,21 +773,15 @@ function openIdCredentialRequestVersion1() {
|
|
|
735
773
|
title: 'OID4VCI-1.0 Credential Request',
|
|
736
774
|
type: 'object',
|
|
737
775
|
additionalProperties: false,
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
// which is not supported
|
|
742
|
-
{required: ['credential_identifier']}//,
|
|
743
|
-
//{required: ['credential_configuration_id']}
|
|
744
|
-
],
|
|
776
|
+
// `credential_configuration_id` is for scope-identified credentials,
|
|
777
|
+
// which is not supported
|
|
778
|
+
required: ['credential_identifier'],
|
|
745
779
|
properties: {
|
|
746
780
|
credential_identifier: {type: 'string'},
|
|
747
|
-
// FIXME: remove me
|
|
748
|
-
//credential_configuration_id: {type: 'string'},
|
|
749
781
|
proofs: {
|
|
750
782
|
type: 'object',
|
|
751
783
|
additionalProperties: false,
|
|
752
|
-
|
|
784
|
+
anyOf: [
|
|
753
785
|
{required: ['jwt']},
|
|
754
786
|
{required: ['di_vp']}
|
|
755
787
|
],
|