@sphereon/ssi-sdk.siopv2-oid4vp-op-auth 0.34.1-next.3 → 0.34.1-next.323

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,31 +1,21 @@
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'
1
+ import { AuthorizationRequest } from '@sphereon/did-auth-siop'
2
+ import { getOrCreatePrimaryIdentifier, SupportedDidMethodEnum } from '@sphereon/ssi-sdk-ext.did-utils'
4
3
  import { isOID4VCIssuerIdentifier, ManagedIdentifierOptsOrResult } from '@sphereon/ssi-sdk-ext.identifier-resolution'
4
+ import { encodeJoseBlob } from '@sphereon/ssi-sdk.core'
5
5
  import { UniqueDigitalCredential, verifiableCredentialForRoleFilter } from '@sphereon/ssi-sdk.credential-store'
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'
6
+ import { ConnectionType } from '@sphereon/ssi-sdk.data-store-types'
7
+ import { CredentialMapper, CredentialRole, HasherSync, Loggers, OriginalVerifiableCredential } from '@sphereon/ssi-types'
20
8
  import { IAgentContext, IDIDManager } from '@veramo/core'
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'
9
+ import { DcqlPresentation, DcqlQuery } from 'dcql'
10
+ import { createVerifiablePresentationForFormat, OpSession, PresentationBuilderContext } from '../session'
11
+ import { LOGGER_NAMESPACE, RequiredContext, SelectableCredential, SelectableCredentialsMap, Siopv2HolderEvent } from '../types'
24
12
  import { convertToDcqlCredentials } from '../utils/dcql'
25
- import { getOriginalVerifiableCredential } from '../utils/CredentialUtils'
13
+
14
+ const CLOCK_SKEW = 120
26
15
 
27
16
  export const logger = Loggers.DEFAULT.get(LOGGER_NAMESPACE)
28
17
 
