@sphereon/ssi-sdk.siopv2-oid4vp-op-auth 0.34.1-feature.SSISDK.45.94 → 0.34.1-feature.SSISDK.46.40

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.
@@ -1,21 +1,31 @@
1
- import { AuthorizationRequest, Json, SupportedVersion } from '@sphereon/did-auth-siop'
1
+ import { AuthorizationRequest, SupportedVersion } from '@sphereon/did-auth-siop'
2
+ import { IPresentationDefinition, PEX } from '@sphereon/pex'
3
+ import { InputDescriptorV1, InputDescriptorV2, PresentationDefinitionV1, PresentationDefinitionV2 } from '@sphereon/pex-models'
2
4
  import { isOID4VCIssuerIdentifier, ManagedIdentifierOptsOrResult } from '@sphereon/ssi-sdk-ext.identifier-resolution'
3
5
  import { UniqueDigitalCredential, verifiableCredentialForRoleFilter } from '@sphereon/ssi-sdk.credential-store'
4
- import { ConnectionType } from '@sphereon/ssi-sdk.data-store'
5
- import { CredentialRole } from '@sphereon/ssi-types'
6
-
7
- import { CredentialMapper, HasherSync, Loggers, OriginalVerifiableCredential, SdJwtDecodedVerifiableCredential } from '@sphereon/ssi-types'
8
- import { OpSession } from '../session'
9
- import { LOGGER_NAMESPACE, RequiredContext, SelectableCredential, SelectableCredentialsMap, Siopv2HolderEvent } from '../types'
10
- import { encodeJoseBlob } from '@sphereon/ssi-sdk.core'
11
- import { DcqlPresentation, DcqlQuery } from 'dcql'
12
- import { convertToDcqlCredentials } from '../utils/dcql'
6
+ import { ConnectionType, CredentialRole } from '@sphereon/ssi-sdk.data-store'
7
+ import { CredentialMapper, HasherSync, Loggers, OriginalVerifiableCredential, PresentationSubmission } from '@sphereon/ssi-types'
8
+ import { OID4VP, OpSession } from '../session'
9
+ import {
10
+ DidAgents,
11
+ LOGGER_NAMESPACE,
12
+ RequiredContext,
13
+ SelectableCredential,
14
+ SelectableCredentialsMap,
15
+ Siopv2HolderEvent,
16
+ SuitableCredentialAgents,
17
+ VerifiableCredentialsWithDefinition,
18
+ VerifiablePresentationWithDefinition,
19
+ } from '../types'
13
20
  import { IAgentContext, IDIDManager } from '@veramo/core'
14
21
  import { getOrCreatePrimaryIdentifier, SupportedDidMethodEnum } from '@sphereon/ssi-sdk-ext.did-utils'
22
+ import { defaultHasher, encodeJoseBlob } from '@sphereon/ssi-sdk.core'
23
+ import { DcqlCredential, DcqlCredentialPresentation, DcqlPresentation, DcqlQuery } from 'dcql'
24
+ import { convertToDcqlCredentials } from '../utils/dcql'
25
+ import { getOriginalVerifiableCredential } from '../utils/CredentialUtils'
15
26
 
16
27
  export const logger = Loggers.DEFAULT.get(LOGGER_NAMESPACE)
17
28
 
