@bedrock/vc-delivery 5.4.0 → 5.5.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/oid4/http.js CHANGED
@@ -156,8 +156,13 @@ export async function createRoutes({
156
156
  getConfigMiddleware,
157
157
  getExchange,
158
158
  asyncHandler(async (req, res) => {
159
- const response = await oid4vci.processAccessTokenRequest({req, res});
160
- res.json(response);
159
+ let result;
160
+ try {
161
+ result = await oid4vci.processAccessTokenRequest({req, res});
162
+ } catch(error) {
163
+ return _sendOID4Error({res, error});
164
+ }
165
+ res.json(result);
161
166
  }));
162
167
 
163
168
  // a credential delivery server endpoint
@@ -195,41 +200,46 @@ export async function createRoutes({
195
200
  }
196
201
  }
197
202
  */
198
- const result = await oid4vci.processCredentialRequests({
199
- req, res, isBatchRequest: false
200
- });
201
- if(!result) {
202
- // DID proof request response sent
203
- return;
204
- }
205
-
206
- // send VC(s)
207
- const {
208
- response: {verifiablePresentation: {verifiableCredential}},
209
- format
210
- } = result;
211
- // FIXME: "format" doesn't seem to be in the spec anymore (draft 14+)...
212
- const credentials = verifiableCredential.map(vc => {
213
- // parse any enveloped VC
214
- let credential;
215
- if(vc.type === 'EnvelopedVerifiableCredential' &&
216
- vc.id?.startsWith('data:application/jwt,')) {
217
- credential = vc.id.slice('data:application/jwt,'.length);
218
- } else {
219
- credential = vc;
203
+ let result;
204
+ try {
205
+ result = await oid4vci.processCredentialRequests({
206
+ req, res, isBatchRequest: false
207
+ });
208
+ if(!result) {
209
+ // DID proof request response sent
210
+ return;
220
211
  }
221
- return credential;
222
- });
223
212
 
224
- /* Note: The `/credential` route only supports sending VCs of the same
225
- type, but there can be more than one of them. The above `isBatchRequest`
226
- check will ensure that the workflow used here only allows a single
227
- credential request, indicating a single type. */
213
+ // send VC(s)
214
+ const {
215
+ response: {verifiablePresentation: {verifiableCredential}},
216
+ format
217
+ } = result;
218
+ // FIXME: "format" doesn't seem to be in the spec anymore (draft 14+)...
219
+ const credentials = verifiableCredential.map(vc => {
220
+ // parse any enveloped VC
221
+ let credential;
222
+ if(vc.type === 'EnvelopedVerifiableCredential' &&
223
+ vc.id?.startsWith('data:application/jwt,')) {
224
+ credential = vc.id.slice('data:application/jwt,'.length);
225
+ } else {
226
+ credential = vc;
227
+ }
228
+ return credential;
229
+ });
230
+
231
+ /* Note: The `/credential` route only supports sending VCs of the same
232
+ type, but there can be more than one of them. The above `isBatchRequest`
233
+ check will ensure that the workflow used here only allows a single
234
+ credential request, indicating a single type. */
228
235
 
229
- // send OID4VCI response
230
- const response = credentials.length === 1 ?
231
- {format, credential: credentials[0]} : {format, credentials};
232
- res.json(response);
236
+ // send OID4VCI response
237
+ result = credentials.length === 1 ?
238
+ {format, credential: credentials[0]} : {format, credentials};
239
+ } catch(error) {
240
+ return _sendOID4Error({res, error});
241
+ }
242
+ res.json(result);
233
243
  }));
234
244
 
235
245
  // a credential delivery server endpoint
@@ -240,8 +250,13 @@ export async function createRoutes({
240
250
  getConfigMiddleware,
241
251
  getExchange,
242
252
  asyncHandler(async (req, res) => {
243
- const offer = await oid4vci.getCredentialOffer({req});
244
- res.json(offer);
253
+ let result;
254
+ try {
255
+ result = await oid4vci.getCredentialOffer({req});
256
+ } catch(error) {
257
+ return _sendOID4Error({res, error});
258
+ }
259
+ res.json(result);
245
260
  }));
246
261
 
247
262
  // a batch credential delivery server endpoint
@@ -281,29 +296,37 @@ export async function createRoutes({
281
296
  }]
282
297
  }
283
298
  */
