@sphereon/ssi-sdk.siopv2-oid4vp-op-auth 0.34.1-next.7 → 0.34.1-next.86
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/dist/index.cjs +592 -1115
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +709 -111
- package/dist/index.d.ts +709 -111
- package/dist/index.js +518 -1041
- package/dist/index.js.map +1 -1
- package/package.json +24 -25
- package/src/agent/DidAuthSiopOpAuthenticator.ts +18 -136
- package/src/index.ts +2 -1
- package/src/machine/Siopv2Machine.ts +4 -4
- package/src/services/Siopv2MachineService.ts +97 -203
- package/src/session/OID4VP.ts +310 -299
- package/src/session/OpSession.ts +22 -114
- package/src/types/IDidAuthSiopOpAuthenticator.ts +5 -58
- package/src/types/identifier/index.ts +0 -4
- package/src/types/siop-service/index.ts +1 -3
- package/src/utils/CredentialUtils.ts +1 -39
- package/src/utils/dcql.ts +21 -19
|
@@ -1,31 +1,38 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
import {
|
|
2
|
+
AuthorizationRequest,
|
|
3
|
+
Json,
|
|
4
|
+
SupportedVersion
|
|
5
|
+
} from '@sphereon/did-auth-siop'
|
|
4
6
|
import { isOID4VCIssuerIdentifier, ManagedIdentifierOptsOrResult } from '@sphereon/ssi-sdk-ext.identifier-resolution'
|
|
5
7
|
import { UniqueDigitalCredential, verifiableCredentialForRoleFilter } from '@sphereon/ssi-sdk.credential-store'
|
|
6
8
|
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
9
|
import {
|
|
10
|
-
|
|
10
|
+
CredentialMapper,
|
|
11
|
+
HasherSync,
|
|
12
|
+
Loggers,
|
|
13
|
+
OriginalVerifiableCredential,
|
|
14
|
+
SdJwtDecodedVerifiableCredential
|
|
15
|
+
} from '@sphereon/ssi-types'
|
|
16
|
+
import { OpSession } from '../session'
|
|
17
|
+
import {
|
|
11
18
|
LOGGER_NAMESPACE,
|
|
12
19
|
RequiredContext,
|
|
13
20
|
SelectableCredential,
|
|
14
21
|
SelectableCredentialsMap,
|
|
15
|
-
Siopv2HolderEvent
|
|
16
|
-
SuitableCredentialAgents,
|
|
17
|
-
VerifiableCredentialsWithDefinition,
|
|
18
|
-
VerifiablePresentationWithDefinition,
|
|
22
|
+
Siopv2HolderEvent
|
|
19
23
|
} from '../types'
|
|
20
|
-
import {
|
|
21
|
-
import {
|
|
22
|
-
import { defaultHasher, encodeJoseBlob } from '@sphereon/ssi-sdk.core'
|
|
23
|
-
import { DcqlCredential, DcqlCredentialPresentation, DcqlPresentation, DcqlQuery } from 'dcql'
|
|
24
|
+
import { encodeJoseBlob } from '@sphereon/ssi-sdk.core'
|
|
25
|
+
import { DcqlPresentation, DcqlQuery } from 'dcql'
|
|
24
26
|
import { convertToDcqlCredentials } from '../utils/dcql'
|
|
25
|
-
import {
|
|
27
|
+
import { IAgentContext, IDIDManager } from '@veramo/core'
|
|
28
|
+
import {
|
|
29
|
+
getOrCreatePrimaryIdentifier,
|
|
30
|
+
SupportedDidMethodEnum
|
|
31
|
+
} from '@sphereon/ssi-sdk-ext.did-utils'
|
|
26
32
|
|
|
27
33
|
export const logger = Loggers.DEFAULT.get(LOGGER_NAMESPACE)
|
|
28
34
|
|
|
35
|
+
// @ts-ignore
|
|
29
36
|
const createEbsiIdentifier = async (agentContext: IAgentContext<IDIDManager>): Promise<ManagedIdentifierOptsOrResult> => {
|
|
30
37
|
logger.log(`No EBSI key present yet. Creating a new one...`)
|
|
31
38
|
const { result: newIdentifier, created } = await getOrCreatePrimaryIdentifier(agentContext, {
|
|
@@ -39,9 +46,10 @@ const createEbsiIdentifier = async (agentContext: IAgentContext<IDIDManager>): P
|
|
|
39
46
|
return await agentContext.agent.identifierManagedGetByDid({ identifier: newIdentifier.did })
|
|
40
47
|
}
|
|
41
48
|
|
|
49
|
+
// @ts-ignore
|
|
42
50
|
const hasEbsiClient = async (authorizationRequest: AuthorizationRequest) => {
|
|
43
|
-
const clientId =
|
|
44
|
-
const redirectUri =
|
|
51
|
+
const clientId = authorizationRequest.getMergedProperty<string>('client_id')
|
|
52
|
+
const redirectUri = authorizationRequest.getMergedProperty<string>('redirect_uri')
|
|
45
53
|
return clientId?.toLowerCase().includes('.ebsi.eu') || redirectUri?.toLowerCase().includes('.ebsi.eu')
|
|
46
54
|
}
|
|
47
55
|
|
|
@@ -49,137 +57,25 @@ export const siopSendAuthorizationResponse = async (
|
|
|
49
57
|
connectionType: ConnectionType,
|
|
50
58
|
args: {
|
|
51
59
|
sessionId: string
|
|
52
|
-
|
|
60
|
+
credentials: Array<UniqueDigitalCredential | OriginalVerifiableCredential>
|
|
53
61
|
idOpts?: ManagedIdentifierOptsOrResult
|
|
54
62
|
isFirstParty?: boolean
|
|
55
63
|
hasher?: HasherSync
|
|
56
|
-
dcqlQuery?: DcqlQuery
|
|
57
64
|
},
|
|
58
65
|
context: RequiredContext,
|
|
59
66
|
) => {
|
|
60
67
|
const { agent } = context
|
|
61
|
-
const
|
|
62
|
-
let { idOpts, isFirstParty, hasher = defaultHasher } = args
|
|
63
|
-
|
|
68
|
+
const { credentials } = args
|
|
64
69
|
if (connectionType !== ConnectionType.SIOPv2_OpenID4VP) {
|
|
65
70
|
return Promise.reject(Error(`No supported authentication provider for type: ${connectionType}`))
|
|
66
71
|
}
|
|
72
|
+
|
|
67
73
|
const session: OpSession = await agent.siopGetOPSession({ sessionId: args.sessionId })
|
|
68
74
|
const request = await session.getAuthorizationRequest()
|
|
69
|
-
const aud =
|
|
75
|
+
const aud = request.authorizationRequest.getMergedProperty<string>('aud')
|
|
70
76
|
logger.debug(`AUD: ${aud}`)
|
|
71
77
|
logger.debug(JSON.stringify(request.authorizationRequest))
|
|
72
78
|
|
|
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
|
-
}
|
|
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
|
-
}
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
if (identifier === undefined && idOpts !== undefined && (await hasEbsiClient(request.authorizationRequest))) {
|
|
148
|
-
identifier = await createEbsiIdentifier(agentContext)
|
|
149
|
-
}
|
|
150
|
-
logger.debug(`Identifier`, identifier)
|
|
151
|
-
|
|
152
|
-
// TODO Add mdoc support
|
|
153
|
-
|
|
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
|
-
}
|
|
166
|
-
|
|
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
79
|
const domain =
|
|
184
80
|
((await request.authorizationRequest.getMergedProperty('client_id')) as string) ??
|
|
185
81
|
request.issuer ??
|
|
@@ -188,7 +84,7 @@ export const siopSendAuthorizationResponse = async (
|
|
|
188
84
|
: 'https://self-issued.me/v2')
|
|
189
85
|
logger.debug(`NONCE: ${session.nonce}, domain: ${domain}`)
|
|
190
86
|
|
|
191
|
-
const firstUniqueDC =
|
|
87
|
+
const firstUniqueDC = credentials[0]
|
|
192
88
|
if (typeof firstUniqueDC !== 'object' || !('digitalCredential' in firstUniqueDC)) {
|
|
193
89
|
return Promise.reject(Error('SiopMachine only supports UniqueDigitalCredentials for now'))
|
|
194
90
|
}
|
|
@@ -238,104 +134,102 @@ export const siopSendAuthorizationResponse = async (
|
|
|
238
134
|
})
|
|
239
135
|
}
|
|
240
136
|
}
|
|
241
|
-
console.log(`Identifier`, identifier)
|
|
242
137
|
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
if (rep) {
|
|
247
|
-
dcqlRepresentations.push(rep)
|
|
248
|
-
}
|
|
249
|
-
})
|
|
138
|
+
const dcqlCredentialsWithCredentials = new Map(
|
|
139
|
+
credentials.map((vc) => [convertToDcqlCredentials(vc), vc])
|
|
140
|
+
)
|
|
250
141
|
|
|
251
|
-
|
|
252
|
-
const presentation: Record<string, DcqlCredentialPresentation> = {}
|
|
142
|
+
const queryResult = DcqlQuery.query(request.dcqlQuery, Array.from(dcqlCredentialsWithCredentials.keys()))
|
|
253
143
|
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
144
|
+
if (!queryResult.can_be_satisfied) {
|
|
145
|
+
return Promise.reject(Error('Credentials do not match required query request'))
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
const presentation: DcqlPresentation.Output = {}
|
|
149
|
+
const uniqueCredentials = Array.from(dcqlCredentialsWithCredentials.values())
|
|
150
|
+
for (const [key, value] of Object.entries(queryResult.credential_matches)) {
|
|
151
|
+
if (value.success) {
|
|
152
|
+
const matchedCredentials = value.valid_credentials.map(cred => uniqueCredentials[cred.input_credential_index])
|
|
153
|
+
const vc = matchedCredentials[0] // taking the first match for now //uniqueCredentials[value.input_credential_index]
|
|
154
|
+
if (!vc) {
|
|
155
|
+
continue
|
|
156
|
+
}
|
|
157
|
+
const originalVc = retrieveEncodedCredential(vc as UniqueDigitalCredential) // TODO this is not nice // also always a UniqueDigitalCredential
|
|
158
|
+
if (!originalVc) {
|
|
159
|
+
continue
|
|
266
160
|
}
|
|
161
|
+
if (originalVc) {
|
|
162
|
+
presentation[key] = originalVc as | string | { [x: string]: Json }
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
const dcqlPresentation = DcqlPresentation.parse(presentation)
|
|
267
168
|
|
|
268
169
|
const response = session.sendAuthorizationResponse({
|
|
269
170
|
responseSignerOpts: identifier,
|
|
270
|
-
|
|
171
|
+
dcqlResponse: {
|
|
172
|
+
dcqlPresentation
|
|
173
|
+
}
|
|
271
174
|
})
|
|
272
175
|
|
|
273
176
|
logger.debug(`Response: `, response)
|
|
274
|
-
|
|
275
177
|
return response
|
|
276
|
-
}
|
|
277
|
-
}
|
|
278
|
-
throw Error('Presentation Definition or DCQL is required')
|
|
279
178
|
}
|
|
280
179
|
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
)
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
} as IPresentationDefinition
|
|
180
|
+
const retrieveEncodedCredential = (credential: UniqueDigitalCredential): OriginalVerifiableCredential | undefined => {
|
|
181
|
+
return credential.originalVerifiableCredential !== undefined &&
|
|
182
|
+
credential.originalVerifiableCredential !== null &&
|
|
183
|
+
(credential?.originalVerifiableCredential as SdJwtDecodedVerifiableCredential)?.compactSdJwtVc !== undefined &&
|
|
184
|
+
(credential?.originalVerifiableCredential as SdJwtDecodedVerifiableCredential)?.compactSdJwtVc !== null
|
|
185
|
+
? (credential.originalVerifiableCredential as SdJwtDecodedVerifiableCredential).compactSdJwtVc
|
|
186
|
+
: credential.originalVerifiableCredential
|
|
289
187
|
}
|
|
290
188
|
|
|
291
189
|
export const getSelectableCredentials = async (
|
|
292
|
-
|
|
190
|
+
dcqlQuery: DcqlQuery,
|
|
293
191
|
context: RequiredContext,
|
|
294
192
|
): Promise<SelectableCredentialsMap> => {
|
|
295
|
-
const agentContext = { ...context, agent: context.agent
|
|
193
|
+
const agentContext = { ...context, agent: context.agent }
|
|
296
194
|
const { agent } = agentContext
|
|
297
|
-
const pex = new PEX()
|
|
298
|
-
|
|
299
195
|
const uniqueVerifiableCredentials = await agent.crsGetUniqueCredentials({
|
|
300
196
|
filter: verifiableCredentialForRoleFilter(CredentialRole.HOLDER),
|
|
301
197
|
})
|
|
302
|
-
const
|
|
303
|
-
|
|
198
|
+
const branding = await agent.ibGetCredentialBranding()
|
|
199
|
+
const dcqlCredentialsWithCredentials = new Map(
|
|
200
|
+
uniqueVerifiableCredentials.map((vc) => [convertToDcqlCredentials(vc), vc])
|
|
201
|
+
)
|
|
202
|
+
const queryResult = DcqlQuery.query(dcqlQuery, Array.from(dcqlCredentialsWithCredentials.keys()))
|
|
203
|
+
const uniqueCredentials = Array.from(dcqlCredentialsWithCredentials.values())
|
|
304
204
|
const selectableCredentialsMap: SelectableCredentialsMap = new Map()
|
|
305
205
|
|
|
306
|
-
for (const
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
})
|
|
311
|
-
const selectionResults = pex.selectFrom(partialPD, originalCredentials)
|
|
206
|
+
for (const [key, value] of Object.entries(queryResult.credential_matches)) {
|
|
207
|
+
if (!value.valid_credentials) {
|
|
208
|
+
continue
|
|
209
|
+
}
|
|
312
210
|
|
|
313
|
-
const
|
|
314
|
-
|
|
315
|
-
const
|
|
316
|
-
|
|
317
|
-
|
|
211
|
+
const mapSelectableCredentialPromises = value.valid_credentials.map(async cred => {
|
|
212
|
+
const matchedCredential = uniqueCredentials[cred.input_credential_index]
|
|
213
|
+
const credentialBranding = branding.filter((cb) => cb.vcHash === matchedCredential.hash)
|
|
214
|
+
const issuerPartyIdentity = await agent.cmGetContacts({
|
|
215
|
+
filter: [{ identities: { identifier: { correlationId: matchedCredential.uniformVerifiableCredential!.issuerDid } } }],
|
|
216
|
+
})
|
|
217
|
+
const subjectPartyIdentity = await agent.cmGetContacts({
|
|
218
|
+
filter: [{ identities: { identifier: { correlationId: matchedCredential.uniformVerifiableCredential!.subjectDid } } }],
|
|
318
219
|
})
|
|
319
220
|
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
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
|
-
})
|
|
221
|
+
return {
|
|
222
|
+
credential: matchedCredential,
|
|
223
|
+
credentialBranding: credentialBranding[0]?.localeBranding,
|
|
224
|
+
issuerParty: issuerPartyIdentity?.[0],
|
|
225
|
+
subjectParty: subjectPartyIdentity?.[0],
|
|
335
226
|
}
|
|
336
|
-
}
|
|
337
|
-
|
|
227
|
+
})
|
|
228
|
+
|
|
229
|
+
const selectableCredentials: Array<SelectableCredential> = await Promise.all(mapSelectableCredentialPromises)
|
|
230
|
+
selectableCredentialsMap.set(key, selectableCredentials)
|
|
338
231
|
}
|
|
232
|
+
|
|
339
233
|
return selectableCredentialsMap
|
|
340
234
|
}
|
|
341
235
|
|