@bedrock/vc-delivery 7.11.2 → 7.12.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/ExchangeProcessor.js +577 -0
- package/lib/helpers.js +13 -77
- package/lib/inviteRequest/inviteRequest.js +22 -72
- package/lib/issue.js +35 -23
- package/lib/oid4/authorizationRequest.js +10 -2
- package/lib/oid4/authorizationResponse.js +20 -31
- package/lib/oid4/http.js +1 -1
- package/lib/oid4/oid4vci.js +59 -81
- package/lib/oid4/oid4vp.js +180 -230
- package/lib/vcapi.js +15 -230
- package/lib/verify.js +10 -8
- package/package.json +2 -2
- package/schemas/bedrock-vc-workflow.js +8 -0
package/lib/helpers.js
CHANGED
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
* Copyright (c) 2022-2026 Digital Bazaar, Inc. All rights reserved.
|
|
3
3
|
*/
|
|
4
4
|
import * as bedrock from '@bedrock/core';
|
|
5
|
-
import * as vcjwt from './vcjwt.js';
|
|
6
5
|
import {decodeId, generateId} from 'bnid';
|
|
7
6
|
import {compile} from '@bedrock/validation';
|
|
8
7
|
import {Ed25519Signature2020} from '@digitalbazaar/ed25519-signature-2020';
|
|
@@ -22,16 +21,6 @@ const ALLOWED_ERROR_KEYS = [
|
|
|
22
21
|
'status'
|
|
23
22
|
];
|
|
24
23
|
|
|
25
|
-
const JWT_FORMAT_ALIASES = new Set([
|
|
26
|
-
'application/jwt',
|
|
27
|
-
'application/vc+jwt',
|
|
28
|
-
'application/vp+jwt',
|
|
29
|
-
'jwt_vp',
|
|
30
|
-
'jwt_vp_json',
|
|
31
|
-
'jwt_vc_json-ld',
|
|
32
|
-
'jwt_vc_json'
|
|
33
|
-
]);
|
|
34
|
-
|
|
35
24
|
export function buildPresentationFromResults({
|
|
36
25
|
presentation, verifyResult
|
|
37
26
|
}) {
|
|
@@ -128,8 +117,9 @@ export async function evaluateExchangeStep({
|
|
|
128
117
|
}) {
|
|
129
118
|
let step = workflow.steps[stepName];
|
|
130
119
|
if(step.stepTemplate) {
|
|
131
|
-
step = await evaluateTemplate(
|
|
132
|
-
|
|
120
|
+
step = await evaluateTemplate({
|
|
121
|
+
workflow, exchange, typedTemplate: step.stepTemplate
|
|
122
|
+
});
|
|
133
123
|
}
|
|
134
124
|
await validateStep({step});
|
|
135
125
|
return step;
|
|
@@ -284,7 +274,7 @@ export function createVerifyOptions({
|
|
|
284
274
|
// update `challenge`
|
|
285
275
|
if(options.challenge === undefined) {
|
|
286
276
|
options.challenge = expectedChallenge ??
|
|
287
|
-
verifiablePresentationRequest
|
|
277
|
+
verifiablePresentationRequest?.challenge ??
|
|
288
278
|
presentation?.proof?.challenge;
|
|
289
279
|
}
|
|
290
280
|
|
|
@@ -357,44 +347,6 @@ export function stripStacktrace(error) {
|
|
|
357
347
|
return error;
|
|
358
348
|
}
|
|
359
349
|
|
|
360
|
-
export async function unenvelopeCredential({
|
|
361
|
-
envelopedCredential, format
|
|
362
|
-
} = {}) {
|
|
363
|
-
const result = _getEnvelope({envelope: envelopedCredential, format});
|
|
364
|
-
|
|
365
|
-
// only supported format is VC-JWT at this time
|
|
366
|
-
const credential = vcjwt.decodeVCJWTCredential({jwt: result.envelope});
|
|
367
|
-
return {credential, ...result};
|
|
368
|
-
}
|
|
369
|
-
|
|
370
|
-
export async function unenvelopePresentation({
|
|
371
|
-
envelopedPresentation, format
|
|
372
|
-
} = {}) {
|
|
373
|
-
const result = _getEnvelope({envelope: envelopedPresentation, format});
|
|
374
|
-
|
|
375
|
-
// only supported format is VC-JWT at this time
|
|
376
|
-
const presentation = vcjwt.decodeVCJWTPresentation({jwt: result.envelope});
|
|
377
|
-
|
|
378
|
-
// unenvelope any VCs in the presentation
|
|
379
|
-
let {verifiableCredential = []} = presentation;
|
|
380
|
-
if(!Array.isArray(verifiableCredential)) {
|
|
381
|
-
verifiableCredential = [verifiableCredential];
|
|
382
|
-
}
|
|
383
|
-
if(verifiableCredential.length > 0) {
|
|
384
|
-
presentation.verifiableCredential = await Promise.all(
|
|
385
|
-
verifiableCredential.map(async vc => {
|
|
386
|
-
if(vc?.type !== 'EnvelopedVerifiableCredential') {
|
|
387
|
-
return vc;
|
|
388
|
-
}
|
|
389
|
-
const {credential} = await unenvelopeCredential({
|
|
390
|
-
envelopedCredential: vc
|
|
391
|
-
});
|
|
392
|
-
return credential;
|
|
393
|
-
}));
|
|
394
|
-
}
|
|
395
|
-
return {presentation, ...result};
|
|
396
|
-
}
|
|
397
|
-
|
|
398
350
|
export async function validateStep({step} = {}) {
|
|
399
351
|
// FIXME: use `ajv` and do JSON schema check
|
|
400
352
|
if(Object.keys(step).length === 0) {
|
|
@@ -422,35 +374,19 @@ export async function validateStep({step} = {}) {
|
|
|
422
374
|
}
|
|
423
375
|
}
|
|
424
376
|
|
|
425
|
-
function
|
|
426
|
-
const
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
format = 'application/jwt';
|
|
431
|
-
}
|
|
432
|
-
} else {
|
|
433
|
-
const {id} = envelope;
|
|
434
|
-
if(id?.startsWith('data:application/jwt,')) {
|
|
435
|
-
format = 'application/jwt';
|
|
436
|
-
envelope = id.slice('data:application/jwt,'.length);
|
|
437
|
-
}
|
|
438
|
-
}
|
|
439
|
-
|
|
440
|
-
if(format === 'application/jwt' && envelope !== undefined) {
|
|
441
|
-
return {envelope, format};
|
|
377
|
+
export function validateVerifiablePresentation({schema, presentation}) {
|
|
378
|
+
const validate = compile({schema});
|
|
379
|
+
const {valid, error} = validate(presentation);
|
|
380
|
+
if(!valid) {
|
|
381
|
+
throw error;
|
|
442
382
|
}
|
|
443
|
-
|
|
444
|
-
throw new BedrockError(
|
|
445
|
-
`Unsupported credential or presentation envelope format "${format}".`, {
|
|
446
|
-
name: 'NotSupportedError',
|
|
447
|
-
details: {httpStatusCode: 400, public: true}
|
|
448
|
-
});
|
|
449
383
|
}
|
|
450
384
|
|
|
451
|
-
export function
|
|
385
|
+
export function validateVerifiablePresentationRequest({
|
|
386
|
+
schema, presentationRequest
|
|
387
|
+
}) {
|
|
452
388
|
const validate = compile({schema});
|
|
453
|
-
const {valid, error} = validate(
|
|
389
|
+
const {valid, error} = validate(presentationRequest);
|
|
454
390
|
if(!valid) {
|
|
455
391
|
throw error;
|
|
456
392
|
}
|
|
@@ -2,89 +2,39 @@
|
|
|
2
2
|
* Copyright (c) 2025-2026 Digital Bazaar, Inc. All rights reserved.
|
|
3
3
|
*/
|
|
4
4
|
import * as bedrock from '@bedrock/core';
|
|
5
|
-
import
|
|
6
|
-
import {emitExchangeUpdated, evaluateExchangeStep} from '../helpers.js';
|
|
7
|
-
import {logger} from '../logger.js';
|
|
5
|
+
import {ExchangeProcessor} from '../ExchangeProcessor.js';
|
|
8
6
|
|
|
9
7
|
const {util: {BedrockError}} = bedrock;
|
|
10
8
|
|
|
11
9
|
export async function processInviteResponse({req}) {
|
|
12
10
|
const {config: workflow} = req.serviceObject;
|
|
13
11
|
const exchangeRecord = await req.getExchange();
|
|
14
|
-
let {meta: {updated: lastUpdated}} = exchangeRecord;
|
|
15
|
-
const {exchange} = exchangeRecord;
|
|
16
|
-
let step;
|
|
17
12
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
const currentStep = exchange.step;
|
|
21
|
-
if(!currentStep) {
|
|
22
|
-
_throwUnsupportedProtocol();
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
step = await evaluateExchangeStep({workflow, exchange});
|
|
26
|
-
|
|
27
|
-
// step must have `inviteRequest` to perform protocol
|
|
28
|
-
if(!step.inviteRequest) {
|
|
29
|
-
_throwUnsupportedProtocol();
|
|
30
|
-
}
|
|
13
|
+
// `inviteResponse` validated via HTTP body JSON schema already
|
|
14
|
+
const {body: inviteResponse} = req;
|
|
31
15
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
if(!exchange.variables.results) {
|
|
46
|
-
exchange.variables.results = {};
|
|
47
|
-
}
|
|
48
|
-
const stepResult = {
|
|
49
|
-
inviteRequest: {inviteResponse}
|
|
50
|
-
};
|
|
51
|
-
const prevState = exchange.state;
|
|
52
|
-
exchange.variables.results[currentStep] = stepResult;
|
|
53
|
-
try {
|
|
54
|
-
// mark exchange complete
|
|
55
|
-
exchange.state = 'complete';
|
|
56
|
-
exchange.sequence++;
|
|
57
|
-
await exchanges.complete({workflowId: workflow.id, exchange});
|
|
58
|
-
await emitExchangeUpdated({workflow, exchange, step});
|
|
59
|
-
lastUpdated = Date.now();
|
|
60
|
-
} catch(e) {
|
|
61
|
-
// revert exchange changes as it couldn't be written
|
|
62
|
-
exchange.sequence--;
|
|
63
|
-
exchange.state = prevState;
|
|
64
|
-
delete exchange.variables.results[currentStep];
|
|
65
|
-
throw e;
|
|
16
|
+
// process exchange to completion
|
|
17
|
+
const exchangeProcessor = new ExchangeProcessor({
|
|
18
|
+
workflow, exchangeRecord,
|
|
19
|
+
prepareStep({exchange, step}) {
|
|
20
|
+
// step must have `inviteRequest` to perform protocol
|
|
21
|
+
if(!step.inviteRequest) {
|
|
22
|
+
_throwUnsupportedProtocol();
|
|
23
|
+
}
|
|
24
|
+
// store invite response in variables associated with current step
|
|
25
|
+
exchange.variables.results[exchange.step] = {
|
|
26
|
+
...exchange.variables.results[exchange.step],
|
|
27
|
+
inviteRequest: {inviteResponse}
|
|
28
|
+
};
|
|
66
29
|
}
|
|
30
|
+
});
|
|
31
|
+
await exchangeProcessor.process();
|
|
67
32
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
}
|
|
72
|
-
return result;
|
|
73
|
-
} catch(e) {
|
|
74
|
-
if(e.name === 'InvalidStateError') {
|
|
75
|
-
throw e;
|
|
76
|
-
}
|
|
77
|
-
// write last error if exchange hasn't been frequently updated
|
|
78
|
-
const {id: workflowId} = workflow;
|
|
79
|
-
const copy = {...exchange};
|
|
80
|
-
copy.sequence++;
|
|
81
|
-
copy.lastError = e;
|
|
82
|
-
await exchanges.setLastError({workflowId, exchange: copy, lastUpdated})
|
|
83
|
-
.catch(error => logger.error(
|
|
84
|
-
'Could not set last exchange error: ' + error.message, {error}));
|
|
85
|
-
await emitExchangeUpdated({workflow, exchange, step});
|
|
86
|
-
throw e;
|
|
33
|
+
const result = {};
|
|
34
|
+
if(inviteResponse.referenceId !== undefined) {
|
|
35
|
+
result.referenceId = inviteResponse.referenceId;
|
|
87
36
|
}
|
|
37
|
+
return result;
|
|
88
38
|
}
|
|
89
39
|
|
|
90
40
|
export function getInviteRequestProtocols({workflow, exchange, step}) {
|
package/lib/issue.js
CHANGED
|
@@ -15,13 +15,16 @@ const {util: {BedrockError}} = bedrock;
|
|
|
15
15
|
|
|
16
16
|
export async function issue({
|
|
17
17
|
workflow, exchange, step, format = 'application/vc',
|
|
18
|
+
issueRequestsParams,
|
|
19
|
+
verifiablePresentation,
|
|
20
|
+
// FIXME: remove `filter`
|
|
18
21
|
// by default do not issue any VCs that are to be stored in the exchange;
|
|
19
22
|
// (`result` is NOT set)
|
|
20
23
|
filter = params => !params.result
|
|
21
24
|
} = {}) {
|
|
22
25
|
// eval all issue requests for current step in exchange
|
|
23
26
|
const issueRequests = await _evalIssueRequests({
|
|
24
|
-
workflow, exchange, step, filter
|
|
27
|
+
workflow, exchange, step, issueRequestsParams, filter
|
|
25
28
|
});
|
|
26
29
|
|
|
27
30
|
// return early if there is no explicit VP in step nor nothing to issue
|
|
@@ -42,8 +45,10 @@ export async function issue({
|
|
|
42
45
|
|
|
43
46
|
// generate VP to return VCs; use any explicitly defined VP from the step
|
|
44
47
|
// (which may include out-of-band issued VCs that are to be delivered)
|
|
45
|
-
|
|
46
|
-
|
|
48
|
+
verifiablePresentation =
|
|
49
|
+
verifiablePresentation ??
|
|
50
|
+
structuredClone(step?.verifiablePresentation) ??
|
|
51
|
+
createPresentation();
|
|
47
52
|
|
|
48
53
|
// add issued VCs to VP
|
|
49
54
|
if(issuedVcs.length > 0) {
|
|
@@ -60,31 +65,16 @@ export async function issue({
|
|
|
60
65
|
return {response: {verifiablePresentation}, format, exchangeChanged};
|
|
61
66
|
}
|
|
62
67
|
|
|
63
|
-
|
|
64
|
-
// evaluate all issue requests in parallel
|
|
65
|
-
let results = await _getIssueRequestParams({workflow, exchange, step});
|
|
66
|
-
results = results.filter(filter);
|
|
67
|
-
return Promise.all(results.map(async params => {
|
|
68
|
-
const {typedTemplate, variables} = params;
|
|
69
|
-
return {
|
|
70
|
-
params,
|
|
71
|
-
body: await evaluateTemplate({
|
|
72
|
-
workflow, exchange, typedTemplate, variables
|
|
73
|
-
})
|
|
74
|
-
};
|
|
75
|
-
}));
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
async function _getIssueRequestParams({workflow, exchange, step}) {
|
|
68
|
+
export function getIssueRequestsParams({workflow, exchange, step}) {
|
|
79
69
|
// use any templates from workflow and variables from exchange to produce
|
|
80
70
|
// credentials to be issued; issue via the configured issuer instance
|
|
81
71
|
const {credentialTemplates = []} = workflow;
|
|
82
72
|
if(!(credentialTemplates.length > 0)) {
|
|
83
|
-
// no issue
|
|
73
|
+
// no issue requests params
|
|
84
74
|
return [];
|
|
85
75
|
}
|
|
86
76
|
|
|
87
|
-
if(!
|
|
77
|
+
if(!workflow.steps ||
|
|
88
78
|
(!step.issueRequests && Object.keys(workflow.steps).length === 1)) {
|
|
89
79
|
// backwards-compatibility: deprecated workflows with no step or a single
|
|
90
80
|
// step do not explicitly define `issueRequests` but instead consider each
|
|
@@ -93,9 +83,14 @@ async function _getIssueRequestParams({workflow, exchange, step}) {
|
|
|
93
83
|
return credentialTemplates.map(typedTemplate => ({typedTemplate}));
|
|
94
84
|
}
|
|
95
85
|
|
|
96
|
-
|
|
86
|
+
if(!step.issueRequests) {
|
|
87
|
+
// no issue requests params
|
|
88
|
+
return [];
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// resolve all issue requests params
|
|
97
92
|
const variables = getTemplateVariables({workflow, exchange});
|
|
98
|
-
return
|
|
93
|
+
return step.issueRequests.map(r => {
|
|
99
94
|
// find the typed template to use
|
|
100
95
|
let typedTemplate;
|
|
101
96
|
if(r.credentialTemplateIndex !== undefined) {
|
|
@@ -138,6 +133,23 @@ async function _getIssueRequestParams({workflow, exchange, step}) {
|
|
|
138
133
|
params.result = r.result;
|
|
139
134
|
}
|
|
140
135
|
return params;
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
async function _evalIssueRequests({
|
|
140
|
+
workflow, exchange, step, issueRequestsParams, filter
|
|
141
|
+
}) {
|
|
142
|
+
// evaluate all issue requests in parallel
|
|
143
|
+
const results = issueRequestsParams ??
|
|
144
|
+
getIssueRequestsParams({workflow, exchange, step}).filter(filter);
|
|
145
|
+
return Promise.all(results.map(async params => {
|
|
146
|
+
const {typedTemplate, variables} = params;
|
|
147
|
+
return {
|
|
148
|
+
params,
|
|
149
|
+
body: await evaluateTemplate({
|
|
150
|
+
workflow, exchange, typedTemplate, variables
|
|
151
|
+
})
|
|
152
|
+
};
|
|
141
153
|
}));
|
|
142
154
|
}
|
|
143
155
|
|
|
@@ -12,6 +12,9 @@ import {randomUUID} from 'node:crypto';
|
|
|
12
12
|
|
|
13
13
|
const {util: {BedrockError}} = bedrock;
|
|
14
14
|
|
|
15
|
+
const ENCRYPTED_RESPONSE_MODES = new Set([
|
|
16
|
+
'direct_post.jwt', 'dc_api.jwt', 'dc_api'
|
|
17
|
+
]);
|
|
15
18
|
const OID4VP_JWT_TYP = 'oauth-authz-req+jwt';
|
|
16
19
|
const TEXT_ENCODER = new TextEncoder();
|
|
17
20
|
|
|
@@ -28,6 +31,7 @@ export async function create({
|
|
|
28
31
|
// get params from step OID4VP client profile to apply to the AR
|
|
29
32
|
const {
|
|
30
33
|
client_id, client_id_scheme,
|
|
34
|
+
dcql_query,
|
|
31
35
|
nonce,
|
|
32
36
|
presentation_definition,
|
|
33
37
|
response_mode, response_uri
|
|
@@ -37,6 +41,10 @@ export async function create({
|
|
|
37
41
|
// client_id_scheme (draft versions of OID4VP use this param)
|
|
38
42
|
authorizationRequest.client_id_scheme = client_id_scheme ?? 'redirect_uri';
|
|
39
43
|
|
|
44
|
+
// dcql_query
|
|
45
|
+
authorizationRequest.dcql_query = dcql_query ??
|
|
46
|
+
authorizationRequest.dcql_query;
|
|
47
|
+
|
|
40
48
|
// presentation_definition
|
|
41
49
|
authorizationRequest.presentation_definition = presentation_definition ??
|
|
42
50
|
authorizationRequest.presentation_definition;
|
|
@@ -175,8 +183,8 @@ async function _createClientMetaData({
|
|
|
175
183
|
client_metadata.require_signed_request_object = true;
|
|
176
184
|
}
|
|
177
185
|
|
|
178
|
-
//
|
|
179
|
-
if(authorizationRequest.response_mode
|
|
186
|
+
// offer encryption options for encrypted response modes
|
|
187
|
+
if(ENCRYPTED_RESPONSE_MODES.has(authorizationRequest.response_mode)) {
|
|
180
188
|
// generate ECDH-ES P-256 key
|
|
181
189
|
const kp = await generateKeyPair('ECDH-ES', {
|
|
182
190
|
crv: 'P-256', extractable: true
|
|
@@ -8,7 +8,6 @@ import {
|
|
|
8
8
|
} from '../../schemas/bedrock-vc-workflow.js';
|
|
9
9
|
import {compile} from '@bedrock/validation';
|
|
10
10
|
import {oid4vp} from '@digitalbazaar/oid4-client';
|
|
11
|
-
import {unenvelopePresentation} from '../helpers.js';
|
|
12
11
|
|
|
13
12
|
const {util: {BedrockError}} = bedrock;
|
|
14
13
|
|
|
@@ -27,58 +26,46 @@ bedrock.events.on('bedrock.init', () => {
|
|
|
27
26
|
});
|
|
28
27
|
});
|
|
29
28
|
|
|
30
|
-
export async function parse({
|
|
29
|
+
export async function parse({
|
|
30
|
+
req, exchange, clientProfileId, authorizationRequest
|
|
31
|
+
} = {}) {
|
|
31
32
|
try {
|
|
32
33
|
const {body} = req;
|
|
33
34
|
const {
|
|
34
|
-
responseMode, parsed, protectedHeader
|
|
35
|
+
responseMode, parsed, protectedHeader,
|
|
36
|
+
recipientPublicJwk, recipientPublicJwkThumbprint,
|
|
37
|
+
vpTokenMediaType
|
|
35
38
|
} = await oid4vp.verifier.parseAuthorizationResponse({
|
|
36
39
|
body,
|
|
37
40
|
getDecryptParameters() {
|
|
38
41
|
return _getDecryptParameters({exchange, clientProfileId});
|
|
39
|
-
}
|
|
42
|
+
},
|
|
43
|
+
authorizationRequest
|
|
40
44
|
});
|
|
41
45
|
|
|
42
|
-
// validate parsed presentation submission
|
|
46
|
+
// validate parsed presentation submission if given
|
|
43
47
|
const {presentationSubmission} = parsed;
|
|
44
|
-
|
|
48
|
+
if(presentationSubmission) {
|
|
49
|
+
_validate(VALIDATORS.presentationSubmission, presentationSubmission);
|
|
50
|
+
}
|
|
45
51
|
|
|
46
52
|
// obtain `presentation` and optional `envelope` from parsed `vpToken`
|
|
47
53
|
const {vpToken} = parsed;
|
|
48
54
|
let presentation;
|
|
49
55
|
let envelope;
|
|
50
56
|
|
|
51
|
-
if(
|
|
52
|
-
|
|
53
|
-
})) {
|
|
54
|
-
// `vp_token` is declared to be a base64url-encoded mDL device response
|
|
55
|
-
presentation = {
|
|
56
|
-
'@context': VC_CONTEXT_2,
|
|
57
|
-
id: `data:application/mdl-vp-token,${vpToken}`,
|
|
58
|
-
type: 'EnvelopedVerifiablePresentation'
|
|
59
|
-
};
|
|
60
|
-
} else if(typeof vpToken === 'string') {
|
|
61
|
-
// FIXME: remove unenveloping here and delegate it to VC API verifier;
|
|
62
|
-
// FIXME: check if envelope matches submission once verified
|
|
63
|
-
const {
|
|
64
|
-
envelope: raw, presentation: contents, format
|
|
65
|
-
} = await unenvelopePresentation({
|
|
66
|
-
envelopedPresentation: vpToken,
|
|
67
|
-
// FIXME: check `presentationSubmission` for VP format
|
|
68
|
-
format: 'application/jwt'
|
|
69
|
-
});
|
|
70
|
-
_validate(VALIDATORS.presentation, contents);
|
|
57
|
+
if(vpTokenMediaType !== 'application/vp') {
|
|
58
|
+
// `vp_token` contains some enveloped format
|
|
71
59
|
presentation = {
|
|
72
60
|
'@context': VC_CONTEXT_2,
|
|
73
|
-
id: `data:${
|
|
61
|
+
id: `data:${vpTokenMediaType},${vpToken}`,
|
|
74
62
|
type: 'EnvelopedVerifiablePresentation'
|
|
75
63
|
};
|
|
76
|
-
envelope = {
|
|
64
|
+
envelope = {mediaType: vpTokenMediaType};
|
|
77
65
|
} else {
|
|
78
|
-
// simplest case: `vpToken` is a VP; validate it
|
|
66
|
+
// simplest case: `vpToken` is a VP; validate it against basic schema
|
|
79
67
|
presentation = vpToken;
|
|
80
68
|
_validate(VALIDATORS.presentation, presentation);
|
|
81
|
-
// FIXME: validate VP against presentation submission
|
|
82
69
|
}
|
|
83
70
|
|
|
84
71
|
return {
|
|
@@ -86,7 +73,9 @@ export async function parse({req, exchange, clientProfileId} = {}) {
|
|
|
86
73
|
presentationSubmission,
|
|
87
74
|
presentation,
|
|
88
75
|
envelope,
|
|
89
|
-
protectedHeader
|
|
76
|
+
protectedHeader,
|
|
77
|
+
recipientPublicJwk,
|
|
78
|
+
recipientPublicJwkThumbprint
|
|
90
79
|
};
|
|
91
80
|
} catch(cause) {
|
|
92
81
|
throw new BedrockError(
|
package/lib/oid4/http.js
CHANGED
|
@@ -405,7 +405,7 @@ function _normalizeCredentials({verifiablePresentation}) {
|
|
|
405
405
|
// use raw format for each credential
|
|
406
406
|
const {verifiableCredential} = verifiablePresentation;
|
|
407
407
|
return verifiableCredential.map(vc => {
|
|
408
|
-
// parse any enveloped VC into its non-VC format
|
|
408
|
+
// parse any JWT-enveloped VC into its non-VC format
|
|
409
409
|
if(vc.type === 'EnvelopedVerifiableCredential' &&
|
|
410
410
|
vc.id?.startsWith('data:application/jwt,')) {
|
|
411
411
|
return vc.id.slice('data:application/jwt,'.length);
|