@sphereon/ssi-sdk.siopv2-oid4vp-op-auth 0.34.1-feature.FIDES.1.274 → 0.34.1-feature.IDK.11.48
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 +1091 -608
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +112 -710
- package/dist/index.d.ts +112 -710
- package/dist/index.js +1046 -563
- package/dist/index.js.map +1 -1
- package/package.json +24 -24
- package/src/agent/DidAuthSiopOpAuthenticator.ts +145 -10
- package/src/index.ts +1 -2
- package/src/machine/Siopv2Machine.ts +5 -5
- package/src/services/Siopv2MachineService.ts +265 -189
- package/src/session/OID4VP.ts +300 -310
- package/src/session/OpSession.ts +114 -22
- package/src/session/functions.ts +8 -1
- package/src/types/IDidAuthSiopOpAuthenticator.ts +59 -6
- package/src/types/identifier/index.ts +4 -0
- package/src/types/machine/index.ts +1 -1
- package/src/types/siop-service/index.ts +10 -12
- package/src/utils/CredentialUtils.ts +40 -2
- package/src/utils/dcql.ts +19 -22
|
@@ -1,31 +1,31 @@
|
|
|
1
|
-
import { AuthorizationRequest } from '@sphereon/did-auth-siop'
|
|
2
|
-
import
|
|
3
|
-
import {
|
|
4
|
-
import { getOrCreatePrimaryIdentifier, SupportedDidMethodEnum } from '@sphereon/ssi-sdk-ext.did-utils'
|
|
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'
|
|
5
4
|
import { isOID4VCIssuerIdentifier, ManagedIdentifierOptsOrResult } from '@sphereon/ssi-sdk-ext.identifier-resolution'
|
|
6
|
-
import { encodeJoseBlob } from '@sphereon/ssi-sdk.core'
|
|
7
5
|
import { UniqueDigitalCredential, verifiableCredentialForRoleFilter } from '@sphereon/ssi-sdk.credential-store'
|
|
8
|
-
import { ConnectionType } from '@sphereon/ssi-sdk.data-store
|
|
9
|
-
import {
|
|
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'
|
|
10
9
|
import {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
10
|
+
DidAgents,
|
|
11
|
+
LOGGER_NAMESPACE,
|
|
12
|
+
RequiredContext,
|
|
13
|
+
SelectableCredential,
|
|
14
|
+
SelectableCredentialsMap,
|
|
15
|
+
Siopv2HolderEvent,
|
|
16
|
+
SuitableCredentialAgents,
|
|
17
|
+
VerifiableCredentialsWithDefinition,
|
|
18
|
+
VerifiablePresentationWithDefinition,
|
|
19
|
+
} from '../types'
|
|
18
20
|
import { IAgentContext, IDIDManager } from '@veramo/core'
|
|
19
|
-
import {
|
|
20
|
-
import {
|
|
21
|
-
import {
|
|
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'
|
|
22
24
|
import { convertToDcqlCredentials } from '../utils/dcql'
|
|
23
|
-
|
|
24
|
-
const CLOCK_SKEW = 120
|
|
25
|
+
import { getOriginalVerifiableCredential } from '../utils/CredentialUtils'
|
|
25
26
|
|
|
26
27
|
export const logger = Loggers.DEFAULT.get(LOGGER_NAMESPACE)
|
|
27
28
|
|
|
28
|
-
// @ts-ignore
|
|
29
29
|
const createEbsiIdentifier = async (agentContext: IAgentContext<IDIDManager>): Promise<ManagedIdentifierOptsOrResult> => {
|
|
30
30
|
logger.log(`No EBSI key present yet. Creating a new one...`)
|
|
31
31
|
const { result: newIdentifier, created } = await getOrCreatePrimaryIdentifier(agentContext, {
|
|
@@ -39,10 +39,9 @@ const createEbsiIdentifier = async (agentContext: IAgentContext<IDIDManager>): P
|
|
|
39
39
|
return await agentContext.agent.identifierManagedGetByDid({ identifier: newIdentifier.did })
|
|
40
40
|
}
|
|
41
41
|
|
|
42
|
-
// @ts-ignore
|
|
43
42
|
const hasEbsiClient = async (authorizationRequest: AuthorizationRequest) => {
|
|
44
|
-
const clientId = authorizationRequest.getMergedProperty<string>('client_id')
|
|
45
|
-
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')
|
|
46
45
|
return clientId?.toLowerCase().includes('.ebsi.eu') || redirectUri?.toLowerCase().includes('.ebsi.eu')
|
|
47
46
|
}
|
|
48
47
|
|
|
@@ -50,186 +49,293 @@ export const siopSendAuthorizationResponse = async (
|
|
|
50
49
|
connectionType: ConnectionType,
|
|
51
50
|
args: {
|
|
52
51
|
sessionId: string
|
|
53
|
-
|
|
52
|
+
verifiableCredentialsWithDefinition?: VerifiableCredentialsWithDefinition[]
|
|
54
53
|
idOpts?: ManagedIdentifierOptsOrResult
|
|
55
54
|
isFirstParty?: boolean
|
|
56
55
|
hasher?: HasherSync
|
|
56
|
+
dcqlQuery?: DcqlQuery
|
|
57
57
|
},
|
|
58
58
|
context: RequiredContext,
|
|
59
59
|
) => {
|
|
60
60
|
const { agent } = context
|
|
61
|
-
const {
|
|
61
|
+
const agentContext = { ...context, agent: context.agent as DidAgents }
|
|
62
|
+
let { idOpts, isFirstParty, hasher = defaultHasher } = args
|
|
63
|
+
|
|
62
64
|
if (connectionType !== ConnectionType.SIOPv2_OpenID4VP) {
|
|
63
65
|
return Promise.reject(Error(`No supported authentication provider for type: ${connectionType}`))
|
|
64
66
|
}
|
|
65
|
-
|
|
66
67
|
const session: OpSession = await agent.siopGetOPSession({ sessionId: args.sessionId })
|
|
67
68
|
const request = await session.getAuthorizationRequest()
|
|
68
|
-
const aud = request.authorizationRequest.getMergedProperty<string>('aud')
|
|
69
|
+
const aud = await request.authorizationRequest.getMergedProperty<string>('aud')
|
|
69
70
|
logger.debug(`AUD: ${aud}`)
|
|
70
71
|
logger.debug(JSON.stringify(request.authorizationRequest))
|
|
71
72
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
? firstVC.credentialSubject[0].id
|
|
92
|
-
: firstVC.credentialSubject.id
|
|
93
|
-
if (!digitalCredential.kmsKeyRef) {
|
|
94
|
-
// In case the store does not have the kmsKeyRef lets search for the holder
|
|
95
|
-
|
|
96
|
-
if (!holder) {
|
|
97
|
-
return Promise.reject(`No holder found and no kmsKeyRef in DB. Cannot determine identifier to use`)
|
|
98
|
-
}
|
|
99
|
-
try {
|
|
100
|
-
identifier = await session.context.agent.identifierManagedGet({ identifier: holder })
|
|
101
|
-
} catch (e) {
|
|
102
|
-
logger.debug(`Holder DID not found: ${holder}`)
|
|
103
|
-
throw e
|
|
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'))
|
|
104
92
|
}
|
|
105
|
-
|
|
106
|
-
identifier
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
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
|
+
}
|
|
124
145
|
}
|
|
125
|
-
}
|
|
126
146
|
|
|
127
|
-
|
|
147
|
+
if (identifier === undefined && idOpts !== undefined && (await hasEbsiClient(request.authorizationRequest))) {
|
|
148
|
+
identifier = await createEbsiIdentifier(agentContext)
|
|
149
|
+
}
|
|
150
|
+
logger.debug(`Identifier`, identifier)
|
|
128
151
|
|
|
129
|
-
|
|
152
|
+
// TODO Add mdoc support
|
|
130
153
|
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
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
|
+
}
|
|
134
166
|
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
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'))
|
|
143
194
|
}
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
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
|
+
}
|
|
147
240
|
}
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
const
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
...updatedSdJwt.kbJwt?.payload,
|
|
157
|
-
// FIXME SSISDK-44
|
|
158
|
-
nonce: updatedSdJwt.kbJwt?.payload.nonce ?? request.requestObject!.getPayload()!.nonce,
|
|
159
|
-
// FIXME SSISDK-44
|
|
160
|
-
aud: updatedSdJwt.kbJwt?.payload.aud ?? domain,
|
|
161
|
-
iat: updatedSdJwt.kbJwt?.payload?.iat ?? Math.floor(Date.now() / 1000 - CLOCK_SKEW),
|
|
162
|
-
},
|
|
163
|
-
},
|
|
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
|
+
}
|
|
164
249
|
})
|
|
165
250
|
|
|
166
|
-
|
|
167
|
-
|
|
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
|
+
})
|
|
168
266
|
}
|
|
169
|
-
}
|
|
170
|
-
}
|
|
171
267
|
|
|
172
|
-
|
|
268
|
+
const response = session.sendAuthorizationResponse({
|
|
269
|
+
responseSignerOpts: identifier,
|
|
270
|
+
...{ dcqlQuery: { dcqlPresentation: DcqlPresentation.parse(presentation) } },
|
|
271
|
+
})
|
|
173
272
|
|
|
174
|
-
|
|
175
|
-
responseSignerOpts: identifier,
|
|
176
|
-
dcqlResponse: {
|
|
177
|
-
dcqlPresentation,
|
|
178
|
-
},
|
|
179
|
-
})
|
|
273
|
+
logger.debug(`Response: `, response)
|
|
180
274
|
|
|
181
|
-
|
|
182
|
-
|
|
275
|
+
return response
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
throw Error('Presentation Definition or DCQL is required')
|
|
183
279
|
}
|
|
184
280
|
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
:
|
|
281
|
+
function buildPartialPD(
|
|
282
|
+
inputDescriptor: InputDescriptorV1 | InputDescriptorV2,
|
|
283
|
+
presentationDefinition: PresentationDefinitionV1 | PresentationDefinitionV2,
|
|
284
|
+
): IPresentationDefinition {
|
|
285
|
+
return {
|
|
286
|
+
...presentationDefinition,
|
|
287
|
+
input_descriptors: [inputDescriptor],
|
|
288
|
+
} as IPresentationDefinition
|
|
192
289
|
}
|
|
193
290
|
|
|
194
|
-
export const getSelectableCredentials = async (
|
|
195
|
-
|
|
291
|
+
export const getSelectableCredentials = async (
|
|
292
|
+
presentationDefinition: IPresentationDefinition,
|
|
293
|
+
context: RequiredContext,
|
|
294
|
+
): Promise<SelectableCredentialsMap> => {
|
|
295
|
+
const agentContext = { ...context, agent: context.agent as SuitableCredentialAgents }
|
|
196
296
|
const { agent } = agentContext
|
|
297
|
+
const pex = new PEX()
|
|
298
|
+
|
|
197
299
|
const uniqueVerifiableCredentials = await agent.crsGetUniqueCredentials({
|
|
198
300
|
filter: verifiableCredentialForRoleFilter(CredentialRole.HOLDER),
|
|
199
301
|
})
|
|
200
|
-
const
|
|
201
|
-
|
|
202
|
-
const queryResult = DcqlQuery.query(dcqlQuery, Array.from(dcqlCredentialsWithCredentials.keys()))
|
|
203
|
-
const uniqueCredentials = Array.from(dcqlCredentialsWithCredentials.values())
|
|
302
|
+
const credentialBranding = await agent.ibGetCredentialBranding()
|
|
303
|
+
|
|
204
304
|
const selectableCredentialsMap: SelectableCredentialsMap = new Map()
|
|
205
305
|
|
|
206
|
-
for (const
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
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)
|
|
210
312
|
|
|
211
|
-
const
|
|
212
|
-
|
|
213
|
-
const
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
})
|
|
217
|
-
const subjectPartyIdentity = await agent.cmGetContacts({
|
|
218
|
-
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
|
|
219
318
|
})
|
|
220
319
|
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
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
|
+
})
|
|
228
328
|
|
|
229
|
-
|
|
230
|
-
|
|
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)
|
|
231
338
|
}
|
|
232
|
-
|
|
233
339
|
return selectableCredentialsMap
|
|
234
340
|
}
|
|
235
341
|
|
|
@@ -246,33 +352,3 @@ export const translateCorrelationIdToName = async (correlationId: string, contex
|
|
|
246
352
|
|
|
247
353
|
return contacts[0].contact.displayName
|
|
248
354
|
}
|
|
249
|
-
|
|
250
|
-
const updateSdJwtCredential = (
|
|
251
|
-
credential: SdJwtDecodedVerifiableCredential,
|
|
252
|
-
nonce?: string,
|
|
253
|
-
aud?: string,
|
|
254
|
-
): PartialSdJwtDecodedVerifiableCredential => {
|
|
255
|
-
const sdJwtCredential = credential as SdJwtDecodedVerifiableCredential
|
|
256
|
-
|
|
257
|
-
// extract sd_alg or default to sha-256
|
|
258
|
-
const hashAlg = sdJwtCredential.signedPayload._sd_alg ?? 'sha-256'
|
|
259
|
-
const sdHash = calculateSdHash(sdJwtCredential.compactSdJwtVc, hashAlg, defaultGenerateDigest)
|
|
260
|
-
|
|
261
|
-
const kbJwt = {
|
|
262
|
-
// alg MUST be set by the signer
|
|
263
|
-
header: {
|
|
264
|
-
typ: 'kb+jwt',
|
|
265
|
-
},
|
|
266
|
-
payload: {
|
|
267
|
-
iat: Math.floor(new Date().getTime() / 1000),
|
|
268
|
-
sd_hash: sdHash,
|
|
269
|
-
...(nonce && { nonce }),
|
|
270
|
-
...(aud && { aud }),
|
|
271
|
-
},
|
|
272
|
-
} satisfies PartialSdJwtKbJwt
|
|
273
|
-
|
|
274
|
-
return {
|
|
275
|
-
...sdJwtCredential,
|
|
276
|
-
kbJwt,
|
|
277
|
-
} satisfies PartialSdJwtDecodedVerifiableCredential
|
|
278
|
-
}
|