@bedrock/vc-delivery 1.0.0 → 3.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/lib/http.js CHANGED
@@ -1,13 +1,14 @@
1
1
  /*!
2
- * Copyright (c) 2018-2022 Digital Bazaar, Inc. All rights reserved.
2
+ * Copyright (c) 2018-2023 Digital Bazaar, Inc. All rights reserved.
3
3
  */
4
+ import * as _openId from './openId.js';
4
5
  import * as bedrock from '@bedrock/core';
5
6
  import * as exchanges from './exchanges.js';
6
- import * as oidc4vci from './oidc4vci.js';
7
7
  import {createChallenge as _createChallenge, verify} from './verify.js';
8
8
  import {
9
9
  createExchangeBody, useExchangeBody
10
10
  } from '../schemas/bedrock-vc-exchanger.js';
11
+ import {exportJWK, generateKeyPair, importJWK} from 'jose';
11
12
  import {metering, middleware} from '@bedrock/service-core';
12
13
  import {asyncHandler} from '@bedrock/express';
13
14
  import bodyParser from 'body-parser';
@@ -70,7 +71,7 @@ export async function addRoutes({app, service} = {}) {
70
71
  try {
71
72
  const {config} = req.serviceObject;
72
73
  const {
73
- ttl, oidc4vci, variables = {},
74
+ ttl, openId, variables = {},
74
75
  // allow steps to be skipped by creator as needed
75
76
  step = config.initialStep
76
77
  } = req.body;
@@ -83,13 +84,41 @@ export async function addRoutes({app, service} = {}) {
83
84
  });
84
85
  }
85
86
 
87
+ // perform key generation if requested
88
+ if(openId?.oauth2?.generateKeyPair) {
89
+ const {oauth2} = openId;
90
+ const {algorithm} = oauth2.generateKeyPair;
91
+ const kp = await generateKeyPair(algorithm, {extractable: true});
92
+ const [privateKeyJwk, publicKeyJwk] = await Promise.all([
93
+ exportJWK(kp.privateKey),
94
+ exportJWK(kp.publicKey),
95
+ ]);
96
+ oauth2.keyPair = {privateKeyJwk, publicKeyJwk};
97
+ delete oauth2.generateKeyPair;
98
+ } else if(openId) {
99
+ // ensure key pair can be imported
100
+ try {
101
+ const {oauth2: {keyPair}} = openId;
102
+ await Promise.all([
103
+ importJWK(keyPair.privateKeyJwk),
104
+ importJWK(keyPair.publicKeyJwk)
105
+ ]);
106
+ } catch(e) {
107
+ throw new BedrockError('Could not import OpenID OAuth2 key pair.', {
108
+ name: 'DataError',
109
+ details: {httpStatusCode: 400, public: true},
110
+ cause: e
111
+ });
112
+ }
113
+ }
114
+
86
115
  // insert exchange
87
116
  const {id: exchangerId} = config;
88
117
  const exchange = {
89
118
  id: await generateRandom(),
90
119
  ttl,
91
120
  variables,
92
- oidc4vci,
121
+ openId,
93
122
  step
94
123
  };
95
124
  await exchanges.insert({exchangerId, exchange});
@@ -189,7 +218,7 @@ export async function addRoutes({app, service} = {}) {
189
218
  res.json({verifiablePresentation});
190
219
  }));
191
220
 
192
- // create OIDC4VCI routes to be used with each individual exchange
193
- await oidc4vci.createRoutes(
221
+ // create OID4VCI routes to be used with each individual exchange
222
+ await _openId.createRoutes(
194
223
  {app, exchangeRoute: routes.exchange, getConfigMiddleware, getExchange});
195
224
  }
package/lib/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * Copyright (c) 2022 Digital Bazaar, Inc. All rights reserved.
2
+ * Copyright (c) 2022-2023 Digital Bazaar, Inc. All rights reserved.
3
3
  */
4
4
  import * as bedrock from '@bedrock/core';
