From bba0c08f4231114bc25d6278882412b2400b91ff Mon Sep 17 00:00:00 2001 From: Dave Longley Date: Tue, 1 Oct 2024 12:29:15 -0400 Subject: [PATCH] Auto-detect whether to include `vc` in PD constraint JSON paths. --- CHANGELOG.md | 6 +++ lib/oid4vp.js | 13 ++++- tests/unit/oid4vp.spec.js | 105 ++++++++++++++++++++++++++++++++++++-- 3 files changed, 120 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c15b0ae..c36d958 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # @digitalbazaar/oid4-client Changelog +## 4.1.0 - 2024-10-dd + +### Added +- Auto-detect whether to include `vc` in the JSON paths when + computing presentation definition constraints from a VPR. + ## 4.0.0 - 2024-09-20 ### Changed diff --git a/lib/oid4vp.js b/lib/oid4vp.js index 11433d2..6861592 100644 --- a/lib/oid4vp.js +++ b/lib/oid4vp.js @@ -298,7 +298,7 @@ export async function toVpr({ // converts a VPR to partial "authorization request" export function fromVpr({ - verifiablePresentationRequest, strict = false, prefixJwtVcPath = false + verifiablePresentationRequest, strict = false, prefixJwtVcPath } = {}) { try { let {query} = verifiablePresentationRequest; @@ -571,6 +571,17 @@ function _matchesInputDescriptor({ // exported for testing purposes only export function _fromQueryByExampleQuery({credentialQuery, prefixJwtVcPath}) { + // determine `prefixJwtVcPath` default: + // if `credentialQuery` does NOT specify any `acceptedCryptosuites` but it + // does specify `acceptedEnvelopes: ['application/jwt']`, then default + // `prefixJwtVcPath` to `true` + if(prefixJwtVcPath === undefined && + !credentialQuery.acceptedCryptosuites?.length > 0 && + (credentialQuery.acceptedEnvelopes?.length === 1 && + credentialQuery.acceptedEnvelopes?.[0] === 'application/jwt')) { + prefixJwtVcPath = true; + } + const fields = []; const inputDescriptor = { id: uuid(), diff --git a/tests/unit/oid4vp.spec.js b/tests/unit/oid4vp.spec.js index 19ed4bf..f447310 100644 --- a/tests/unit/oid4vp.spec.js +++ b/tests/unit/oid4vp.spec.js @@ -1,5 +1,5 @@ /*! - * Copyright (c) 2022-2023 Digital Bazaar, Inc. All rights reserved. + * Copyright (c) 2022-2024 Digital Bazaar, Inc. All rights reserved. */ import {_fromQueryByExampleQuery} from '../../lib/oid4vp.js'; import chai from 'chai'; @@ -8,8 +8,32 @@ chai.should(); const {expect} = chai; describe('OID4VP', () => { - describe('constructor', () => { - it('should map a QueryByExample to a Presentation Definition', async () => { + describe('QueryByExample => Presentation Definition', () => { + it('should NOT include "vc" prefix in paths', async () => { + const presentation_definition = _fromQueryByExampleQuery({ + credentialQuery: { + reason: 'Please present your Driver\'s License to complete the ' + + 'verification process.', + example: { + '@context': [ + 'https://www.w3.org/2018/credentials/v1', + 'https://w3id.org/vdl/v1', + 'https://w3id.org/vdl/aamva/v1' + ], + type: [ + 'Iso18013DriversLicenseCredential' + ] + } + }, + prefixJwtVcPath: false + }); + expect(presentation_definition.constraints.fields[0].path).to.eql( + ['$[\'@context\']']); + expect(presentation_definition.constraints.fields[1].path).to.eql( + ['$[\'type\']']); + }); + + it('should include "vc" prefix in paths', async () => { const presentation_definition = _fromQueryByExampleQuery({ credentialQuery: { reason: 'Please present your Driver\'s License to complete the ' + @@ -36,5 +60,80 @@ describe('OID4VP', () => { '$[\'vc\'][\'type\']' ]); }); + + it('auto-detect to NOT include "vc" w/unspecified security', async () => { + const presentation_definition = _fromQueryByExampleQuery({ + credentialQuery: { + reason: 'Please present your Driver\'s License to complete the ' + + 'verification process.', + example: { + '@context': [ + 'https://www.w3.org/2018/credentials/v1', + 'https://w3id.org/vdl/v1', + 'https://w3id.org/vdl/aamva/v1' + ], + type: [ + 'Iso18013DriversLicenseCredential' + ] + } + } + }); + expect(presentation_definition.constraints.fields[0].path).to.eql( + ['$[\'@context\']']); + expect(presentation_definition.constraints.fields[1].path).to.eql( + ['$[\'type\']']); + }); + + it('auto-detect to NOT include "vc" w/acceptedCryptosuites', async () => { + const presentation_definition = _fromQueryByExampleQuery({ + credentialQuery: { + reason: 'Please present your Driver\'s License to complete the ' + + 'verification process.', + example: { + '@context': [ + 'https://www.w3.org/2018/credentials/v1', + 'https://w3id.org/vdl/v1', + 'https://w3id.org/vdl/aamva/v1' + ], + type: [ + 'Iso18013DriversLicenseCredential' + ] + }, + acceptedCryptosuites: ['bbs-2023'] + } + }); + expect(presentation_definition.constraints.fields[0].path).to.eql( + ['$[\'@context\']']); + expect(presentation_definition.constraints.fields[1].path).to.eql( + ['$[\'type\']']); + }); + + it('auto-detect to include "vc" w/acceptedEnvelopes', async () => { + const presentation_definition = _fromQueryByExampleQuery({ + credentialQuery: { + reason: 'Please present your Driver\'s License to complete the ' + + 'verification process.', + example: { + '@context': [ + 'https://www.w3.org/2018/credentials/v1', + 'https://w3id.org/vdl/v1', + 'https://w3id.org/vdl/aamva/v1' + ], + type: [ + 'Iso18013DriversLicenseCredential' + ] + }, + acceptedEnvelopes: ['application/jwt'] + } + }); + expect(presentation_definition.constraints.fields[0].path).to.eql([ + '$[\'@context\']', + '$[\'vc\'][\'@context\']' + ]); + expect(presentation_definition.constraints.fields[1].path).to.eql([ + '$[\'type\']', + '$[\'vc\'][\'type\']' + ]); + }); }); });