@bedrock/vc-delivery 7.11.3 → 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.
@@ -288,10 +288,21 @@ export class ExchangeProcessor {
288
288
  // 4.2. Call subalgorithm `prepareStep`, passing `workflow`,
289
289
  // `exchange`, `step`, `receivedPresentation`, and
290
290
  // `receivedPresentationRequest` to perform any protocol-specific
291
- // custom step preparation.
292
- await prepareStep?.({
293
- workflow, exchange, step, receivedPresentation
291
+ // custom step preparation. If `prepareStep` returns a `prepareResult`
292
+ // with `receivedPresentation` and/or `receivedPresentationRequest` set,
293
+ // then update `receivedPresentation` and/or
294
+ // `receivedPresentationRequest` accordingly.
295
+ const prepareResult = await prepareStep?.({
296
+ workflow, exchange, step,
297
+ receivedPresentation, receivedPresentationRequest
294
298
  });
299
+ if(prepareResult?.receivedPresentation !== undefined) {
300
+ receivedPresentation = prepareResult.receivedPresentation;
301
+ }
302
+ if(prepareResult?.receivedPresentationRequest) {
303
+ receivedPresentationRequest =
304
+ prepareResult.receivedPresentationRequest;
305
+ }
295
306
 
296
307
  // 4.3. If `receivedPresentation` is set, then call the
297
308
  // `validateReceivedPresentation` sub-algorithm, passing `workflow`,
@@ -312,20 +323,27 @@ export class ExchangeProcessor {
312
323
  });
313
324
  }
314
325
 
315
- // 4.5. Set `isInputRequired` to the result of calling
326
+ // 4.5. If the implementation supports blocking callbacks that can
327
+ // return results to be added to exchange variables (or return errors),
328
+ // call the callback and store its results in
329
+ // `exchange.variables.results[exchange.step].callbackResults` or
330
+ // throw any error received.
331
+ // FIXME: to be implemented
332
+
333
+ // 4.6. Set `isInputRequired` to the result of calling
316
334
  // `inputRequired({step, receivedPresentation})`.
317
335
  const isInputRequired = await inputRequired?.({
318
336
  workflow, exchange, step, receivedPresentation
319
337
  }) ?? false;
320
338
 
321
- // 4.6. If `isInputRequired` is true:
339
+ // 4.7. If `isInputRequired` is true:
322
340
  if(isInputRequired) {
323
- // 4.6.1. If `response` is `null`, set it to an empty object.
341
+ // 4.7.1. If `response` is `null`, set it to an empty object.
324
342
  if(!response) {
325
343
  response = {};
326
344
  }
327
345
 
328
- // 4.6.2. If `step.verifiablePresentationRequest` is set, call
346
+ // 4.7.2. If `step.verifiablePresentationRequest` is set, call
329
347
  // the `createVerifiablePresentationRequest` sub-algorithm, passing
330
348
  // `workflow`, `exchange`, `step`, and `response`.
331
349
  if(step.verifiablePresentationRequest) {
@@ -334,12 +352,13 @@ export class ExchangeProcessor {
334
352
  });
335
353
  }
336
354
 
337
- // 4.6.3. Save the exchange and return `response`.
355
+ // 4.7.3. Save the exchange (and call any non-blocking callback
356
+ // in the step) and return `response`.
338
357
  await _updateExchange({workflow, exchange, meta, step});
339
358
  return response;
340
359
  }
341
360
 
342
- // 4.7. Set `issueToClient` to `true` if `step.issueRequests` includes
361
+ // 4.8. Set `issueToClient` to `true` if `step.issueRequests` includes
343
362
  // any issuer requests for VCs that are to be sent to the client
344
363
  // (`issueRequest.result` is NOT set), otherwise set it to `false`.