18
- // @ts-ignore
19
29
  const createEbsiIdentifier = async (agentContext: IAgentContext<IDIDManager>): Promise<ManagedIdentifierOptsOrResult> => {
20
30
  logger.log(`No EBSI key present yet. Creating a new one...`)
21
31
  const { result: newIdentifier, created } = await getOrCreatePrimaryIdentifier(agentContext, {
@@ -29,10 +39,9 @@ const createEbsiIdentifier = async (agentContext: IAgentContext<IDIDManager>): P
29
39
  return await agentContext.agent.identifierManagedGetByDid({ identifier: newIdentifier.did })
30
40
  }
31
41
 
32
- // @ts-ignore
33
42
  const hasEbsiClient = async (authorizationRequest: AuthorizationRequest) => {
34
- const clientId = authorizationRequest.getMergedProperty<string>('client_id')
35
- const redirectUri = authorizationRequest.getMergedProperty<string>('redirect_uri')
43
+ const clientId = await authorizationRequest.getMergedProperty<string>('client_id')
44
+ const redirectUri = await authorizationRequest.getMergedProperty<string>('redirect_uri')
36
45
  return clientId?.toLowerCase().includes('.ebsi.eu') || redirectUri?.toLowerCase().includes('.ebsi.eu')
37
46
  }
38
47
 
@@ -40,170 +49,293 @@ export const siopSendAuthorizationResponse = async (
40
49
  connectionType: ConnectionType,
41
50
  args: {
42
51
  sessionId: string
43
- credentials: Array<UniqueDigitalCredential | OriginalVerifiableCredential>
52
+ verifiableCredentialsWithDefinition?: VerifiableCredentialsWithDefinition[]
44
53
  idOpts?: ManagedIdentifierOptsOrResult
45
54
  isFirstParty?: boolean
46
55
  hasher?: HasherSync
56
+ dcqlQuery?: DcqlQuery
47
57
  },
48
58
  context: RequiredContext,
49
59
  ) => {
50
60
  const { agent } = context
51
- const { credentials } = args
61
+ const agentContext = { ...context, agent: context.agent as DidAgents }
62
+ let { idOpts, isFirstParty, hasher = defaultHasher } = args
63
+
52
64
  if (connectionType !== ConnectionType.SIOPv2_OpenID4VP) {
53
65
  return Promise.reject(Error(`No supported authentication provider for type: ${connectionType}`))
54
66
  }
55
-
56
67
  const session: OpSession = await agent.siopGetOPSession({ sessionId: args.sessionId })
57
68
  const request = await session.getAuthorizationRequest()
58
- const aud = request.authorizationRequest.getMergedProperty<string>('aud')
69
+ const aud = await request.authorizationRequest.getMergedProperty<string>('aud')
59
70
  logger.debug(`AUD: ${aud}`)
60
71
  logger.debug(JSON.stringify(request.authorizationRequest))
61
72
 
62
- const domain =
63
- ((await request.authorizationRequest.getMergedProperty('client_id')) as string) ??
64
- request.issuer ??
65
- (request.versions.includes(SupportedVersion.JWT_VC_PRESENTATION_PROFILE_v1) ? 'https://self-issued.me/v2/openid-vc' : 'https://self-issued.me/v2')
66
- logger.debug(`NONCE: ${session.nonce}, domain: ${domain}`)
73
+ let presentationsAndDefs: VerifiablePresentationWithDefinition[] | undefined
74
+ let presentationSubmission: PresentationSubmission | undefined
75
+ if (await session.hasPresentationDefinitions()) {
76
+ const oid4vp: OID4VP = await session.getOID4VP({ hasher })
67
77
 
68
- const firstUniqueDC = credentials[0]
69
- if (typeof firstUniqueDC !== 'object' || !('digitalCredential' in firstUniqueDC)) {
70
- return Promise.reject(Error('SiopMachine only supports UniqueDigitalCredentials for now'))
71
- }
78
+ const credentialsAndDefinitions = args.verifiableCredentialsWithDefinition
79
+ ? args.verifiableCredentialsWithDefinition
80
+ : await oid4vp.filterCredentialsAgainstAllDefinitions(CredentialRole.HOLDER)
81
+ const domain =
82
+ ((await request.authorizationRequest.getMergedProperty('client_id')) as string) ??
83
+ request.issuer ??
84
+ (request.versions.includes(SupportedVersion.JWT_VC_PRESENTATION_PROFILE_v1)
85
+ ? 'https://self-issued.me/v2/openid-vc'
86
+ : 'https://self-issued.me/v2')
87
+ logger.log(`NONCE: ${session.nonce}, domain: ${domain}`)
72
88
 
73
- let identifier: ManagedIdentifierOptsOrResult
74
- const digitalCredential = firstUniqueDC.digitalCredential
75
- const firstVC = firstUniqueDC.uniformVerifiableCredential
76
- const holder = CredentialMapper.isSdJwtDecodedCredential(firstVC)
77
- ? firstVC.decodedPayload.cnf?.jwk
78
- ? //TODO SDK-19: convert the JWK to hex and search for the appropriate key and associated DID
79
- //doesn't apply to did:jwk only, as you can represent any DID key as a JWK. So whenever you encounter a JWK it doesn't mean it had to come from a did:jwk in the system. It just can always be represented as a did:jwk
80
- `did:jwk:${encodeJoseBlob(firstVC.decodedPayload.cnf?.jwk)}#0`
81
- : firstVC.decodedPayload.sub
82
- : Array.isArray(firstVC.credentialSubject)
83
- ? firstVC.credentialSubject[0].id
84
- : firstVC.credentialSubject.id
85
- if (!digitalCredential.kmsKeyRef) {
86
- // In case the store does not have the kmsKeyRef lets search for the holder
87
-
88
- if (!holder) {
89
- return Promise.reject(`No holder found and no kmsKeyRef in DB. Cannot determine identifier to use`)
89
+ const firstUniqueDC = credentialsAndDefinitions[0].credentials[0]
90
+ if (typeof firstUniqueDC !== 'object' || !('digitalCredential' in firstUniqueDC)) {
91
+ return Promise.reject(Error('SiopMachine only supports UniqueDigitalCredentials for now'))
90
92
  }
91
- try {
92
- identifier = await session.context.agent.identifierManagedGet({ identifier: holder })
93
- } catch (e) {
94
- logger.debug(`Holder DID not found: ${holder}`)
95
- throw e
93
+
94
+ let identifier: ManagedIdentifierOptsOrResult
95
+ const digitalCredential = firstUniqueDC.digitalCredential
96
+ const firstVC = firstUniqueDC.uniformVerifiableCredential
97
+ const holder = CredentialMapper.isSdJwtDecodedCredential(firstVC)
98
+ ? firstVC.decodedPayload.cnf?.jwk
99
+ ? //TODO SDK-19: convert the JWK to hex and search for the appropriate key and associated DID
100
+ //doesn't apply to did:jwk only, as you can represent any DID key as a JWK. So whenever you encounter a JWK it doesn't mean it had to come from a did:jwk in the system. It just can always be represented as a did:jwk
101
+ `did:jwk:${encodeJoseBlob(firstVC.decodedPayload.cnf?.jwk)}#0`
102
+ : firstVC.decodedPayload.sub
103
+ : Array.isArray(firstVC.credentialSubject)
104
+ ? firstVC.credentialSubject[0].id
105
+ : firstVC.credentialSubject.id
106
+ if (!digitalCredential.kmsKeyRef) {
107
+ // In case the store does not have the kmsKeyRef lets search for the holder
108
+
109
+ if (!holder) {
110
+ return Promise.reject(`No holder found and no kmsKeyRef in DB. Cannot determine identifier to use`)
111
+ }
112
+ try {
113
+ identifier = await session.context.agent.identifierManagedGet({ identifier: holder })
114
+ } catch (e) {
115
+ logger.debug(`Holder DID not found: ${holder}`)
116
+ throw e
117
+ }
118
+ } else if (isOID4VCIssuerIdentifier(digitalCredential.kmsKeyRef)) {
119
+ identifier = await session.context.agent.identifierManagedGetByOID4VCIssuer({
120
+ identifier: firstUniqueDC.digitalCredential.kmsKeyRef,
121
+ })
122
+ } else {
123
+ switch (digitalCredential.subjectCorrelationType) {
124
+ case 'DID':
125
+ identifier = await session.context.agent.identifierManagedGetByDid({
126
+ identifier: digitalCredential.subjectCorrelationId ?? holder,
127
+ kmsKeyRef: digitalCredential.kmsKeyRef,
128
+ })
129
+ break
130
+ // TODO other implementations?
131
+ default:
132
+ if (digitalCredential.subjectCorrelationId?.startsWith('did:') || holder?.startsWith('did:')) {
133
+ identifier = await session.context.agent.identifierManagedGetByDid({
134
+ identifier: digitalCredential.subjectCorrelationId ?? holder,
135
+ kmsKeyRef: digitalCredential.kmsKeyRef,
136
+ })
137
+ } else {
138
+ // Since we are using the kmsKeyRef we will find the KID regardless of the identifier. We set it for later access though
139
+ identifier = await session.context.agent.identifierManagedGetByKid({
140
+ identifier: digitalCredential.subjectCorrelationId ?? holder ?? digitalCredential.kmsKeyRef,
141
+ kmsKeyRef: digitalCredential.kmsKeyRef,
142
+ })
143
+ }
144
+ }
96
145
  }
97
- } else if (isOID4VCIssuerIdentifier(digitalCredential.kmsKeyRef)) {
98
- identifier = await session.context.agent.identifierManagedGetByOID4VCIssuer({
99
- identifier: firstUniqueDC.digitalCredential.kmsKeyRef,
100
- })
101
- } else {
102
- switch (digitalCredential.subjectCorrelationType) {
103
- case 'DID':
104
- identifier = await session.context.agent.identifierManagedGetByDid({
105
- identifier: digitalCredential.subjectCorrelationId ?? holder,
106
- kmsKeyRef: digitalCredential.kmsKeyRef,
107
- })
108
- break
109
- // TODO other implementations?
110
- default:
111
- // Since we are using the kmsKeyRef we will find the KID regardless of the identifier. We set it for later access though
112
- identifier = await session.context.agent.identifierManagedGetByKid({
113
- identifier: digitalCredential.subjectCorrelationId ?? holder ?? digitalCredential.kmsKeyRef,
114
- kmsKeyRef: digitalCredential.kmsKeyRef,
115
- })
146
+
147
+ if (identifier === undefined && idOpts !== undefined && (await hasEbsiClient(request.authorizationRequest))) {
148
+ identifier = await createEbsiIdentifier(agentContext)
116
149
  }
117
- }
150
+ logger.debug(`Identifier`, identifier)
118
151
 
119
- const dcqlCredentialsWithCredentials = new Map(credentials.map((vc) => [convertToDcqlCredentials(vc), vc]))
152
+ // TODO Add mdoc support
120
153
 
121
- const queryResult = DcqlQuery.query(request.dcqlQuery, Array.from(dcqlCredentialsWithCredentials.keys()))
154
+ presentationsAndDefs = await oid4vp.createVerifiablePresentations(CredentialRole.HOLDER, credentialsAndDefinitions, {
155
+ idOpts: identifier,
156
+ proofOpts: {
157
+ nonce: session.nonce,
158
+ domain,
159
+ },
160
+ })
161
+ if (!presentationsAndDefs || presentationsAndDefs.length === 0) {
162
+ throw Error('No verifiable presentations could be created')
163
+ } else if (presentationsAndDefs.length > 1) {
164
+ throw Error(`Only one verifiable presentation supported for now. Got ${presentationsAndDefs.length}`)
165
+ }
122
166
 
123
- if (!queryResult.can_be_satisfied) {
124
- return Promise.reject(Error('Credentials do not match required query request'))
125
- }
167
+ idOpts = presentationsAndDefs[0].idOpts
168
+ presentationSubmission = presentationsAndDefs[0].presentationSubmission
126
169
 
127
- const presentation: DcqlPresentation.Output = {}
128
- const uniqueCredentials = Array.from(dcqlCredentialsWithCredentials.values())
129
- for (const [key, value] of Object.entries(queryResult.credential_matches)) {
130
- if (value.success) {
131
- const matchedCredentials = value.valid_credentials.map((cred) => uniqueCredentials[cred.input_credential_index])
132
- const vc = matchedCredentials[0] // taking the first match for now //uniqueCredentials[value.input_credential_index]
133
- if (!vc) {
134
- continue
170
+ logger.log(`Definitions and locations:`, JSON.stringify(presentationsAndDefs?.[0]?.verifiablePresentations, null, 2))
171
+ logger.log(`Presentation Submission:`, JSON.stringify(presentationSubmission, null, 2))
172
+ const mergedVerifiablePresentations = presentationsAndDefs?.flatMap((pd) => pd.verifiablePresentations) || []
173
+ return await session.sendAuthorizationResponse({
174
+ ...(presentationsAndDefs && { verifiablePresentations: mergedVerifiablePresentations }),
175
+ ...(presentationSubmission && { presentationSubmission }),
176
+ // todo: Change issuer value in case we do not use identifier. Use key.meta.jwkThumbprint then
177
+ responseSignerOpts: idOpts!,
178
+ isFirstParty,
179
+ })
180
+ } else if (request.dcqlQuery) {
181
+ if (args.verifiableCredentialsWithDefinition !== undefined && args.verifiableCredentialsWithDefinition !== null) {
182
+ const vcs = args.verifiableCredentialsWithDefinition.flatMap((vcd) => vcd.credentials)
183
+ const domain =
184
+ ((await request.authorizationRequest.getMergedProperty('client_id')) as string) ??
185
+ request.issuer ??
186
+ (request.versions.includes(SupportedVersion.JWT_VC_PRESENTATION_PROFILE_v1)
187
+ ? 'https://self-issued.me/v2/openid-vc'
188
+ : 'https://self-issued.me/v2')
189
+ logger.debug(`NONCE: ${session.nonce}, domain: ${domain}`)
190
+
191
+ const firstUniqueDC = vcs[0]
192
+ if (typeof firstUniqueDC !== 'object' || !('digitalCredential' in firstUniqueDC)) {
193
+ return Promise.reject(Error('SiopMachine only supports UniqueDigitalCredentials for now'))
135
194
  }
136
- const originalVc = retrieveEncodedCredential(vc as UniqueDigitalCredential) // TODO this is not nice // also always a UniqueDigitalCredential
137
- if (!originalVc) {
138
- continue
195
+
196
+ let identifier: ManagedIdentifierOptsOrResult
197
+ const digitalCredential = firstUniqueDC.digitalCredential
198
+ const firstVC = firstUniqueDC.uniformVerifiableCredential
199
+ const holder = CredentialMapper.isSdJwtDecodedCredential(firstVC)
200
+ ? firstVC.decodedPayload.cnf?.jwk
201
+ ? //TODO SDK-19: convert the JWK to hex and search for the appropriate key and associated DID
202
+ //doesn't apply to did:jwk only, as you can represent any DID key as a JWK. So whenever you encounter a JWK it doesn't mean it had to come from a did:jwk in the system. It just can always be represented as a did:jwk
203
+ `did:jwk:${encodeJoseBlob(firstVC.decodedPayload.cnf?.jwk)}#0`
204
+ : firstVC.decodedPayload.sub
205
+ : Array.isArray(firstVC.credentialSubject)
206
+ ? firstVC.credentialSubject[0].id
207
+ : firstVC.credentialSubject.id
208
+ if (!digitalCredential.kmsKeyRef) {
209
+ // In case the store does not have the kmsKeyRef lets search for the holder
210
+
211
+ if (!holder) {
212
+ return Promise.reject(`No holder found and no kmsKeyRef in DB. Cannot determine identifier to use`)
213
+ }
214
+ try {
215
+ identifier = await session.context.agent.identifierManagedGet({ identifier: holder })
216
+ } catch (e) {
217
+ logger.debug(`Holder DID not found: ${holder}`)
218
+ throw e
219
+ }
220
+ } else if (isOID4VCIssuerIdentifier(digitalCredential.kmsKeyRef)) {
221
+ identifier = await session.context.agent.identifierManagedGetByOID4VCIssuer({
222
+ identifier: firstUniqueDC.digitalCredential.kmsKeyRef,
223
+ })
224
+ } else {
225
+ switch (digitalCredential.subjectCorrelationType) {
226
+ case 'DID':
227
+ identifier = await session.context.agent.identifierManagedGetByDid({
228
+ identifier: digitalCredential.subjectCorrelationId ?? holder,
229
+ kmsKeyRef: digitalCredential.kmsKeyRef,
230
+ })
231
+ break
232
+ // TODO other implementations?
233
+ default:
234
+ // Since we are using the kmsKeyRef we will find the KID regardless of the identifier. We set it for later access though
235
+ identifier = await session.context.agent.identifierManagedGetByKid({
236
+ identifier: digitalCredential.subjectCorrelationId ?? holder ?? digitalCredential.kmsKeyRef,
237
+ kmsKeyRef: digitalCredential.kmsKeyRef,
238
+ })
239
+ }
139
240
  }
140
- if (originalVc) {
141
- presentation[key] = originalVc as string | { [x: string]: Json }
241
+ console.log(`Identifier`, identifier)
242
+
243
+ const dcqlRepresentations: DcqlCredential[] = []
244
+ vcs.forEach((vc: UniqueDigitalCredential | OriginalVerifiableCredential) => {
245
+ const rep = convertToDcqlCredentials(vc, args.hasher)
246
+ if (rep) {
247
+ dcqlRepresentations.push(rep)
248
+ }
249
+ })
250
+
251
+ const queryResult = DcqlQuery.query(request.dcqlQuery, dcqlRepresentations)
252
+ const presentation: Record<string, DcqlCredentialPresentation> = {}
253
+
254
+ for (const [key, value] of Object.entries(queryResult.credential_matches)) {
255
+ const allMatches = Array.isArray(value) ? value : [value]
256
+ allMatches.forEach((match) => {
257
+ if (match.success) {
258
+ const originalCredential = getOriginalVerifiableCredential(vcs[match.input_credential_index])
259
+ if (!originalCredential) {
260
+ throw new Error(`Index ${match.input_credential_index} out of range in credentials array`)
261
+ }
262
+ presentation[key] =
263
+ (originalCredential as any)['compactSdJwtVc'] !== undefined ? (originalCredential as any).compactSdJwtVc : originalCredential
264
+ }
265
+ })
142
266
  }
143
- }
144
- }
145
267
 
146
- const dcqlPresentation = DcqlPresentation.parse(presentation)
268
+ const response = session.sendAuthorizationResponse({
269
+ responseSignerOpts: identifier,
270
+ ...{ dcqlQuery: { dcqlPresentation: DcqlPresentation.parse(presentation) } },
271
+ })
147
272
 
148
- const response = session.sendAuthorizationResponse({
149
- responseSignerOpts: identifier,
150
- dcqlResponse: {
151
- dcqlPresentation,
152
- },
153
- })
273
+ logger.debug(`Response: `, response)
154
274
 
155
- logger.debug(`Response: `, response)
156
- return response
275
+ return response
276
+ }
277
+ }
278
+ throw Error('Presentation Definition or DCQL is required')
157
279
  }
158
280
 
159
- const retrieveEncodedCredential = (credential: UniqueDigitalCredential): OriginalVerifiableCredential | undefined => {
160
- return credential.originalVerifiableCredential !== undefined &&
161
- credential.originalVerifiableCredential !== null &&
162
- (credential?.originalVerifiableCredential as SdJwtDecodedVerifiableCredential)?.compactSdJwtVc !== undefined &&
163
- (credential?.originalVerifiableCredential as SdJwtDecodedVerifiableCredential)?.compactSdJwtVc !== null
164
- ? (credential.originalVerifiableCredential as SdJwtDecodedVerifiableCredential).compactSdJwtVc
165
- : credential.originalVerifiableCredential
281
+ function buildPartialPD(
282
+ inputDescriptor: InputDescriptorV1 | InputDescriptorV2,
283
+ presentationDefinition: PresentationDefinitionV1 | PresentationDefinitionV2,
284
+ ): IPresentationDefinition {
285
+ return {
286
+ ...presentationDefinition,
287
+ input_descriptors: [inputDescriptor],
288
+ } as IPresentationDefinition
166
289
  }
167
290
 
168
- export const getSelectableCredentials = async (dcqlQuery: DcqlQuery, context: RequiredContext): Promise<SelectableCredentialsMap> => {
169
- const agentContext = { ...context, agent: context.agent }
291
+ export const getSelectableCredentials = async (
292
+ presentationDefinition: IPresentationDefinition,
293
+ context: RequiredContext,
294
+ ): Promise<SelectableCredentialsMap> => {
295
+ const agentContext = { ...context, agent: context.agent as SuitableCredentialAgents }
170
296
  const { agent } = agentContext
297
+ const pex = new PEX()
298
+
171
299
  const uniqueVerifiableCredentials = await agent.crsGetUniqueCredentials({
172
300
  filter: verifiableCredentialForRoleFilter(CredentialRole.HOLDER),
173
301
  })
174
- const branding = await agent.ibGetCredentialBranding()
175
- const dcqlCredentialsWithCredentials = new Map(uniqueVerifiableCredentials.map((vc) => [convertToDcqlCredentials(vc), vc]))
176
- const queryResult = DcqlQuery.query(dcqlQuery, Array.from(dcqlCredentialsWithCredentials.keys()))
177
- const uniqueCredentials = Array.from(dcqlCredentialsWithCredentials.values())
302
+ const credentialBranding = await agent.ibGetCredentialBranding()
303
+
178
304
  const selectableCredentialsMap: SelectableCredentialsMap = new Map()
179
305
 
180
- for (const [key, value] of Object.entries(queryResult.credential_matches)) {
181
- if (!value.valid_credentials) {
182
- continue
183
- }
306
+ for (const inputDescriptor of presentationDefinition.input_descriptors) {
307
+ const partialPD = buildPartialPD(inputDescriptor, presentationDefinition)
308
+ const originalCredentials = uniqueVerifiableCredentials.map((uniqueVC) => {
309
+ return CredentialMapper.storedCredentialToOriginalFormat(uniqueVC.originalVerifiableCredential!) // ( ! is valid for verifiableCredentialForRoleFilter )
310
+ })
311
+ const selectionResults = pex.selectFrom(partialPD, originalCredentials)
184
312
 
185
- const mapSelectableCredentialPromises = value.valid_credentials.map(async (cred) => {
186
- const matchedCredential = uniqueCredentials[cred.input_credential_index]
187
- const credentialBranding = branding.filter((cb) => cb.vcHash === matchedCredential.hash)
188
- const issuerPartyIdentity = await agent.cmGetContacts({
189
- filter: [{ identities: { identifier: { correlationId: matchedCredential.uniformVerifiableCredential!.issuerDid } } }],
190
- })
191
- const subjectPartyIdentity = await agent.cmGetContacts({
192
- filter: [{ identities: { identifier: { correlationId: matchedCredential.uniformVerifiableCredential!.subjectDid } } }],
313
+ const selectableCredentials: Array<SelectableCredential> = []
314
+ for (const selectedCredential of selectionResults.verifiableCredential || []) {
315
+ const filteredUniqueVC = uniqueVerifiableCredentials.find((uniqueVC) => {
316
+ const proof = uniqueVC.uniformVerifiableCredential!.proof
317
+ return Array.isArray(proof) ? proof.some((proofItem) => proofItem.jwt === selectedCredential) : proof.jwt === selectedCredential
193
318
  })
194
319
 
195
- return {
196
- credential: matchedCredential,
197
- credentialBranding: credentialBranding[0]?.localeBranding,
198
- issuerParty: issuerPartyIdentity?.[0],
199
- subjectParty: subjectPartyIdentity?.[0],
200
- }
201
- })
320
+ if (filteredUniqueVC) {
321
+ const filteredCredentialBrandings = credentialBranding.filter((cb) => cb.vcHash === filteredUniqueVC.hash)
322
+ const issuerPartyIdentity = await agent.cmGetContacts({
323
+ filter: [{ identities: { identifier: { correlationId: filteredUniqueVC.uniformVerifiableCredential!.issuerDid } } }],
324
+ })
325
+ const subjectPartyIdentity = await agent.cmGetContacts({
326
+ filter: [{ identities: { identifier: { correlationId: filteredUniqueVC.uniformVerifiableCredential!.subjectDid } } }],
327
+ })
202
328
 
203
- const selectableCredentials: Array<SelectableCredential> = await Promise.all(mapSelectableCredentialPromises)
204
- selectableCredentialsMap.set(key, selectableCredentials)
329
+ selectableCredentials.push({
330
+ credential: filteredUniqueVC,
331
+ credentialBranding: filteredCredentialBrandings[0]?.localeBranding,
332
+ issuerParty: issuerPartyIdentity?.[0],
333
+ subjectParty: subjectPartyIdentity?.[0],
334
+ })
335
+ }
336
+ }
337
+ selectableCredentialsMap.set(inputDescriptor.id, selectableCredentials)
205
338
  }
206
-
207
339
  return selectableCredentialsMap
208
340
  }
209
341