diff --git a/CHANGELOG.md b/CHANGELOG.md index 2c836982..5d1e0d58 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,20 @@ # Release Notes +## v2.2.2 - 2023-11-28 + +- Updated: + - Updated pex-model deps + - Added more logic to deduce holder(s) for a VP +- Fixed: + - Logic when to generate a presentation submission was incorrect + +## v2.2.1 - 2023-10-23 + +- Updated: + - Updated logic to return holder DID when constructing presentation +- Fixed: + - Issue with determining whether to generate submission data or not. + ## v2.2.0 - 2023-10-12 - Updated: diff --git a/lib/PEX.ts b/lib/PEX.ts index 160fa861..1ab26c35 100644 --- a/lib/PEX.ts +++ b/lib/PEX.ts @@ -3,6 +3,7 @@ import { CompactSdJwtVc, CredentialMapper, Hasher, + ICredential, IPresentation, IProof, OriginalVerifiableCredential, @@ -28,7 +29,7 @@ import { VerifiablePresentationResult, } from './signing'; import { DiscoveredVersion, IInternalPresentationDefinition, IPresentationDefinition, PEVersion, SSITypesBuilder } from './types'; -import { calculateSdHash, definitionVersionDiscovery } from './utils'; +import { calculateSdHash, definitionVersionDiscovery, getSubjectIdsAsString } from './utils'; import { PresentationDefinitionV1VB, PresentationDefinitionV2VB, PresentationSubmissionVB, Validated, ValidationEngine } from './validation'; export interface PEXOptions { @@ -80,7 +81,7 @@ export class PEX { }, ): EvaluationResults { const generatePresentationSubmission = - opts?.generatePresentationSubmission !== undefined ? opts.generatePresentationSubmission : opts?.presentationSubmission !== undefined; + opts?.generatePresentationSubmission !== undefined ? opts.generatePresentationSubmission : opts?.presentationSubmission === undefined; const pd: IInternalPresentationDefinition = SSITypesBuilder.toInternalPresentationDefinition(presentationDefinition); const presentationCopy: OriginalVerifiablePresentation = JSON.parse(JSON.stringify(presentation)); const wrappedPresentation: WrappedVerifiablePresentation = SSITypesBuilder.mapExternalVerifiablePresentationToWrappedVP( @@ -242,6 +243,7 @@ export class PEX { const presentation = PEX.constructPresentation(selectedCredentials, { ...opts, + // We only pass in the submission in case it needs to be included in the presentation presentationSubmission: presentationSubmissionLocation === PresentationSubmissionLocation.PRESENTATION ? presentationSubmission : undefined, hasher: this.options?.hasher, }); @@ -317,12 +319,28 @@ export class PEX { kbJwt, }; } else { - const holder = opts?.holderDID; - const type = Array.isArray(opts?.basePresentationPayload?.type) - ? opts?.basePresentationPayload?.type || [] - : opts?.basePresentationPayload?.type - ? [opts.basePresentationPayload.type] + if (!selectedCredentials) { + throw Error(`At least a verifiable credential needs to be passed in to create a presentation`); + } + const verifiableCredential = (Array.isArray(selectedCredentials) ? selectedCredentials : [selectedCredentials]) as W3CVerifiableCredential[]; + const wVCs = verifiableCredential.map((vc) => CredentialMapper.toWrappedVerifiableCredential(vc)); + const holders = Array.from(new Set(wVCs.flatMap((wvc) => getSubjectIdsAsString(wvc.credential as ICredential)))); + if (holders.length !== 1 && !opts?.holderDID) { + console.log( + `We deduced ${holders.length} subject from ${wVCs.length} Verifiable Credentials, and no holder property was given. This might lead to undesired results`, + ); + } + const holder = opts?.holderDID ?? (holders.length === 1 ? holders[0] : undefined); + + const type = opts?.basePresentationPayload?.type + ? Array.isArray(opts.basePresentationPayload.type) + ? opts.basePresentationPayload.type + : [opts.basePresentationPayload.type] : []; + if (!type.includes('VerifiablePresentation')) { + type.push('VerifiablePresentation'); + } + const context = opts?.basePresentationPayload?.['@context'] ? Array.isArray(opts.basePresentationPayload['@context']) ? opts.basePresentationPayload['@context'] @@ -332,9 +350,6 @@ export class PEX { context.push('https://www.w3.org/2018/credentials/v1'); } - if (!type.includes('VerifiablePresentation')) { - type.push('VerifiablePresentation'); - } if (opts?.presentationSubmission) { if (!type.includes('PresentationSubmission')) { type.push('PresentationSubmission'); @@ -349,7 +364,7 @@ export class PEX { type, holder, ...(!!opts?.presentationSubmission && { presentation_submission: opts.presentationSubmission }), - verifiableCredential: (Array.isArray(selectedCredentials) ? selectedCredentials : [selectedCredentials]) as W3CVerifiableCredential[], + verifiableCredential, }; } } diff --git a/package.json b/package.json index 35b986bf..d0226703 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@sphereon/pex", - "version": "2.2.1-unstable.0", + "version": "2.2.3-unstable.0", "description": "A Typescript implementation of the v1 and v2 DIF Presentation Exchange specification", "main": "dist/main/index.js", "module": "dist/module/index.js", diff --git a/test/PEX.spec.ts b/test/PEX.spec.ts index 1e770ece..49b2f8f0 100644 --- a/test/PEX.spec.ts +++ b/test/PEX.spec.ts @@ -137,9 +137,12 @@ describe('evaluate', () => { // Delete the submission to trigger an error delete vpSimple.presentation_submission; const pex: PEX = new PEX(); - expect(() => pex.evaluatePresentation(pdSchema, vpSimple, { limitDisclosureSignatureSuites: LIMIT_DISCLOSURE_SIGNATURE_SUITES })).toThrowError( - 'Either a presentation submission as part of the VP or provided separately was expected', - ); + expect(() => + pex.evaluatePresentation(pdSchema, vpSimple, { + limitDisclosureSignatureSuites: LIMIT_DISCLOSURE_SIGNATURE_SUITES, + generatePresentationSubmission: false, + }), + ).toThrowError('Either a presentation submission as part of the VP or provided separately was expected'); }); it('Evaluate case without any error 2', () => {