345
364
  const issueRequestsParams = getIssueRequestsParams({
@@ -347,27 +366,28 @@ export class ExchangeProcessor {
347
366
  });
348
367
  const issueToClient = issueRequestsParams.some(p => !p.result);
349
368
 
350
- // 4.8. If `step.verifiablePresentation` is set or `issueToClient` is
369
+ // 4.9. If `step.verifiablePresentation` is set or `issueToClient` is
351
370
  // `true`:
352
371
  if(step.verifiablePresentation || issueToClient) {
353
- // 4.8.1. If `response` is not `null`
372
+ // 4.9.1. If `response` is not `null`
354
373
  if(response) {
355
- // 4.8.1.1. If `response.verifiablePresentationRequest` is not set,
374
+ // 4.9.1.1. If `response.verifiablePresentationRequest` is not set,
356
375
  // set it to an empty object (to indicate that the exchange is
357
376
  // not yet complete).
358
377
  if(!response.verifiablePresentationRequest) {
359
378
  response.verifiablePresentationRequest = {};
360
379
  }
361
- // 4.8.1.2. Save the exchange.
380
+ // 4.9.1.2. Save the exchange (and call any non-blocking callback
381
+ // in the step).
362
382
  await _updateExchange({workflow, exchange, meta, step});
363
- // 4.8.1.3. Return `response`.
383
+ // 4.9.1.3. Return `response`.
364
384
  return response;
365
385
  }
366
386
 
367
- // 4.8.2. Set `response` to an empty object.
387
+ // 4.9.2. Set `response` to an empty object.
368
388
  response = {};
369
389
 
370
- // 4.8.3. If `step.verifiablePresentation` is set, set
390
+ // 4.9.3. If `step.verifiablePresentation` is set, set
371
391
  // `response.verifiablePresentation` to a copy of it, otherwise
372
392
  // set `response.verifiablePresentation` to a new, empty,
373
393
  // Verifiable Presentation (using VCDM 2.0 by default, but a custom
@@ -380,14 +400,14 @@ export class ExchangeProcessor {
380
400
  // issuance has been triggered
381
401
  issuanceTriggered = true;
382
402
 
383
- // 4.9. Perform every issue request (optionally in parallel), returning
384
- // an error response to the client if any fails (note: implementations
385
- // can optionally implement failure recovery or retry issue requests at
386
- // their own discretion):
387
- // 4.9.1. For each issue request where `result` is set to an exchange
403
+ // 4.10. Perform every issue request (optionally in parallel),
404
+ // returning an error response to the client if any fails (note:
405
+ // implementations can optionally implement failure recovery or retry
406
+ // issue requests at their own discretion):
407
+ // 4.10.1. For each issue request where `result` is set to an exchange
388
408
  // variable path or name, save the issued credential in the referenced
389
409
  // exchange variable.
390
- // 4.9.2. For each issue request where `result` is not specified, save
410
+ // 4.10.2. For each issue request where `result` is not specified, save
391
411
  // the issued credential in `response.verifiablePresentation`, i.e.,
392
412
  // for a VCDM presentation, append the issued credential to
393
413
  // `response.verifiablePresentation.verifiableCredential`.
@@ -396,44 +416,45 @@ export class ExchangeProcessor {
396
416
  verifiablePresentation: response?.verifiablePresentation
397
417
  });
398
418
 
399
- // 4.10. If `response.verifiablePresentation` is set and the step
419
+ // 4.11. If `response.verifiablePresentation` is set and the step
400
420
  // configuration indicates it should be signed, sign the presentation
401
421
  // (e.g., by using a VCALM holder instance's `/presentations/create`
402
422
  // endpoint).
403
423
  // FIXME: implement
404
424
  //if(response?.verifiablePresentation) {}
405
425
 
406
- // 4.11. If `step.redirectUrl` is set:
426
+ // 4.12. If `step.redirectUrl` is set:
407
427
  if(step.redirectUrl) {
408
- // 4.11.1. If `response` is `null` then set it to an empty object.
428
+ // 4.12.1. If `response` is `null` then set it to an empty object.
409
429
  if(!response) {
410
430
  response = {};
411
431
  }
412
- // 4.11.2. Set `response.redirectUrl` to `step.redirectUrl`.
432
+ // 4.12.2. Set `response.redirectUrl` to `step.redirectUrl`.
413
433
  response.redirectUrl = step.redirectUrl;
414
434
  }
415
435
 
416
- // 4.12. If `step.nextStep` is not set then set `exchange.state` to
436
+ // 4.13. If `step.nextStep` is not set then set `exchange.state` to
417
437
  // `complete`.
418
438
  if(!step.nextStep) {
419
439
  exchange.state = 'complete';
420
440
  } else {
421
- // 4.13. Otherwise, delete `exchange.variables.results[step.nextStep]`
441
+ // 4.14. Otherwise, delete `exchange.variables.results[step.nextStep]`
422
442
  // if it exists, and set `exchange.step` to `step.nextStep`.
423
443
  delete exchange.variables.results[step.nextStep];
424
444
  exchange.step = step.nextStep;
425
445
  }
426
446
 
427
- // 4.14. Save the exchange.
447
+ // 4.15. Save the exchange (and call any non-blocking callback in
448
+ // the step).
428
449
  await _updateExchange({workflow, exchange, meta, step});
429
450
 
430
- // 4.15. If `exchange.state` is `complete`, return `response` if it is
451
+ // 4.16. If `exchange.state` is `complete`, return `response` if it is
431
452
  // not `null`, otherwise return an empty object.
432
453
  if(exchange.state === 'complete') {
433
454
  return response ?? {};
434
455
  }
435
456
 
436
- // 4.16. Set `receivedPresentation` to `null`.
457
+ // 4.17. Set `receivedPresentation` to `null`.
437
458
  receivedPresentation = null;
438
459
  }
439
460
  } catch(e) {
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
  }) {
@@ -358,44 +347,6 @@ export function stripStacktrace(error) {
358
347
  return error;
359
348
  }
360
349
 
361
- export async function unenvelopeCredential({
362
- envelopedCredential, format
363
- } = {}) {
364
- const result = _getEnvelope({envelope: envelopedCredential, format});
365
-
366
- // only supported format is VC-JWT at this time
367
- const credential = vcjwt.decodeVCJWTCredential({jwt: result.envelope});
368
- return {credential, ...result};
369
- }
370
-
371
- export async function unenvelopePresentation({
372
- envelopedPresentation, format
373
- } = {}) {
374
- const result = _getEnvelope({envelope: envelopedPresentation, format});
375
-
376
- // only supported format is VC-JWT at this time
377
- const presentation = vcjwt.decodeVCJWTPresentation({jwt: result.envelope});
378
-
379
- // unenvelope any VCs in the presentation
380
- let {verifiableCredential = []} = presentation;
381
- if(!Array.isArray(verifiableCredential)) {
382
- verifiableCredential = [verifiableCredential];
383
- }
384
- if(verifiableCredential.length > 0) {
385
- presentation.verifiableCredential = await Promise.all(
386
- verifiableCredential.map(async vc => {
387
- if(vc?.type !== 'EnvelopedVerifiableCredential') {
388
- return vc;
389
- }
390
- const {credential} = await unenvelopeCredential({
391
- envelopedCredential: vc
392
- });
393
- return credential;
394
- }));
395
- }
396
- return {presentation, ...result};
397
- }
398
-
399
350
  export async function validateStep({step} = {}) {
400
351
  // FIXME: use `ajv` and do JSON schema check
401
352
  if(Object.keys(step).length === 0) {
@@ -423,32 +374,6 @@ export async function validateStep({step} = {}) {
423
374
  }
424
375
  }
425
376
 
426
- function _getEnvelope({envelope, format}) {
427
- const isString = typeof envelope === 'string';
428
- if(isString) {
429
- // supported formats
430
- if(JWT_FORMAT_ALIASES.has(format)) {
431
- format = 'application/jwt';
432
- }
433
- } else {
434
- const {id} = envelope;
435
- if(id?.startsWith('data:application/jwt,')) {
436
- format = 'application/jwt';
437
- envelope = id.slice('data:application/jwt,'.length);
438
- }
439
- }
440
-
441
- if(format === 'application/jwt' && envelope !== undefined) {
442
- return {envelope, format};
443
- }
444
-
445
- throw new BedrockError(
446
- `Unsupported credential or presentation envelope format "${format}".`, {
447
- name: 'NotSupportedError',
448
- details: {httpStatusCode: 400, public: true}
449
- });
450
- }
451
-
452
377
  export function validateVerifiablePresentation({schema, presentation}) {
453
378
  const validate = compile({schema});
454
379
  const {valid, error} = validate(presentation);
@@ -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
- // for response mode `direct_post.jwt`, offer encryption options
179
- if(authorizationRequest.response_mode === 'direct_post.jwt') {
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({req, exchange, clientProfileId} = {}) {
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
- _validate(VALIDATORS.presentationSubmission, presentationSubmission);
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(oid4vp.authzResponse.submitsFormat({
52
- presentationSubmission, format: 'mso_mdoc'
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:${format},${raw}`,
61
+ id: `data:${vpTokenMediaType},${vpToken}`,
74
62
  type: 'EnvelopedVerifiablePresentation'
75
63
  };
76
- envelope = {raw, contents, format};
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);
@@ -138,15 +138,8 @@ export async function processAuthorizationResponse({req, clientProfileId}) {
138
138
  const {config: workflow} = req.serviceObject;
139
139
  const exchangeRecord = await req.getExchange();
140
140
 
141
- // ensure authz response can be parsed
142
- const {
143
- presentation, envelope, presentationSubmission,
144
- responseMode, protectedHeader
145
- } = await parseAuthorizationResponse({
146
- req, exchange: exchangeRecord.exchange, clientProfileId
147
- });
148
-
149
141
  // process exchange and produce result
142
+ let parseResponseResult;
150
143
  const result = {};
151
144
  const exchangeProcessor = new ExchangeProcessor({
152
145
  workflow, exchangeRecord,
@@ -156,15 +149,11 @@ export async function processAuthorizationResponse({req, clientProfileId}) {
156
149
  });
157
150
  result.authorizationRequest = authorizationRequest;
158
151
 
159
- // ensure response mode matches
160
- if(responseMode !== authorizationRequest.response_mode) {
161
- throw new BedrockError(
162
- `The used response mode ("${responseMode}") does not match the ` +
163
- `expected response mode ("${authorizationRequest.response_mode}".`, {
164
- name: 'ConstraintError',
165
- details: {httpStatusCode: 400, public: true}
166
- });
167
- }
152
+ // ensure authz response can be parsed
153
+ parseResponseResult = await parseAuthorizationResponse({
154
+ req, exchange: exchangeRecord.exchange, clientProfileId,
155
+ authorizationRequest
156
+ });
168
157
 
169
158
  // only mark exchange complete if there is nothing to be issued; this
170
159
  // handles same-step OID4VCI+OID4VP case
@@ -178,6 +167,9 @@ export async function processAuthorizationResponse({req, clientProfileId}) {
178
167
  if(redirect_uri) {
179
168
  result.redirect_uri = redirect_uri;
180
169
  }
170
+
171
+ const {presentation: receivedPresentation} = parseResponseResult;
172
+ return {receivedPresentation};
181
173
  },
182
174
  inputRequired({step}) {
183
175
  // indicate input always required to avoid automatically advancing
@@ -199,26 +191,61 @@ export async function processAuthorizationResponse({req, clientProfileId}) {
199
191
  verifyPresentationOptions.challenge = authorizationRequest.nonce;
200
192
  verifyPresentationOptions.domain = authorizationRequest.response_uri;
201
193
 
202
- // if `direct_post.jwt` used w/ mDL presentation, include `mDL` options
203
- if(responseMode === 'direct_post.jwt' &&
204
- oid4vp.authzResponse.submitsFormat({
205
- presentationSubmission, format: 'mso_mdoc'
206
- })) {
194
+ // FIXME: OID4VP 1.0+ does not have a presentation submission
195
+ // handle mDL submission
196
+ const {envelope} = parseResponseResult;
197
+ if(envelope?.mediaType === 'application/mdl-vp-token') {
198
+ // generate `handover` for mDL verification
199
+ let handover;
200
+
201
+ // common `handover` parameters:
202
+ const origin = authorizationRequest?.expected_origins?.[0] ??
203
+ new URL(authorizationRequest.response_uri).origin;
204
+ const nonce = authorizationRequest.nonce;
205
+
206
+ // `direct_post.jwt` => ISO18013-7 Annex B
207
+ // FIXME: same response mode is also used for OID4VP 1.0 with
208
+ // `OpenID4VPHandover` where `presentationSubmission` will be absent;
209
+ // this is not yet supported
210
+ // https://openid.net/specs/openid-4-verifiable-presentations-1_0.html#name-invocation-via-redirects
211
+ const {responseMode} = parseResponseResult;
212
+ if(responseMode === 'direct_post.jwt') {
213
+ handover = {
214
+ type: 'AnnexBHandover',
215
+ // per ISO 18013-7 B the `mdocGeneratedNonce` is base64url-encoded
216
+ // and put into the `apu` protected header parameter, so parse that
217
+ // here and convert it to a UTF-8 string instead
218
+ mdocGeneratedNonce: Buffer
219
+ .from(parseResponseResult.protectedHeader?.apu ?? '', 'base64url')
220
+ .toString('utf8'),
221
+ clientId: authorizationRequest.client_id,
222
+ responseUri: authorizationRequest.response_uri,
223
+ verifierGeneratedNonce: nonce
224
+ };
225
+ } else if(responseMode === 'dc_api') {
226
+ // `dc_api` => ISO18013-7 Annex C
227
+ handover = {
228
+ type: 'dcapi',
229
+ origin,
230
+ nonce,
231
+ recipientPublicJwk: parseResponseResult.recipientPublicJwk
232
+ };
233
+ } else if(responseMode === 'dc_api.jwt') {
234
+ // `dc_api.jwt` => ISO18013-7 Annex D
235
+ handover = {
236
+ type: 'OpenID4VPDCAPIHandover',
237
+ origin,
238
+ nonce,
239
+ jwkThumbprint: parseResponseResult.recipientPublicJwkThumbprint
240
+ };
241
+ }
242
+
207
243
  verifyPresentationOptions.mdl = {
208
244
  ...verifyPresentationOptions.mdl,
209
- // note: in session transcript:
210
- // `domain` option above will be used for `responseUri`
211
- // `challenge` option above will be used for `verifierGeneratedNonce`
212
- // so do not send here to avoid redundancy
213
- sessionTranscript: {
214
- // per ISO 18013-7 the `mdocGeneratedNonce` is base64url-encoded
215
- // and put into the `apu` protected header parameter -- and the
216
- // VC API `mdl.sessionTranscript` option expects the
217
- // `mdocGeneratedNonce` to be base64url-encoded, so we can pass
218
- // it straight through
219
- mdocGeneratedNonce: protectedHeader.apu,
220
- clientId: authorizationRequest.client_id
221
- }
245
+ // send encoded mDL `sessionTranscript`
246
+ sessionTranscript: Buffer
247
+ .from(await oid4vp.mdl.encodeSessionTranscript({handover}))
248
+ .toString('base64url')
222
249
  };
223
250
  }
224
251
 
@@ -234,6 +261,7 @@ export async function processAuthorizationResponse({req, clientProfileId}) {
234
261
  });
235
262
 
236
263
  // save OID4VP results in exchange
264
+ const {presentationSubmission} = parseResponseResult;
237
265
  exchange.variables.results[exchange.step] = {
238
266
  ...exchange.variables.results[exchange.step],
239
267
  openId: {
@@ -242,7 +270,9 @@ export async function processAuthorizationResponse({req, clientProfileId}) {
242
270
  presentationSubmission
243
271
  }
244
272
  };
245
- if(envelope) {
273
+ // FIXME: do w/o `parseResponseResult.envelope` to eliminate envelope
274
+ // parsing (let verifier do it)
275
+ if(parseResponseResult.envelope) {
246
276
  // include enveloped VP in step result
247
277
  exchange.variables.results[exchange.step]
248
278
  .envelopedPresentation = presentation;
@@ -251,7 +281,7 @@ export async function processAuthorizationResponse({req, clientProfileId}) {
251
281
  return verifyResult;
252
282
  }
253
283
  });
254
- await exchangeProcessor.process({receivedPresentation: presentation});
284
+ await exchangeProcessor.process();
255
285
 
256
286
  return result;
257
287
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bedrock/vc-delivery",
3
- "version": "7.11.3",
3
+ "version": "7.12.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.6.3",
43
+ "@digitalbazaar/oid4-client": "^5.8.0",
44
44
  "@digitalbazaar/vc": "^7.2.0",
45
45
  "@digitalbazaar/webkms-client": "^14.2.0",
46
46
  "assert-plus": "^1.0.0",
@@ -460,6 +460,14 @@ const oid4vpClientProfile = {
460
460
  client_id: {type: 'string'},
461
461
  client_id_scheme: {type: 'string'},
462
462
  client_metadata: {type: 'object'},
463
+ dcql_query: {type: 'object'},
464
+ expected_origins: {
465
+ type: 'array',
466
+ minItems: 1,
467
+ items: {
468
+ type: 'string'
469
+ }
470
+ },
463
471
  nonce: {type: 'string'},
464
472
  presentation_definition: {type: 'object'},
465
473
  response_mode: {type: 'string'},