284
- const result = await oid4vci.processCredentialRequests({
285
- req, res, isBatchRequest: true
286
- });
287
- if(!result) {
288
- // DID proof request response sent
289
- return;
290
- }
291
-
292
- // send VCs
293
- const {response: {verifiablePresentation}, format} = result;
294
- // FIXME: "format" doesn't seem to be in the spec anymore (draft 14+)...
295
- const responses = verifiablePresentation.verifiableCredential.map(vc => {
296
- // parse any enveloped VC
297
- let credential;
298
- if(vc.type === 'EnvelopedVerifiableCredential' &&
299
- vc.id?.startsWith('data:application/jwt,')) {
300
- credential = vc.id.slice('data:application/jwt,'.length);
301
- } else {
302
- credential = vc;
299
+ let result;
300
+ try {
301
+ result = await oid4vci.processCredentialRequests({
302
+ req, res, isBatchRequest: true
303
+ });
304
+ if(!result) {
305
+ // DID proof request response sent
306
+ return;
303
307
  }
304
- return {format, credential};
305
- });
306
- res.json({credential_responses: responses});
308
+
309
+ // send VCs
310
+ const {
311
+ response: {verifiablePresentation: {verifiableCredential}},
312
+ format
313
+ } = result;
314
+ // FIXME: "format" doesn't seem to be in the spec anymore (draft 14+)...
315
+ result = verifiableCredential.map(vc => {
316
+ // parse any enveloped VC
317
+ let credential;
318
+ if(vc.type === 'EnvelopedVerifiableCredential' &&
319
+ vc.id?.startsWith('data:application/jwt,')) {
320
+ credential = vc.id.slice('data:application/jwt,'.length);
321
+ } else {
322
+ credential = vc;
323
+ }
324
+ return {format, credential};
325
+ });
326
+ } catch(error) {
327
+ return _sendOID4Error({res, error});
328
+ }
329
+ res.json({credential_responses: result});
307
330
  }));
308
331
 
309
332
  // an OID4VP verifier endpoint
@@ -315,13 +338,18 @@ export async function createRoutes({
315
338
  getConfigMiddleware,
316
339
  getExchange,
317
340
  asyncHandler(async (req, res) => {
318
- const {
319
- authorizationRequest
320
- } = await oid4vp.getAuthorizationRequest({req});
321
- // construct and send authz request as unsecured JWT
322
- const jwt = new UnsecuredJWT(authorizationRequest).encode();
323
- res.set('content-type', 'application/oauth-authz-req+jwt');
324
- res.send(jwt);
341
+ let result;
342
+ try {
343
+ const {
344
+ authorizationRequest
345
+ } = await oid4vp.getAuthorizationRequest({req});
346
+ // construct and send authz request as unsecured JWT
347
+ result = new UnsecuredJWT(authorizationRequest).encode();
348
+ res.set('content-type', 'application/oauth-authz-req+jwt');
349
+ } catch(error) {
350
+ return _sendOID4Error({res, error});
351
+ }
352
+ res.send(result);
325
353
  }));
326
354
 
327
355
  // an OID4VP verifier endpoint
@@ -335,7 +363,35 @@ export async function createRoutes({
335
363
  getConfigMiddleware,
336
364
  getExchange,
337
365
  asyncHandler(async (req, res) => {
338
- const result = await oid4vp.processAuthorizationResponse({req});
366
+ let result;
367
+ try {
368
+ result = await oid4vp.processAuthorizationResponse({req});
369
+ } catch(error) {
370
+ return _sendOID4Error({res, error});
371
+ }
339
372
  res.json(result);
340
373
  }));
341
374
  }
375
+
376
+ function _sendOID4Error({res, error}) {
377
+ const status = error.details?.httpStatusCode ?? 500;
378
+ const oid4Error = {
379
+ error: _camelToSnakeCase(error.name ?? 'OperationError'),
380
+ error_description: error.message
381
+ };
382
+ if(error?.details?.public) {
383
+ oid4Error.details = error.details;
384
+ // expose first level cause only
385
+ if(oid4Error.cause?.details?.public) {
386
+ oid4Error.cause = {
387
+ name: error.cause.name,
388
+ message: error.cause.message
389
+ };
390
+ }
391
+ }
392
+ res.status(status).json(oid4Error);
393
+ }
394
+
395
+ function _camelToSnakeCase(s) {
396
+ return s.replace(/[A-Z]/g, (c, i) => (i === 0 ? '' : '_') + c.toLowerCase());
397
+ }
@@ -552,9 +552,10 @@ async function _requestDidProof({res, exchangeRecord}) {
552
552
  const {exchange, meta: {expires}} = exchangeRecord;
553
553
  const ttl = Math.floor((expires.getTime() - Date.now()) / 1000);
554
554
 
555
- res.status(400).json({
556
- error: 'invalid_or_missing_proof',
557
- error_description:
555
+ _sendOID4Error({
556
+ res,
557
+ error: 'invalid_proof',
558
+ description:
558
559
  'Credential issuer requires proof element in Credential Request',
559
560
  // use exchange ID
560
561
  c_nonce: exchange.id,
@@ -588,10 +589,19 @@ async function _requestOID4VP({authorizationRequest, res}) {
588
589
  challenge to be signed is just the exchange ID itself. An exchange cannot
589
590
  be reused and neither can a challenge. */
590
591
 
591
- res.status(400).json({
592
+ _sendOID4Error({
593
+ res,
592
594
  error: 'presentation_required',
593
- error_description:
595
+ description:
594
596
  'Credential issuer requires presentation before Credential Request',
595
597
  authorization_request: authorizationRequest
596
598
  });
597
599
  }
600
+
601
+ function _sendOID4Error({res, error, description, status = 400, ...rest}) {
602
+ res.status(status).json({
603
+ error,
604
+ error_description: description,
605
+ ...rest
606
+ });
607
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bedrock/vc-delivery",
3
- "version": "5.4.0",
3
+ "version": "5.5.0",
4
4
  "type": "module",
5
5
  "description": "Bedrock Verifiable Credential Delivery",
6
6
  "main": "./lib/index.js",