5
5
  import * as exchangerSchemas from '../schemas/bedrock-vc-exchanger.js';
@@ -1,10 +1,10 @@
1
1
  /*!
2
- * Copyright (c) 2022 Digital Bazaar, Inc. All rights reserved.
2
+ * Copyright (c) 2022-2023 Digital Bazaar, Inc. All rights reserved.
3
3
  */
4
4
  import * as exchanges from './exchanges.js';
5
5
  import {importJWK, SignJWT} from 'jose';
6
6
  import {
7
- oidc4vciCredentialBody, oidc4vciTokenBody
7
+ openIdBatchCredentialBody, openIdCredentialBody, openIdTokenBody
8
8
  } from '../schemas/bedrock-vc-exchanger.js';
9
9
  import {asyncHandler} from '@bedrock/express';
10
10
  import bodyParser from 'body-parser';
@@ -15,10 +15,10 @@ import {timingSafeEqual} from 'node:crypto';
15
15
  import {createValidateMiddleware as validate} from '@bedrock/validation';
16
16
  import {verifyDidProofJwt} from './verify.js';
17
17
 
18
- /* NOTE: Parts of the OIDC4VCI design imply tight integration between the
18
+ /* NOTE: Parts of the OID4VCI design imply tight integration between the
19
19
  authorization server and the credential issuance / delivery server. This
20
20
  file provides the routes for both and treats them as integrated; supporting
21
- the OIDC4VCI pre-authz code flow only as a result. However, we also try to
21
+ the OID4VCI pre-authz code flow only as a result. However, we also try to
22
22
  avoid tight-coupling where possible to enable the non-pre-authz code flow
23
23
  that would use, somehow, a separate authorization server.
24
24
 
@@ -30,7 +30,7 @@ authentic without breaking some abstraction around how the Authorization
30
30
  Server is implemented behind its API. Here we do not implement this option,
31
31
  instead, if a challenge is required, the credential delivery server will send
32
32
  an error with the challenge nonce if one was not provided in the payload to the
33
- credential endpoint. This error follows the OIDC4VCI spec and avoids this
33
+ credential endpoint. This error follows the OID4VCI spec and avoids this
34
34
  particular tight coupling.
35
35
 
36
36
  Other tight couplings cannot be avoided at this time -- such as the fact that
@@ -43,17 +43,18 @@ instantiating a new authorization server instance per VC exchange. */
43
43
  const PRE_AUTH_GRANT_TYPE =
44
44
  'urn:ietf:params:oauth:grant-type:pre-authorized_code';
45
45
 
46
- // creates OIDC4VCI Authorization Server + Credential Delivery Server
46
+ // creates OID4VCI Authorization Server + Credential Delivery Server
47
47
  // endpoints for each individual exchange
48
48
  export async function createRoutes({
49
49
  app, exchangeRoute, getConfigMiddleware, getExchange
50
50
  } = {}) {
51
- const oidc4vciRoute = `${exchangeRoute}/oidc4vci`;
51
+ const openIdRoute = `${exchangeRoute}/openid`;
52
52
  const routes = {
53
53
  asMetadata: `/.well-known/oauth-authorization-server${exchangeRoute}`,
54
- credential: `${oidc4vciRoute}/credential`,
55
- token: `${oidc4vciRoute}/token`,
56
- jwks: `${oidc4vciRoute}/jwks`
54
+ batchCredential: `${openIdRoute}/batch_credential`,
55
+ credential: `${openIdRoute}/credential`,
56
+ token: `${openIdRoute}/token`,
57
+ jwks: `${openIdRoute}/jwks`
57
58
  };
58
59
 
59
60
  // urlencoded body parser (extended=true for rich JSON-like representation)
@@ -72,9 +73,11 @@ export async function createRoutes({
72
73
  const exchangeId = `${exchanger.id}/exchanges/${req.params.exchangeId}`;
73
74
  const oauth2Config = {
74
75
  issuer: exchangeId,
75
- jwks_uri: `${exchangeId}/oidc4vci/jwks`,
76
- token_endpoint: `${exchangeId}/oidc4vci/token`,
77
- credential_endpoint: `${exchangeId}/oidc4vci/credential`
76
+ jwks_uri: `${exchangeId}/openid/jwks`,
77
+ token_endpoint: `${exchangeId}/openid/token`,
78
+ credential_endpoint: `${exchangeId}/openid/credential`,
79
+ batch_credential_endpoint: `${exchangeId}/openid/batch_credential`
80
+ // FIXME: add `credentials_supported`
78
81
  };
79
82
  res.json(oauth2Config);
80
83
  }));