18
+ // @ts-ignore
29
19
  const createEbsiIdentifier = async (agentContext: IAgentContext<IDIDManager>): Promise<ManagedIdentifierOptsOrResult> => {
30
20
  logger.log(`No EBSI key present yet. Creating a new one...`)
31
21
  const { result: newIdentifier, created } = await getOrCreatePrimaryIdentifier(agentContext, {
@@ -39,9 +29,10 @@ const createEbsiIdentifier = async (agentContext: IAgentContext<IDIDManager>): P
39
29
  return await agentContext.agent.identifierManagedGetByDid({ identifier: newIdentifier.did })
40
30
  }
41
31
 
32
+ // @ts-ignore
42
33
  const hasEbsiClient = async (authorizationRequest: AuthorizationRequest) => {
43
- const clientId = await authorizationRequest.getMergedProperty<string>('client_id')
44
- const redirectUri = await authorizationRequest.getMergedProperty<string>('redirect_uri')
34
+ const clientId = authorizationRequest.getMergedProperty<string>('client_id')
35
+ const redirectUri = authorizationRequest.getMergedProperty<string>('redirect_uri')
45
36
  return clientId?.toLowerCase().includes('.ebsi.eu') || redirectUri?.toLowerCase().includes('.ebsi.eu')
46
37
  }
47
38
 
@@ -49,293 +40,175 @@ export const siopSendAuthorizationResponse = async (
49
40
  connectionType: ConnectionType,
50
41
  args: {
51
42
  sessionId: string
52
- verifiableCredentialsWithDefinition?: VerifiableCredentialsWithDefinition[]
43
+ credentials: Array<UniqueDigitalCredential | OriginalVerifiableCredential>
53
44
  idOpts?: ManagedIdentifierOptsOrResult
54
45
  isFirstParty?: boolean
55
46
  hasher?: HasherSync
56
- dcqlQuery?: DcqlQuery
57
47
  },
58
48
  context: RequiredContext,
59
49
  ) => {
60
50
  const { agent } = context
61
- const agentContext = { ...context, agent: context.agent as DidAgents }
62
- let { idOpts, isFirstParty, hasher = defaultHasher } = args
63
-
51
+ const { credentials } = args
64
52
  if (connectionType !== ConnectionType.SIOPv2_OpenID4VP) {
65
53
  return Promise.reject(Error(`No supported authentication provider for type: ${connectionType}`))
66
54
  }
55
+
67
56
  const session: OpSession = await agent.siopGetOPSession({ sessionId: args.sessionId })
68
57
  const request = await session.getAuthorizationRequest()
69
- const aud = await request.authorizationRequest.getMergedProperty<string>('aud')
58
+ const aud = request.authorizationRequest.getMergedProperty<string>('aud')
70
59
  logger.debug(`AUD: ${aud}`)
71
60
  logger.debug(JSON.stringify(request.authorizationRequest))
72
61
 
73
- let presentationsAndDefs: VerifiablePresentationWithDefinition[] | undefined
74
- let presentationSubmission: PresentationSubmission | undefined
75
- if (await session.hasPresentationDefinitions()) {
76
- const oid4vp: OID4VP = await session.getOID4VP({ hasher })
77
-
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}`)
88
-
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'))
92
- }
62
+ const domain = ((await request.authorizationRequest.getMergedProperty('client_id')) as string) ?? request.issuer ?? 'https://self-issued.me/v2'
93
63
 
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
- }
145
- }
64
+ logger.debug(`NONCE: ${session.nonce}, domain: ${domain}`)
146
65
 
147
- if (identifier === undefined && idOpts !== undefined && (await hasEbsiClient(request.authorizationRequest))) {
148
- identifier = await createEbsiIdentifier(agentContext)
149
- }
150
- logger.debug(`Identifier`, identifier)
66
+ const firstUniqueDC = credentials[0]
67
+ if (typeof firstUniqueDC !== 'object' || !('digitalCredential' in firstUniqueDC)) {
68
+ return Promise.reject(Error('SiopMachine only supports UniqueDigitalCredentials for now'))
69
+ }
151
70
 
152
- // TODO Add mdoc support
71
+ let identifier: ManagedIdentifierOptsOrResult
72
+ const digitalCredential = firstUniqueDC.digitalCredential
73
+ const firstVC = firstUniqueDC.uniformVerifiableCredential
74
+
75
+ // Determine holder DID for identifier resolution
76
+ let holder: string | undefined
77
+ if (CredentialMapper.isSdJwtDecodedCredential(firstVC)) {
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
80
+ holder = firstVC.decodedPayload.cnf?.jwk ? `did:jwk:${encodeJoseBlob(firstVC.decodedPayload.cnf?.jwk)}#0` : firstVC.decodedPayload.sub
81
+ } else {
82
+ holder = Array.isArray(firstVC.credentialSubject) ? firstVC.credentialSubject[0].id : firstVC.credentialSubject.id
83
+ }
84
+
85
+ // Resolve identifier
86
+ if (!digitalCredential.kmsKeyRef) {
87
+ // In case the store does not have the kmsKeyRef lets search for the holder
153
88
 
154
- presentationsAndDefs = await oid4vp.createVerifiablePresentations(CredentialRole.HOLDER, credentialsAndDefinitions, {
155
- idOpts: identifier,
156
- proofOpts: {
157
- nonce: session.nonce,
158
- domain,
159
- },
89
+ if (!holder) {
90
+ return Promise.reject(`No holder found and no kmsKeyRef in DB. Cannot determine identifier to use`)
91
+ }
92
+ try {
93
+ identifier = await session.context.agent.identifierManagedGet({ identifier: holder })
94
+ } catch (e) {
95
+ logger.debug(`Holder DID not found: ${holder}`)
96
+ throw e
97
+ }
98
+ } else if (isOID4VCIssuerIdentifier(digitalCredential.kmsKeyRef)) {
99
+ identifier = await session.context.agent.identifierManagedGetByOID4VCIssuer({
100
+ identifier: firstUniqueDC.digitalCredential.kmsKeyRef,
160
101
  })
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}`)
102
+ } else {
103
+ switch (digitalCredential.subjectCorrelationType) {
104
+ case 'DID':
105
+ identifier = await session.context.agent.identifierManagedGetByDid({
106
+ identifier: digitalCredential.subjectCorrelationId ?? holder,
107
+ kmsKeyRef: digitalCredential.kmsKeyRef,
108
+ })
109
+ break
110
+ // TODO other implementations?
111
+ default:
112
+ // Since we are using the kmsKeyRef we will find the KID regardless of the identifier. We set it for later access though
113
+ identifier = await session.context.agent.identifierManagedGetByKid({
114
+ identifier: digitalCredential.subjectCorrelationId ?? holder ?? digitalCredential.kmsKeyRef,
115
+ kmsKeyRef: digitalCredential.kmsKeyRef,
116
+ })
165
117
  }
118
+ }
166
119
 
167
- idOpts = presentationsAndDefs[0].idOpts
168
- presentationSubmission = presentationsAndDefs[0].presentationSubmission
169
-
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'))
194
- }
120
+ const dcqlCredentialsWithCredentials = new Map(credentials.map((vc) => [convertToDcqlCredentials(vc), vc]))
195
121
 
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
- }
240
- }
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
- })
122
+ const queryResult = DcqlQuery.query(request.dcqlQuery, Array.from(dcqlCredentialsWithCredentials.keys()))
250
123
 
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
- })
266
- }
124
+ if (!queryResult.can_be_satisfied) {
125
+ return Promise.reject(Error('Credentials do not match required query request'))
126
+ }
267
127
 
268
- const response = session.sendAuthorizationResponse({
269
- responseSignerOpts: identifier,
270
- ...{ dcqlQuery: { dcqlPresentation: DcqlPresentation.parse(presentation) } },
271
- })
128
+ // Build presentation context for format-aware VP creation
129
+ const presentationContext: PresentationBuilderContext = {
130
+ nonce: request.requestObject?.getPayload()?.nonce ?? session.nonce,
131
+ audience: domain,
132
+ agent: context.agent,
133
+ clockSkew: CLOCK_SKEW,
134
+ hasher: args.hasher,
135
+ }
272
136
 
273
- logger.debug(`Response: `, response)
137
+ // Build DCQL presentation with format-aware VPs
138
+ const presentation: DcqlPresentation.Output = {}
139
+ const uniqueCredentials = Array.from(dcqlCredentialsWithCredentials.values())
140
+ for (const [key, value] of Object.entries(queryResult.credential_matches)) {
141
+ if (value.success) {
142
+ const matchedCredentials = value.valid_credentials.map((cred) => uniqueCredentials[cred.input_credential_index])
143
+ const vc = matchedCredentials[0] // taking the first match for now
274
144
 
275
- return response
145
+ if (!vc) {
146
+ continue
147
+ }
148
+
149
+ try {
150
+ // Use format-aware presentation builder
151
+ const vp = await createVerifiablePresentationForFormat(vc, identifier, presentationContext)
152
+ presentation[key] = vp as any
153
+ } catch (error) {
154
+ logger.error(`Failed to create VP for credential ${key}:`, error)
155
+ throw error
156
+ }
276
157
  }
277
158
  }
278
- throw Error('Presentation Definition or DCQL is required')
279
- }
280
159
 
281
- function buildPartialPD(
282
- inputDescriptor: InputDescriptorV1 | InputDescriptorV2,
283
- presentationDefinition: PresentationDefinitionV1 | PresentationDefinitionV2,
284
- ): IPresentationDefinition {
285
- return {
286
- ...presentationDefinition,
287
- input_descriptors: [inputDescriptor],
288
- } as IPresentationDefinition
160
+ const dcqlPresentation = DcqlPresentation.parse(presentation)
161
+
162
+ const response = session.sendAuthorizationResponse({
163
+ responseSignerOpts: identifier,
164
+ dcqlResponse: {
165
+ dcqlPresentation,
166
+ },
167
+ })
168
+
169
+ logger.debug(`Response: `, response)
170
+ return response
289
171
  }
290
172
 
291
- export const getSelectableCredentials = async (
292
- presentationDefinition: IPresentationDefinition,
293
- context: RequiredContext,
294
- ): Promise<SelectableCredentialsMap> => {
295
- const agentContext = { ...context, agent: context.agent as SuitableCredentialAgents }
173
+ export const getSelectableCredentials = async (dcqlQuery: DcqlQuery, context: RequiredContext): Promise<SelectableCredentialsMap> => {
174
+ const agentContext = { ...context, agent: context.agent }
296
175
  const { agent } = agentContext
297
- const pex = new PEX()
298
-
299
176
  const uniqueVerifiableCredentials = await agent.crsGetUniqueCredentials({
300
177
  filter: verifiableCredentialForRoleFilter(CredentialRole.HOLDER),
301
178
  })
302
- const credentialBranding = await agent.ibGetCredentialBranding()
303
-
179
+ const branding = await agent.ibGetCredentialBranding()
180
+ const dcqlCredentialsWithCredentials = new Map(uniqueVerifiableCredentials.map((vc) => [convertToDcqlCredentials(vc), vc]))
181
+ const queryResult = DcqlQuery.query(dcqlQuery, Array.from(dcqlCredentialsWithCredentials.keys()))
182
+ const uniqueCredentials = Array.from(dcqlCredentialsWithCredentials.values())
304
183
  const selectableCredentialsMap: SelectableCredentialsMap = new Map()
305
184
 
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)
185
+ for (const [key, value] of Object.entries(queryResult.credential_matches)) {
186
+ if (!value.valid_credentials) {
187
+ continue
188
+ }
312
189
 
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
190
+ const mapSelectableCredentialPromises = value.valid_credentials.map(async (cred) => {
191
+ const matchedCredential = uniqueCredentials[cred.input_credential_index]
192
+ const credentialBranding = branding.filter((cb) => cb.vcHash === matchedCredential.hash)
193
+ const issuerPartyIdentity = await agent.cmGetContacts({
194
+ filter: [{ identities: { identifier: { correlationId: matchedCredential.uniformVerifiableCredential!.issuerDid } } }],
195
+ })
196
+ const subjectPartyIdentity = await agent.cmGetContacts({
197
+ filter: [{ identities: { identifier: { correlationId: matchedCredential.uniformVerifiableCredential!.subjectDid } } }],
318
198
  })
319
199
 
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
- })
328
-
329
- selectableCredentials.push({
330
- credential: filteredUniqueVC,
331
- credentialBranding: filteredCredentialBrandings[0]?.localeBranding,
332
- issuerParty: issuerPartyIdentity?.[0],
333
- subjectParty: subjectPartyIdentity?.[0],
334
- })
200
+ return {
201
+ credential: matchedCredential,
202
+ credentialBranding: credentialBranding[0]?.localeBranding,
203
+ issuerParty: issuerPartyIdentity?.[0],
204
+ subjectParty: subjectPartyIdentity?.[0],
335
205
  }
336
- }
337
- selectableCredentialsMap.set(inputDescriptor.id, selectableCredentials)
206
+ })
207
+
208
+ const selectableCredentials: Array<SelectableCredential> = await Promise.all(mapSelectableCredentialPromises)
209
+ selectableCredentialsMap.set(key, selectableCredentials)
338
210
  }
211
+
339
212
  return selectableCredentialsMap
340
213
  }
341
214