@@ -88,13 +91,13 @@ export async function createRoutes({
88
91
  getExchange,
89
92
  asyncHandler(async (req, res) => {
90
93
  const {exchange} = await req.exchange;
91
- if(!exchange.oidc4vci) {
94
+ if(!exchange.openId) {
92
95
  // FIXME: improve error
93
96
  // unsupported protocol for the exchange
94
97
  throw new Error('unsupported protocol');
95
98
  }
96
99
  // serve exchange's public key
97
- res.json({keys: [exchange.oidc4vci.oauth2.keyPair.publicKeyJwk]});
100
+ res.json({keys: [exchange.openId.oauth2.keyPair.publicKeyJwk]});
98
101
  }));
99
102
 
100
103
  // an authorization server endpoint
@@ -105,13 +108,13 @@ export async function createRoutes({
105
108
  routes.token,
106
109
  cors(),
107
110
  urlencoded,
108
- validate({bodySchema: oidc4vciTokenBody}),
111
+ validate({bodySchema: openIdTokenBody}),
109
112
  getConfigMiddleware,
110
113
  getExchange,
111
114
  asyncHandler(async (req, res) => {
112
115
  const exchangeRecord = await req.exchange;
113
116
  const {exchange} = exchangeRecord;
114
- if(!exchange.oidc4vci) {
117
+ if(!exchange.openId) {
115
118
  // FIXME: improve error
116
119
  // unsupported protocol for the exchange
117
120
  throw new Error('unsupported protocol');
@@ -142,7 +145,7 @@ export async function createRoutes({
142
145
  }
143
146
 
144
147
  // validate grant type
145
- const {oidc4vci: {preAuthorizedCode: expectedCode}} = exchange;
148
+ const {openId: {preAuthorizedCode: expectedCode}} = exchange;
146
149
  if(expectedCode) {
147
150
  // ensure expected pre-authz code matches
148
151
  if(!timingSafeEqual(
@@ -173,7 +176,7 @@ export async function createRoutes({
173
176
  app.post(
174
177
  routes.credential,
175
178
  cors(),
176
- validate({bodySchema: oidc4vciCredentialBody}),
179
+ validate({bodySchema: openIdCredentialBody}),
177
180
  getConfigMiddleware,
178
181
  getExchange,
179
182
  asyncHandler(async (req, res) => {
@@ -184,8 +187,17 @@ export async function createRoutes({
184
187
  Authorization: BEARER czZCaGRSa3F0MzpnWDFmQmF0M2JW
185
188
 
186
189
  {
187
- "type": "https://did.example.org/healthCard"
188
190
  "format": "ldp_vc",
191
+ "credential_description": {
192
+ "@context": [
193
+ "https://www.w3.org/2018/credentials/v1",
194
+ "https://www.w3.org/2018/credentials/examples/v1"
195
+ ],
196
+ "type": [
197
+ "VerifiableCredential",
198
+ "UniversityDegreeCredential"
199
+ ]
200
+ },
189
201
  "did": "did:example:ebfeb1f712ebc6f1c276e12ec21",
190
202
  "proof": {
191
203
  "proof_type": "jwt",
@@ -193,54 +205,72 @@ export async function createRoutes({
193
205
  }
194
206
  }
195
207
  */
196
- const {config: exchanger} = req.serviceObject;
197
- const exchangeRecord = await req.exchange;
198
- const {exchange} = exchangeRecord;
199
- if(!exchange.oidc4vci) {
200
- // FIXME: improve error
201
- // unsupported protocol for the exchange
202
- throw new Error('unsupported protocol');
208
+ const result = await _processCredentialRequests(
209
+ {req, res, isBatchRequest: false});
210
+ if(!result) {
211
+ // DID proof request response sent
212
+ return;
203
213
  }
204
214
 
205
- // ensure oauth2 access token is valid
206
- await _checkAuthz({req, exchanger, exchange});
215
+ // send VC
216
+ res.json({
217
+ format: 'ldp_vc',
218
+ /* Note: The `/credential` route only supports sending a single VC;
219
+ assume here that this exchanger is configured for a single VC and an
220
+ error code would have been sent to the client to use the batch
221
+ endpoint if there was more than one VC to deliver. */
222
+ credential: result.verifiablePresentation.verifiableCredential[0]
223
+ });
224
+ }));
207
225
 
208
- // process exchange step if present
209
- if(exchange.step) {
210
- const step = exchanger.steps[exchange.step];
226
+ // a batch credential delivery server endpoint
227
+ // receives N credential requests and returns N VCs
228
+ app.options(routes.batchCredential, cors());
229
+ app.post(
230
+ routes.batchCredential,
231
+ cors(),
232
+ validate({bodySchema: openIdBatchCredentialBody}),
233
+ getConfigMiddleware,
234
+ getExchange,
235
+ asyncHandler(async (req, res) => {
236
+ /* Clients must POST, e.g.:
237
+ POST /batch_credential HTTP/1.1
238
+ Host: server.example.com
239
+ Content-Type: application/json
240
+ Authorization: BEARER czZCaGRSa3F0MzpnWDFmQmF0M2JW
211
241
 
212
- // handle JWT DID Proof request; if step requires it, then `proof` must
213
- // be in the credential request
214
- if(step.jwtDidProofRequest) {
215
- // if no proof is in the body...
216
- if(!req?.body?.proof?.jwt) {
217
- return _requestDidProof({res, exchangeRecord});
242
+ {
243
+ credential_requests: [{
244
+ "format": "ldp_vc",
245
+ "credential_description": {
246
+ "@context": [
247
+ "https://www.w3.org/2018/credentials/v1",
248
+ "https://www.w3.org/2018/credentials/examples/v1"
249
+ ],
250
+ "type": [
251
+ "VerifiableCredential",
252
+ "UniversityDegreeCredential"
253
+ ]
254
+ },
255
+ "did": "did:example:ebfeb1f712ebc6f1c276e12ec21",
256
+ "proof": {
257
+ "proof_type": "jwt",
258
+ "jwt": "eyJra...nOzM"
218
259
  }
219
- // verify the DID proof
220
- const {body: {proof: {jwt}}} = req;
221
- const {did} = await verifyDidProofJwt({exchanger, exchange, jwt});
222
- // add `did` to exchange variables
223
- exchange.variables[exchange.step] = {did};
224
- }
260
+ }]
261
+ }
262
+ */
263
+ const result = await _processCredentialRequests(
264
+ {req, res, isBatchRequest: true});
265
+ if(!result) {
266
+ // DID proof request response sent
267
+ return;
225
268
  }
226
269
 
227
- // mark exchange complete
228
- await exchanges.complete({exchangerId: exchanger.id, id: exchange.id});
229
-
230
- // FIXME: decide what the best recovery path is if delivery fails (but no
231
- // replay attack detected) after exchange has been marked complete
232
-
233
- // issue VCs
234
- const {verifiablePresentation} = await issue({exchanger, exchange});
235
-
236
- // send VC
237
- res.json({
238
- format: 'ldp_vc',
239
- /* Note: OIDC4VCI only supports sending a single VC; assume here that
240
- the exchanger is configured to only allow OIDC4VCI when a single
241
- VC is being issued. */
242
- credential: verifiablePresentation.verifiableCredential[0]
243
- });
270
+ // send VCs
271
+ const responses = result.verifiablePresentation.verifiableCredential.map(
272
+ vc => ({format: 'ldp_vc', credential: vc}));
273
+ res.json({credential_responses: responses});
244
274
  }));
245
275
  }
246
276
 
@@ -253,7 +283,7 @@ async function _createExchangeAccessToken({exchanger, exchangeRecord}) {
253
283
  // FIXME: allow per-service-object-instance agent via `signer` and custom
254
284
  // JWT signer code instead?
255
285
  const {exchange} = exchangeRecord;
256
- const {oidc4vci: {oauth2: {keyPair: {privateKeyJwk}}}} = exchange;
286
+ const {openId: {oauth2: {keyPair: {privateKeyJwk}}}} = exchange;
257
287
  const exchangeId = `${exchanger.id}/exchanges/${exchange.id}`;
258
288
  const {accessToken, ttl} = await _createOAuth2AccessToken({
259
289
  privateKeyJwk, audience: exchangeId, action: 'write', target: exchangeId,
@@ -265,9 +295,10 @@ async function _createExchangeAccessToken({exchanger, exchangeRecord}) {
265
295
  async function _createOAuth2AccessToken({
266
296
  privateKeyJwk, audience, action, target, exp, iss, nbf, typ = 'at+jwt'
267
297
  }) {
298
+ const alg = _getAlgFromPrivateKey({privateKeyJwk});
268
299
  const scope = `${action}:${target}`;
269
300
  const builder = new SignJWT({scope})
270
- .setProtectedHeader({alg: 'EdDSA', typ})
301
+ .setProtectedHeader({alg, typ})
271
302
  .setIssuer(iss)
272
303
  .setAudience(audience);
273
304
  let ttl;
@@ -282,14 +313,14 @@ async function _createOAuth2AccessToken({
282
313
  if(nbf !== undefined) {
283
314
  builder.setNotBefore(nbf);
284
315
  }
285
- const key = await importJWK({...privateKeyJwk, alg: 'EdDSA'});
316
+ const key = await importJWK({...privateKeyJwk, alg});
286
317
  const accessToken = await builder.sign(key);
287
318
  return {accessToken, ttl};
288
319
  }
289
320
 
290
321
  async function _checkAuthz({req, exchanger, exchange}) {
291
322
  // optional oauth2 options
292
- const {oauth2} = exchange.oidc4vci;
323
+ const {oauth2} = exchange.openId;
293
324
  const {maxClockSkew} = oauth2;
294
325
 
295
326
  // audience is always the `exchangeId` and cannot be configured; this
@@ -311,6 +342,97 @@ async function _checkAuthz({req, exchanger, exchange}) {
311
342
  await checkAccessToken({req, issuerConfigUrl, maxClockSkew, audience});
312
343
  }
313
344
 
345
+ function _getAlgFromPrivateKey({privateKeyJwk}) {
346
+ if(privateKeyJwk.alg) {
347
+ return privateKeyJwk.alg;
348
+ }
349
+ if(privateKeyJwk.kty === 'EC' && privateKeyJwk.crv) {
350
+ if(privateKeyJwk.crv.startsWith('P-')) {
351
+ return `ES${privateKeyJwk.crv.slice(2)}`;
352
+ }
353
+ if(privateKeyJwk.crv === 'secp256k1') {
354
+ return 'ES256K';
355
+ }
356
+ }
357
+ if(privateKeyJwk.kty === 'OKP' && privateKeyJwk.crv?.startsWith('Ed')) {
358
+ return 'EdDSA';
359
+ }
360
+ if(privateKeyJwk.kty === 'RSA') {
361
+ return 'PS256';
362
+ }
363
+ return 'invalid';
364
+ }
365
+
366
+ async function _processCredentialRequests({req, res, isBatchRequest}) {
367
+ const {config: exchanger} = req.serviceObject;
368
+ const exchangeRecord = await req.exchange;
369
+ const {exchange} = exchangeRecord;
370
+ if(!exchange.openId) {
371
+ // FIXME: improve error
372
+ // unsupported protocol for the exchange
373
+ throw new Error('unsupported protocol');
374
+ }
375
+
376
+ // ensure oauth2 access token is valid
377
+ await _checkAuthz({req, exchanger, exchange});
378
+
379
+ // validate body against expected credential requests
380
+ const {openId: {expectedCredentialRequests}} = exchange;
381
+ let credentialRequests;
382
+ if(isBatchRequest) {
383
+ ({credential_requests: credentialRequests} = req.body);
384
+ } else {
385
+ if(expectedCredentialRequests.length > 1) {
386
+ // clients interacting with exchanges with more than one VC to be
387
+ // delivered must use the "batch credential" endpoint
388
+ // FIXME: improve error
389
+ throw new Error('batch_credential_endpoint must be used');
390
+ }
391
+ credentialRequests = [req.body];
392
+ }
393
+ _assertCredentialRequests(
394
+ {credentialRequests, expectedCredentialRequests});
395
+
396
+ // process exchange step if present
397
+ if(exchange.step) {
398
+ const step = exchanger.steps[exchange.step];
399
+
400
+ // handle JWT DID Proof request; if step requires it, then `proof` must
401
+ // be in every credential request
402
+ if(step.jwtDidProofRequest) {
403
+ // if no proof is one of the requests...
404
+ if(credentialRequests.some(cr => !cr.proof?.jwt)) {
405
+ return _requestDidProof({res, exchangeRecord});
406
+ }
407
+ // verify every DID proof and get resulting DIDs
408
+ const results = await Promise.all(
409
+ credentialRequests.map(async cr => {
410
+ const {proof: {jwt}} = cr;
411
+ const {did} = await verifyDidProofJwt({exchanger, exchange, jwt});
412
+ return did;
413
+ }));
414
+ // require `did` to be the same for every proof
415
+ // FIXME: determine if this needs to be more flexible
416
+ const did = results[0];
417
+ if(results.some(d => did !== d)) {
418
+ // FIXME: improve error
419
+ throw new Error('every DID must be the same');
420
+ }
421
+ // add `did` to exchange variables
422
+ exchange.variables[exchange.step] = {did};
423
+ }
424
+ }
425
+
426
+ // mark exchange complete
427
+ await exchanges.complete({exchangerId: exchanger.id, id: exchange.id});
428
+
429
+ // FIXME: decide what the best recovery path is if delivery fails (but no
430
+ // replay attack detected) after exchange has been marked complete
431
+
432
+ // issue VCs
433
+ return issue({exchanger, exchange});
434
+ }
435
+
314
436
  async function _requestDidProof({res, exchangeRecord}) {
315
437
  /* `9.4 Credential Issuer-provided nonce` allows the credential
316
438
  issuer infrastructure to provide the nonce via an error:
@@ -327,7 +449,7 @@ async function _requestDidProof({res, exchangeRecord}) {
327
449
  "c_nonce_expires_in": 86400
328
450
  }*/
329
451
 
330
- /* OIDC4VCI exchanges themselves are not replayable and single-step, so the
452
+ /* OID4VCI exchanges themselves are not replayable and single-step, so the
331
453
  challenge to be signed is just the exchange ID itself. An exchange cannot
332
454
  be reused and neither can a challenge. */
333
455
  const {exchange, meta: {expires}} = exchangeRecord;
@@ -343,3 +465,27 @@ async function _requestDidProof({res, exchangeRecord}) {
343
465
  c_nonce_expires_in: ttl
344
466
  });
345
467
  }
468
+
469
+ function _assertCredentialRequests({
470
+ credentialRequests, expectedCredentialRequests
471
+ }) {
472
+ // ensure every credential request matches against an expected one and none
473
+ // are missing
474
+ if(!(credentialRequests.length === expectedCredentialRequests.length &&
475
+ credentialRequests.every(cr => expectedCredentialRequests.some(
476
+ ecr => _matchCredentialRequest(ecr, cr))))) {
477
+ // FIXME: improve error
478
+ throw new Error('unexpected credential request');
479
+ }
480
+ }
481
+
482
+ function _matchCredentialRequest(a, b) {
483
+ if(a.format !== b.format) {
484
+ return false;
485
+ }
486
+ const {credential_definition: {'@context': c1, type: t1}} = a;
487
+ const {credential_definition: {'@context': c2, type: t2}} = b;
488
+ // contexts must match exact order but types can have different order
489
+ return (c1.length === c2.length && t1.length === t2.length &&
490
+ c1.every((c, i) => c === c2[i]) && t1.every(t => t2.some(x => t === x)));
491
+ }
package/lib/verify.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * Copyright (c) 2022 Digital Bazaar, Inc. All rights reserved.
2
+ * Copyright (c) 2022-2023 Digital Bazaar, Inc. All rights reserved.
3
3
  */
4
4
  import * as bedrock from '@bedrock/core';
5
5
  import {importJWK, jwtVerify} from 'jose';
@@ -60,7 +60,7 @@ export async function verify({
60
60
 
61
61
  export async function verifyDidProofJwt({exchanger, exchange, jwt} = {}) {
62
62
  // optional oauth2 options
63
- const {oauth2} = exchange.oidc4vci;
63
+ const {oauth2} = exchange.openId;
64
64
  const {maxClockSkew} = oauth2;
65
65
 
66
66
  // audience is always the `exchangeId` and cannot be configured; this
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bedrock/vc-delivery",
3
- "version": "1.0.0",
3
+ "version": "3.0.0",
4
4
  "type": "module",
5
5
  "description": "Bedrock Verifiable Credential Delivery",
6
6
  "main": "./lib/index.js",
@@ -40,25 +40,25 @@
40
40
  "peerDependencies": {
41
41
  "@bedrock/app-identity": "4.0.0",
42
42
  "@bedrock/core": "^6.0.1",
43
- "@bedrock/did-io": "^9.0.1",
43
+ "@bedrock/did-io": "^10.0.0",
44
44
  "@bedrock/express": "^8.0.0",
45
45
  "@bedrock/https-agent": "^4.0.0",
46
46
  "@bedrock/mongodb": "^10.0.0",
47
47
  "@bedrock/oauth2-verifier": "^1.0.0",
48
- "@bedrock/service-agent": "^6.0.0",
49
- "@bedrock/service-core": "^7.0.1",
48
+ "@bedrock/service-agent": "^7.0.0",
49
+ "@bedrock/service-core": "^8.0.0",
50
50
  "@bedrock/validation": "^7.1.0"
51
51
  },
52
52
  "directories": {
53
53
  "lib": "./lib"
54
54
  },
55
55
  "devDependencies": {
56
- "eslint": "^8.18.0",
57
- "eslint-config-digitalbazaar": "^4.0.1",
58
- "eslint-plugin-jsdoc": "^39.3.3",
59
- "eslint-plugin-unicorn": "^43.0.0",
60
- "jsdoc": "^3.6.10",
61
- "jsdoc-to-markdown": "^7.1.1"
56
+ "eslint": "^8.41.0",
57
+ "eslint-config-digitalbazaar": "^5.0.1",
58
+ "eslint-plugin-jsdoc": "^45.0.0",
59
+ "eslint-plugin-unicorn": "^47.0.0",
60
+ "jsdoc": "^4.0.2",
61
+ "jsdoc-to-markdown": "^8.0.0"
62
62
  },
63
63
  "engines": {
64
64
  "node": ">=16"