@sphereon/ssi-sdk.siopv2-oid4vp-op-auth 0.32.1-feature.VDX.341.57 → 0.32.1-feature.new.develop.274
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/agent/DidAuthSiopOpAuthenticator.d.ts +5 -5
- package/dist/agent/DidAuthSiopOpAuthenticator.d.ts.map +1 -1
- package/dist/agent/DidAuthSiopOpAuthenticator.js +85 -64
- package/dist/agent/DidAuthSiopOpAuthenticator.js.map +1 -1
- package/dist/services/Siopv2MachineService.d.ts +4 -3
- package/dist/services/Siopv2MachineService.d.ts.map +1 -1
- package/dist/services/Siopv2MachineService.js +24 -16
- package/dist/services/Siopv2MachineService.js.map +1 -1
- package/dist/session/OID4VP.d.ts +4 -4
- package/dist/session/OID4VP.d.ts.map +1 -1
- package/dist/session/OID4VP.js.map +1 -1
- package/dist/session/OpSession.d.ts.map +1 -1
- package/dist/session/OpSession.js +3 -3
- package/dist/session/OpSession.js.map +1 -1
- package/dist/types/IDidAuthSiopOpAuthenticator.d.ts +12 -8
- package/dist/types/IDidAuthSiopOpAuthenticator.d.ts.map +1 -1
- package/dist/types/IDidAuthSiopOpAuthenticator.js.map +1 -1
- package/dist/types/machine/index.d.ts +1 -0
- package/dist/types/machine/index.d.ts.map +1 -1
- package/dist/types/machine/index.js.map +1 -1
- package/dist/types/siop-service/index.d.ts +20 -5
- package/dist/types/siop-service/index.d.ts.map +1 -1
- package/dist/types/siop-service/index.js.map +1 -1
- package/dist/utils/CredentialUtils.d.ts.map +1 -1
- package/dist/utils/CredentialUtils.js.map +1 -1
- package/dist/utils/dcql.d.ts +2 -2
- package/dist/utils/dcql.d.ts.map +1 -1
- package/dist/utils/dcql.js.map +1 -1
- package/package.json +22 -22
- package/src/agent/DidAuthSiopOpAuthenticator.ts +108 -90
- package/src/services/Siopv2MachineService.ts +20 -11
- package/src/session/OID4VP.ts +6 -6
- package/src/session/OpSession.ts +8 -6
- package/src/types/IDidAuthSiopOpAuthenticator.ts +21 -9
- package/src/types/machine/index.ts +1 -0
- package/src/types/siop-service/index.ts +23 -5
- package/src/utils/CredentialUtils.ts +2 -2
- package/src/utils/dcql.ts +2 -2
|
@@ -10,13 +10,14 @@ import {
|
|
|
10
10
|
NonPersistedIdentity,
|
|
11
11
|
Party,
|
|
12
12
|
} from '@sphereon/ssi-sdk.data-store'
|
|
13
|
-
import {
|
|
13
|
+
import { HasherSync, Loggers, SdJwtDecodedVerifiableCredential } from '@sphereon/ssi-types'
|
|
14
14
|
import { IAgentPlugin } from '@veramo/core'
|
|
15
15
|
import { v4 as uuidv4 } from 'uuid'
|
|
16
16
|
import {
|
|
17
17
|
DidAuthSiopOpAuthenticatorOptions,
|
|
18
18
|
GetSelectableCredentialsArgs,
|
|
19
19
|
IOpSessionArgs,
|
|
20
|
+
Json,
|
|
20
21
|
LOGGER_NAMESPACE,
|
|
21
22
|
RequiredContext,
|
|
22
23
|
schema,
|
|
@@ -27,6 +28,10 @@ import {
|
|
|
27
28
|
import { Siopv2Machine } from '../machine/Siopv2Machine'
|
|
28
29
|
import { getSelectableCredentials, siopSendAuthorizationResponse, translateCorrelationIdToName } from '../services/Siopv2MachineService'
|
|
29
30
|
import { OpSession } from '../session'
|
|
31
|
+
import { PEX, Status } from '@sphereon/pex'
|
|
32
|
+
import { computeEntryHash } from '@veramo/utils'
|
|
33
|
+
import { UniqueDigitalCredential } from '@sphereon/ssi-sdk.credential-store'
|
|
34
|
+
import { EventEmitter } from 'events'
|
|
30
35
|
import {
|
|
31
36
|
IDidAuthSiopOpAuthenticator,
|
|
32
37
|
IGetSiopSessionArgs,
|
|
@@ -34,8 +39,7 @@ import {
|
|
|
34
39
|
IRemoveCustomApprovalForSiopArgs,
|
|
35
40
|
IRemoveSiopSessionArgs,
|
|
36
41
|
IRequiredContext,
|
|
37
|
-
} from '../types
|
|
38
|
-
import { Siopv2Machine as Siopv2MachineId, Siopv2MachineInstanceOpts } from '../types/machine'
|
|
42
|
+
} from '../types'
|
|
39
43
|
|
|
40
44
|
import {
|
|
41
45
|
AddIdentityArgs,
|
|
@@ -48,12 +52,10 @@ import {
|
|
|
48
52
|
SendResponseArgs,
|
|
49
53
|
Siopv2AuthorizationRequestData,
|
|
50
54
|
Siopv2HolderEvent,
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
import {
|
|
55
|
-
import { EventEmitter } from 'events'
|
|
56
|
-
import { DcqlCredential, DcqlPresentation, DcqlQuery } from 'dcql'
|
|
55
|
+
Siopv2Machine as Siopv2MachineId,
|
|
56
|
+
Siopv2MachineInstanceOpts,
|
|
57
|
+
} from '../types'
|
|
58
|
+
import { DcqlCredential, DcqlPresentation, DcqlQuery, DcqlSdJwtVcCredential } from 'dcql'
|
|
57
59
|
|
|
58
60
|
const logger = Loggers.DEFAULT.options(LOGGER_NAMESPACE, {}).get(LOGGER_NAMESPACE)
|
|
59
61
|
|
|
@@ -92,26 +94,20 @@ export class DidAuthSiopOpAuthenticator implements IAgentPlugin {
|
|
|
92
94
|
private readonly sessions: Map<string, OpSession>
|
|
93
95
|
private readonly customApprovals: Record<string, (verifiedAuthorizationRequest: VerifiedAuthorizationRequest, sessionId: string) => Promise<void>>
|
|
94
96
|
private readonly presentationSignCallback?: PresentationSignCallback
|
|
95
|
-
|
|
96
97
|
private readonly onContactIdentityCreated?: (args: OnContactIdentityCreatedArgs) => Promise<void>
|
|
97
98
|
private readonly onIdentifierCreated?: (args: OnIdentifierCreatedArgs) => Promise<void>
|
|
98
99
|
private readonly eventEmitter?: EventEmitter
|
|
99
|
-
private readonly hasher
|
|
100
|
-
|
|
101
|
-
constructor(
|
|
102
|
-
presentationSignCallback
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
hasher?: Hasher, // FIXME BEFORE PR move into DidAuthSiopOpAuthenticatorOptions (move everything into options like we do with the rest of the agent plugins)
|
|
106
|
-
) {
|
|
107
|
-
const { onContactIdentityCreated, onIdentifierCreated } = options ?? {}
|
|
100
|
+
private readonly hasher?: HasherSync
|
|
101
|
+
|
|
102
|
+
constructor(options?: DidAuthSiopOpAuthenticatorOptions) {
|
|
103
|
+
const { onContactIdentityCreated, onIdentifierCreated, hasher, customApprovals = {}, presentationSignCallback } = { ...options }
|
|
104
|
+
|
|
105
|
+
this.hasher = hasher
|
|
108
106
|
this.onContactIdentityCreated = onContactIdentityCreated
|
|
109
107
|
this.onIdentifierCreated = onIdentifierCreated
|
|
110
|
-
|
|
111
|
-
this.sessions = new Map<string, OpSession>()
|
|
112
|
-
this.customApprovals = customApprovals || {}
|
|
113
108
|
this.presentationSignCallback = presentationSignCallback
|
|
114
|
-
this.
|
|
109
|
+
this.sessions = new Map<string, OpSession>()
|
|
110
|
+
this.customApprovals = customApprovals
|
|
115
111
|
}
|
|
116
112
|
|
|
117
113
|
public async onEvent(event: any, context: RequiredContext): Promise<void> {
|
|
@@ -191,8 +187,8 @@ export class DidAuthSiopOpAuthenticator implements IAgentPlugin {
|
|
|
191
187
|
return Siopv2Machine.newInstance(siopv2MachineOpts)
|
|
192
188
|
}
|
|
193
189
|
|
|
194
|
-
private async siopCreateConfig(
|
|
195
|
-
const { url } =
|
|
190
|
+
private async siopCreateConfig<TContext extends CreateConfigArgs>(context: TContext): Promise<CreateConfigResult> {
|
|
191
|
+
const { url } = context
|
|
196
192
|
|
|
197
193
|
if (!url) {
|
|
198
194
|
return Promise.reject(Error('Missing request uri in context'))
|
|
@@ -219,9 +215,14 @@ export class DidAuthSiopOpAuthenticator implements IAgentPlugin {
|
|
|
219
215
|
}
|
|
220
216
|
const { sessionId, redirectUrl } = didAuthConfig
|
|
221
217
|
|
|
222
|
-
const session: OpSession = await agent
|
|
223
|
-
|
|
224
|
-
|
|
218
|
+
const session: OpSession = await agent.siopGetOPSession({ sessionId }).catch(
|
|
219
|
+
async () =>
|
|
220
|
+
await agent.siopRegisterOPSession({
|
|
221
|
+
requestJwtOrUri: redirectUrl,
|
|
222
|
+
sessionId,
|
|
223
|
+
op: { eventEmitter: this.eventEmitter, hasher: this.hasher },
|
|
224
|
+
}),
|
|
225
|
+
)
|
|
225
226
|
|
|
226
227
|
logger.debug(`session: ${JSON.stringify(session.id, null, 2)}`)
|
|
227
228
|
const verifiedAuthorizationRequest = await session.getAuthorizationRequest()
|
|
@@ -339,7 +340,7 @@ export class DidAuthSiopOpAuthenticator implements IAgentPlugin {
|
|
|
339
340
|
}
|
|
340
341
|
|
|
341
342
|
private async siopSendResponse(args: SendResponseArgs, context: RequiredContext): Promise<Siopv2AuthorizationResponseData> {
|
|
342
|
-
const { didAuthConfig, authorizationRequestData, selectedCredentials } = args
|
|
343
|
+
const { didAuthConfig, authorizationRequestData, selectedCredentials, isFirstParty } = args
|
|
343
344
|
|
|
344
345
|
if (didAuthConfig === undefined) {
|
|
345
346
|
return Promise.reject(Error('Missing config in context'))
|
|
@@ -349,65 +350,74 @@ export class DidAuthSiopOpAuthenticator implements IAgentPlugin {
|
|
|
349
350
|
return Promise.reject(Error('Missing authorization request data in context'))
|
|
350
351
|
}
|
|
351
352
|
|
|
352
|
-
const pex = new PEX()
|
|
353
|
+
const pex = new PEX({ hasher: this.hasher })
|
|
353
354
|
const verifiableCredentialsWithDefinition: Array<VerifiableCredentialsWithDefinition> = []
|
|
354
355
|
const dcqlCredentialsWithCredentials: Map<DcqlCredential, UniqueDigitalCredential> = new Map()
|
|
355
356
|
|
|
356
|
-
if (authorizationRequestData.presentationDefinitions
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
357
|
+
if (Array.isArray(authorizationRequestData.presentationDefinitions) && authorizationRequestData?.presentationDefinitions.length > 0) {
|
|
358
|
+
try {
|
|
359
|
+
authorizationRequestData.presentationDefinitions?.forEach((presentationDefinition) => {
|
|
360
|
+
const { areRequiredCredentialsPresent, verifiableCredential: verifiableCredentials } = pex.selectFrom(
|
|
361
|
+
presentationDefinition.definition,
|
|
362
|
+
selectedCredentials.map((udc) => udc.originalVerifiableCredential!),
|
|
363
|
+
)
|
|
364
|
+
|
|
365
|
+
if (areRequiredCredentialsPresent !== Status.ERROR && verifiableCredentials) {
|
|
366
|
+
let uniqueDigitalCredentials: UniqueDigitalCredential[] = []
|
|
367
|
+
uniqueDigitalCredentials = verifiableCredentials.map((vc) => {
|
|
368
|
+
// @ts-ignore FIXME Funke
|
|
369
|
+
const hash = typeof vc === 'string' ? computeEntryHash(vc.split('~'[0])) : computeEntryHash(vc)
|
|
370
|
+
const udc = selectedCredentials.find((udc) => udc.hash == hash || udc.originalVerifiableCredential == vc)
|
|
371
|
+
|
|
372
|
+
if (!udc) {
|
|
373
|
+
throw Error(
|
|
374
|
+
`UniqueDigitalCredential could not be found in store. Either the credential is not present in the store or the hash is not correct.`,
|
|
375
|
+
)
|
|
376
|
+
}
|
|
377
|
+
return udc
|
|
378
|
+
})
|
|
379
|
+
verifiableCredentialsWithDefinition.push({
|
|
380
|
+
definition: presentationDefinition,
|
|
381
|
+
credentials: uniqueDigitalCredentials,
|
|
382
|
+
})
|
|
383
|
+
}
|
|
384
|
+
})
|
|
385
|
+
} catch (e) {
|
|
386
|
+
return Promise.reject(e)
|
|
387
|
+
}
|
|
380
388
|
|
|
381
389
|
if (verifiableCredentialsWithDefinition.length === 0) {
|
|
382
390
|
return Promise.reject(Error('None of the selected credentials match any of the presentation definitions.'))
|
|
383
391
|
}
|
|
384
|
-
} else if (authorizationRequestData.dcqlQuery
|
|
392
|
+
} else if (authorizationRequestData.dcqlQuery) {
|
|
385
393
|
//TODO Only SD-JWT and MSO MDOC are supported at the moment
|
|
386
394
|
if (this.hasMDocCredentials(selectedCredentials) || this.hasSdJwtCredentials(selectedCredentials)) {
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
:
|
|
392
|
-
|
|
393
|
-
:
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
}
|
|
395
|
+
try {
|
|
396
|
+
selectedCredentials.forEach((vc) => {
|
|
397
|
+
if (this.isSdJwtCredential(vc)) {
|
|
398
|
+
const payload = (vc.originalVerifiableCredential as SdJwtDecodedVerifiableCredential).decodedPayload
|
|
399
|
+
const result: DcqlSdJwtVcCredential = {
|
|
400
|
+
claims: payload as { [x: string]: Json },
|
|
401
|
+
vct: payload.vct,
|
|
402
|
+
credential_format: 'vc+sd-jwt',
|
|
403
|
+
}
|
|
404
|
+
dcqlCredentialsWithCredentials.set(result, vc)
|
|
405
|
+
//FIXME MDoc namespaces are incompatible: array of strings vs complex object - https://sphereon.atlassian.net/browse/SPRIND-143
|
|
406
|
+
} else {
|
|
407
|
+
throw Error(`Invalid credential format: ${vc.digitalCredential.documentFormat}`)
|
|
408
|
+
}
|
|
409
|
+
})
|
|
410
|
+
} catch (e) {
|
|
411
|
+
return Promise.reject(e)
|
|
412
|
+
}
|
|
405
413
|
|
|
406
|
-
const dcqlPresentationRecord: DcqlPresentation = {}
|
|
414
|
+
const dcqlPresentationRecord: DcqlPresentation.Output = {}
|
|
407
415
|
const queryResult = DcqlQuery.query(authorizationRequestData.dcqlQuery, Array.from(dcqlCredentialsWithCredentials.keys()))
|
|
408
416
|
for (const [key, value] of Object.entries(queryResult.credential_matches)) {
|
|
409
417
|
if (value.success) {
|
|
410
|
-
dcqlPresentationRecord[key] = this.retrieveEncodedCredential(dcqlCredentialsWithCredentials.get(value.output)!)
|
|
418
|
+
dcqlPresentationRecord[key] = this.retrieveEncodedCredential(dcqlCredentialsWithCredentials.get(value.output)!) as
|
|
419
|
+
| string
|
|
420
|
+
| { [x: string]: Json }
|
|
411
421
|
}
|
|
412
422
|
}
|
|
413
423
|
}
|
|
@@ -419,15 +429,16 @@ export class DidAuthSiopOpAuthenticator implements IAgentPlugin {
|
|
|
419
429
|
sessionId: didAuthConfig.sessionId,
|
|
420
430
|
...(args.idOpts && { idOpts: args.idOpts }),
|
|
421
431
|
...(authorizationRequestData.presentationDefinitions !== undefined && { verifiableCredentialsWithDefinition }),
|
|
432
|
+
isFirstParty,
|
|
422
433
|
hasher: this.hasher,
|
|
423
434
|
},
|
|
424
435
|
context,
|
|
425
436
|
)
|
|
426
437
|
|
|
427
|
-
const contentType = response
|
|
438
|
+
const contentType = response.headers.get('content-type') || ''
|
|
428
439
|
let responseBody: any = null
|
|
429
440
|
|
|
430
|
-
const text = await response
|
|
441
|
+
const text = await response.text()
|
|
431
442
|
if (text) {
|
|
432
443
|
responseBody = contentType.includes('application/json') || text.startsWith('{') ? JSON.parse(text) : text
|
|
433
444
|
}
|
|
@@ -435,31 +446,38 @@ export class DidAuthSiopOpAuthenticator implements IAgentPlugin {
|
|
|
435
446
|
return {
|
|
436
447
|
body: responseBody,
|
|
437
448
|
url: response?.url,
|
|
438
|
-
queryParams:
|
|
449
|
+
queryParams: decodeUriAsJson(response?.url),
|
|
439
450
|
}
|
|
440
451
|
}
|
|
441
452
|
|
|
442
453
|
private hasMDocCredentials = (credentials: UniqueDigitalCredential[]): boolean => {
|
|
443
|
-
return credentials.some(
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
454
|
+
return credentials.some(this.isMDocCredential)
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
private isMDocCredential = (credential: UniqueDigitalCredential) => {
|
|
458
|
+
return (
|
|
459
|
+
credential.digitalCredential.documentFormat === CredentialDocumentFormat.MSO_MDOC &&
|
|
460
|
+
credential.digitalCredential.documentType === DocumentType.VC
|
|
447
461
|
)
|
|
448
462
|
}
|
|
449
463
|
|
|
450
464
|
private hasSdJwtCredentials = (credentials: UniqueDigitalCredential[]): boolean => {
|
|
451
|
-
return credentials.some(
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
465
|
+
return credentials.some(this.isSdJwtCredential)
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
private isSdJwtCredential = (credential: UniqueDigitalCredential) => {
|
|
469
|
+
return (
|
|
470
|
+
credential.digitalCredential.documentFormat === CredentialDocumentFormat.SD_JWT && credential.digitalCredential.documentType === DocumentType.VC
|
|
455
471
|
)
|
|
456
472
|
}
|
|
457
473
|
|
|
458
474
|
private retrieveEncodedCredential = (credential: UniqueDigitalCredential) => {
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
475
|
+
return credential.originalVerifiableCredential !== undefined &&
|
|
476
|
+
credential.originalVerifiableCredential !== null &&
|
|
477
|
+
(credential?.originalVerifiableCredential as SdJwtDecodedVerifiableCredential)?.compactSdJwtVc !== undefined &&
|
|
478
|
+
(credential?.originalVerifiableCredential as SdJwtDecodedVerifiableCredential)?.compactSdJwtVc !== null
|
|
479
|
+
? (credential.originalVerifiableCredential as SdJwtDecodedVerifiableCredential).compactSdJwtVc
|
|
480
|
+
: credential.originalVerifiableCredential
|
|
463
481
|
}
|
|
464
482
|
|
|
465
483
|
private async siopGetSelectableCredentials(args: GetSelectableCredentialsArgs, context: RequiredContext): Promise<SelectableCredentialsMap> {
|
|
@@ -4,7 +4,7 @@ import { InputDescriptorV1, InputDescriptorV2, PresentationDefinitionV1, Present
|
|
|
4
4
|
import { isOID4VCIssuerIdentifier, ManagedIdentifierOptsOrResult } from '@sphereon/ssi-sdk-ext.identifier-resolution'
|
|
5
5
|
import { UniqueDigitalCredential, verifiableCredentialForRoleFilter } from '@sphereon/ssi-sdk.credential-store'
|
|
6
6
|
import { ConnectionType, CredentialRole } from '@sphereon/ssi-sdk.data-store'
|
|
7
|
-
import { CredentialMapper,
|
|
7
|
+
import { CredentialMapper, HasherSync, Loggers, OriginalVerifiableCredential, PresentationSubmission } from '@sphereon/ssi-types'
|
|
8
8
|
import { OID4VP, OpSession } from '../session'
|
|
9
9
|
import {
|
|
10
10
|
DidAgents,
|
|
@@ -20,7 +20,7 @@ import {
|
|
|
20
20
|
import { IAgentContext, IDIDManager } from '@veramo/core'
|
|
21
21
|
import { getOrCreatePrimaryIdentifier, SupportedDidMethodEnum } from '@sphereon/ssi-sdk-ext.did-utils'
|
|
22
22
|
import { encodeJoseBlob } from '@sphereon/ssi-sdk.core'
|
|
23
|
-
import { DcqlCredential,
|
|
23
|
+
import { DcqlCredential, DcqlCredentialPresentation, DcqlPresentation, DcqlQuery } from 'dcql'
|
|
24
24
|
import { convertToDcqlCredentials } from '../utils/dcql'
|
|
25
25
|
import { getOriginalVerifiableCredential } from '../utils/CredentialUtils'
|
|
26
26
|
|
|
@@ -51,14 +51,15 @@ export const siopSendAuthorizationResponse = async (
|
|
|
51
51
|
sessionId: string
|
|
52
52
|
verifiableCredentialsWithDefinition?: VerifiableCredentialsWithDefinition[]
|
|
53
53
|
idOpts?: ManagedIdentifierOptsOrResult
|
|
54
|
+
isFirstParty?: boolean
|
|
55
|
+
hasher?: HasherSync
|
|
54
56
|
dcqlQuery?: DcqlQuery
|
|
55
|
-
hasher?: Hasher
|
|
56
57
|
},
|
|
57
58
|
context: RequiredContext,
|
|
58
59
|
) => {
|
|
59
60
|
const { agent } = context
|
|
60
61
|
const agentContext = { ...context, agent: context.agent as DidAgents }
|
|
61
|
-
let { idOpts } = args
|
|
62
|
+
let { idOpts, isFirstParty, hasher } = args
|
|
62
63
|
|
|
63
64
|
if (connectionType !== ConnectionType.SIOPv2_OpenID4VP) {
|
|
64
65
|
return Promise.reject(Error(`No supported authentication provider for type: ${connectionType}`))
|
|
@@ -72,7 +73,7 @@ export const siopSendAuthorizationResponse = async (
|
|
|
72
73
|
let presentationsAndDefs: VerifiablePresentationWithDefinition[] | undefined
|
|
73
74
|
let presentationSubmission: PresentationSubmission | undefined
|
|
74
75
|
if (await session.hasPresentationDefinitions()) {
|
|
75
|
-
const oid4vp: OID4VP = await session.getOID4VP({})
|
|
76
|
+
const oid4vp: OID4VP = await session.getOID4VP({ hasher })
|
|
76
77
|
|
|
77
78
|
const credentialsAndDefinitions = args.verifiableCredentialsWithDefinition
|
|
78
79
|
? args.verifiableCredentialsWithDefinition
|
|
@@ -128,11 +129,18 @@ export const siopSendAuthorizationResponse = async (
|
|
|
128
129
|
break
|
|
129
130
|
// TODO other implementations?
|
|
130
131
|
default:
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
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
|
+
}
|
|
136
144
|
}
|
|
137
145
|
}
|
|
138
146
|
|
|
@@ -167,6 +175,7 @@ export const siopSendAuthorizationResponse = async (
|
|
|
167
175
|
...(presentationSubmission && { presentationSubmission }),
|
|
168
176
|
// todo: Change issuer value in case we do not use identifier. Use key.meta.jwkThumbprint then
|
|
169
177
|
responseSignerOpts: idOpts!,
|
|
178
|
+
isFirstParty,
|
|
170
179
|
})
|
|
171
180
|
} else if (request.dcqlQuery) {
|
|
172
181
|
if (args.verifiableCredentialsWithDefinition !== undefined && args.verifiableCredentialsWithDefinition !== null) {
|
|
@@ -266,7 +275,7 @@ export const siopSendAuthorizationResponse = async (
|
|
|
266
275
|
return response
|
|
267
276
|
}
|
|
268
277
|
}
|
|
269
|
-
|
|
278
|
+
throw Error('Presentation Definition or DCQL is required')
|
|
270
279
|
}
|
|
271
280
|
|
|
272
281
|
function buildPartialPD(
|
package/src/session/OID4VP.ts
CHANGED
|
@@ -2,15 +2,15 @@ import { PresentationDefinitionWithLocation, PresentationExchange } from '@spher
|
|
|
2
2
|
import { SelectResults, Status, SubmissionRequirementMatch } from '@sphereon/pex'
|
|
3
3
|
import { Format } from '@sphereon/pex-models'
|
|
4
4
|
import {
|
|
5
|
-
isOID4VCIssuerIdentifier,
|
|
6
5
|
isManagedIdentifierDidResult,
|
|
6
|
+
isOID4VCIssuerIdentifier,
|
|
7
7
|
ManagedIdentifierOptsOrResult,
|
|
8
8
|
ManagedIdentifierResult,
|
|
9
9
|
} from '@sphereon/ssi-sdk-ext.identifier-resolution'
|
|
10
10
|
import { ProofOptions } from '@sphereon/ssi-sdk.core'
|
|
11
11
|
import { UniqueDigitalCredential, verifiableCredentialForRoleFilter } from '@sphereon/ssi-sdk.credential-store'
|
|
12
12
|
import { CredentialRole, FindDigitalCredentialArgs } from '@sphereon/ssi-sdk.data-store'
|
|
13
|
-
import { CompactJWT,
|
|
13
|
+
import { CompactJWT, HasherSync, IProof, OriginalVerifiableCredential } from '@sphereon/ssi-types'
|
|
14
14
|
import {
|
|
15
15
|
DEFAULT_JWT_PROOF_TYPE,
|
|
16
16
|
IGetPresentationExchangeArgs,
|
|
@@ -24,7 +24,7 @@ import { OpSession } from './OpSession'
|
|
|
24
24
|
export class OID4VP {
|
|
25
25
|
private readonly session: OpSession
|
|
26
26
|
private readonly allIdentifiers: string[]
|
|
27
|
-
private readonly hasher?:
|
|
27
|
+
private readonly hasher?: HasherSync
|
|
28
28
|
|
|
29
29
|
private constructor(args: IOID4VPArgs) {
|
|
30
30
|
const { session, allIdentifiers, hasher } = args
|
|
@@ -34,7 +34,7 @@ export class OID4VP {
|
|
|
34
34
|
this.hasher = hasher
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
-
public static async init(session: OpSession, allIdentifiers: string[], hasher?:
|
|
37
|
+
public static async init(session: OpSession, allIdentifiers: string[], hasher?: HasherSync): Promise<OID4VP> {
|
|
38
38
|
return new OID4VP({ session, allIdentifiers: allIdentifiers ?? (await session.getSupportedDIDs()), hasher })
|
|
39
39
|
}
|
|
40
40
|
|
|
@@ -68,7 +68,7 @@ export class OID4VP {
|
|
|
68
68
|
skipDidResolution?: boolean
|
|
69
69
|
holderDID?: string
|
|
70
70
|
subjectIsHolder?: boolean
|
|
71
|
-
hasher?:
|
|
71
|
+
hasher?: HasherSync
|
|
72
72
|
applyFilter?: boolean
|
|
73
73
|
},
|
|
74
74
|
): Promise<VerifiablePresentationWithDefinition[]> {
|
|
@@ -88,7 +88,7 @@ export class OID4VP {
|
|
|
88
88
|
holder?: string
|
|
89
89
|
subjectIsHolder?: boolean
|
|
90
90
|
applyFilter?: boolean
|
|
91
|
-
hasher?:
|
|
91
|
+
hasher?: HasherSync
|
|
92
92
|
},
|
|
93
93
|
): Promise<VerifiablePresentationWithDefinition> {
|
|
94
94
|
const { subjectIsHolder, holder, forceNoCredentialsInVP = false } = { ...opts }
|
package/src/session/OpSession.ts
CHANGED
|
@@ -20,10 +20,11 @@ import { encodeBase64url } from '@sphereon/ssi-sdk.core'
|
|
|
20
20
|
import {
|
|
21
21
|
CompactSdJwtVc,
|
|
22
22
|
CredentialMapper,
|
|
23
|
-
|
|
23
|
+
HasherSync,
|
|
24
|
+
OriginalVerifiableCredential,
|
|
24
25
|
parseDid,
|
|
25
26
|
PresentationSubmission,
|
|
26
|
-
W3CVerifiablePresentation
|
|
27
|
+
W3CVerifiablePresentation,
|
|
27
28
|
} from '@sphereon/ssi-types'
|
|
28
29
|
import { IIdentifier, IVerifyResult, TKeyType } from '@veramo/core'
|
|
29
30
|
import Debug from 'debug'
|
|
@@ -292,8 +293,8 @@ export class OpSession {
|
|
|
292
293
|
.jwtEncryptJweCompactJwt({
|
|
293
294
|
recipientKey,
|
|
294
295
|
protectedHeader: {},
|
|
295
|
-
alg: requestObjectPayload.client_metadata.authorization_encrypted_response_alg as JweAlg | undefined ?? 'ECDH-ES',
|
|
296
|
-
enc: requestObjectPayload.client_metadata.authorization_encrypted_response_enc as JweEnc | undefined ?? 'A256GCM',
|
|
296
|
+
alg: (requestObjectPayload.client_metadata.authorization_encrypted_response_alg as JweAlg | undefined) ?? 'ECDH-ES',
|
|
297
|
+
enc: (requestObjectPayload.client_metadata.authorization_encrypted_response_enc as JweEnc | undefined) ?? 'A256GCM',
|
|
297
298
|
apv: encodeBase64url(opts.requestObjectPayload.nonce),
|
|
298
299
|
apu: encodeBase64url(v4()),
|
|
299
300
|
payload: authResponse,
|
|
@@ -359,13 +360,14 @@ export class OpSession {
|
|
|
359
360
|
const responseOpts = {
|
|
360
361
|
verification,
|
|
361
362
|
issuer,
|
|
363
|
+
...(args.isFirstParty && { isFirstParty: args.isFirstParty }),
|
|
362
364
|
...(args.verifiablePresentations && {
|
|
363
365
|
presentationExchange: {
|
|
364
366
|
verifiablePresentations,
|
|
365
367
|
presentationSubmission: args.presentationSubmission,
|
|
366
368
|
} as PresentationExchangeResponseOpts,
|
|
367
369
|
}),
|
|
368
|
-
dcqlQuery: args.
|
|
370
|
+
dcqlQuery: args.dcqlResponse,
|
|
369
371
|
}
|
|
370
372
|
|
|
371
373
|
const authResponse = await op.createAuthorizationResponse(request, responseOpts)
|
|
@@ -378,7 +380,7 @@ export class OpSession {
|
|
|
378
380
|
}
|
|
379
381
|
}
|
|
380
382
|
|
|
381
|
-
private countVCsInAllVPs(verifiablePresentations: W3CVerifiablePresentation[], hasher?:
|
|
383
|
+
private countVCsInAllVPs(verifiablePresentations: W3CVerifiablePresentation[], hasher?: HasherSync) {
|
|
382
384
|
return verifiablePresentations.reduce((sum, vp) => {
|
|
383
385
|
if (CredentialMapper.isMsoMdocDecodedPresentation(vp) || CredentialMapper.isMsoMdocOid4VPEncoded(vp)) {
|
|
384
386
|
return sum + 1
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import {
|
|
2
|
-
|
|
2
|
+
DcqlResponseOpts,
|
|
3
3
|
PresentationDefinitionWithLocation,
|
|
4
4
|
PresentationSignCallback,
|
|
5
5
|
ResponseMode,
|
|
@@ -19,7 +19,7 @@ import { ICredentialStore, UniqueDigitalCredential } from '@sphereon/ssi-sdk.cre
|
|
|
19
19
|
import { Party } from '@sphereon/ssi-sdk.data-store'
|
|
20
20
|
import { IPDManager } from '@sphereon/ssi-sdk.pd-manager'
|
|
21
21
|
import { ISDJwtPlugin } from '@sphereon/ssi-sdk.sd-jwt'
|
|
22
|
-
import {
|
|
22
|
+
import { HasherSync, OriginalVerifiableCredential, PresentationSubmission, W3CVerifiablePresentation } from '@sphereon/ssi-types'
|
|
23
23
|
import { VerifyCallback } from '@sphereon/wellknown-dids-client'
|
|
24
24
|
import {
|
|
25
25
|
IAgentContext,
|
|
@@ -123,8 +123,9 @@ export interface IOpsSendSiopAuthorizationResponseArgs {
|
|
|
123
123
|
// verifiedAuthorizationRequest: VerifiedAuthorizationRequest
|
|
124
124
|
presentationSubmission?: PresentationSubmission
|
|
125
125
|
verifiablePresentations?: W3CVerifiablePresentation[]
|
|
126
|
-
|
|
127
|
-
hasher?:
|
|
126
|
+
dcqlResponse?: DcqlResponseOpts
|
|
127
|
+
hasher?: HasherSync
|
|
128
|
+
isFirstParty?: boolean
|
|
128
129
|
}
|
|
129
130
|
|
|
130
131
|
export enum events {
|
|
@@ -161,7 +162,7 @@ export interface IOPOptions {
|
|
|
161
162
|
presentationSignCallback?: PresentationSignCallback
|
|
162
163
|
|
|
163
164
|
resolveOpts?: ResolveOpts
|
|
164
|
-
hasher?:
|
|
165
|
+
hasher?: HasherSync
|
|
165
166
|
}
|
|
166
167
|
|
|
167
168
|
/*
|
|
@@ -184,19 +185,30 @@ export interface VerifiablePresentationWithDefinition extends VerifiablePresenta
|
|
|
184
185
|
|
|
185
186
|
export interface IOpSessionGetOID4VPArgs {
|
|
186
187
|
allIdentifiers?: string[]
|
|
187
|
-
hasher?:
|
|
188
|
+
hasher?: HasherSync
|
|
188
189
|
}
|
|
189
190
|
|
|
190
191
|
export interface IOID4VPArgs {
|
|
191
192
|
session: OpSession
|
|
192
193
|
allIdentifiers?: string[]
|
|
193
|
-
hasher?:
|
|
194
|
+
hasher?: HasherSync
|
|
194
195
|
}
|
|
195
196
|
|
|
196
197
|
export interface IGetPresentationExchangeArgs {
|
|
197
198
|
verifiableCredentials: OriginalVerifiableCredential[]
|
|
198
199
|
allIdentifiers?: string[]
|
|
199
|
-
hasher?:
|
|
200
|
-
}
|
|
200
|
+
hasher?: HasherSync
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
// It was added here because it's not exported from DCQL anymore
|
|
204
|
+
export type Json =
|
|
205
|
+
| string
|
|
206
|
+
| number
|
|
207
|
+
| boolean
|
|
208
|
+
| null
|
|
209
|
+
| {
|
|
210
|
+
[key: string]: Json
|
|
211
|
+
}
|
|
212
|
+
| Json[]
|
|
201
213
|
|
|
202
214
|
export const DEFAULT_JWT_PROOF_TYPE = 'JwtProof2020'
|
|
@@ -1,4 +1,9 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
PresentationDefinitionWithLocation,
|
|
3
|
+
PresentationSignCallback,
|
|
4
|
+
RPRegistrationMetadataPayload,
|
|
5
|
+
VerifiedAuthorizationRequest,
|
|
6
|
+
} from '@sphereon/did-auth-siop'
|
|
2
7
|
import { IIdentifierResolution, ManagedIdentifierOptsOrResult } from '@sphereon/ssi-sdk-ext.identifier-resolution'
|
|
3
8
|
import { IContactManager } from '@sphereon/ssi-sdk.contact-manager'
|
|
4
9
|
import { ICredentialStore, UniqueDigitalCredential } from '@sphereon/ssi-sdk.credential-store'
|
|
@@ -8,10 +13,14 @@ import { IAgentContext, IDIDManager, IIdentifier, IResolver } from '@veramo/core
|
|
|
8
13
|
import { IDidAuthSiopOpAuthenticator } from '../IDidAuthSiopOpAuthenticator'
|
|
9
14
|
import { Siopv2MachineContext, Siopv2MachineInterpreter, Siopv2MachineState } from '../machine'
|
|
10
15
|
import { DcqlQuery } from 'dcql'
|
|
16
|
+
import { HasherSync } from '@sphereon/ssi-types'
|
|
11
17
|
|
|
12
18
|
export type DidAuthSiopOpAuthenticatorOptions = {
|
|
19
|
+
presentationSignCallback?: PresentationSignCallback
|
|
20
|
+
customApprovals?: Record<string, (verifiedAuthorizationRequest: VerifiedAuthorizationRequest, sessionId: string) => Promise<void>>
|
|
13
21
|
onContactIdentityCreated?: (args: OnContactIdentityCreatedArgs) => Promise<void>
|
|
14
22
|
onIdentifierCreated?: (args: OnIdentifierCreatedArgs) => Promise<void>
|
|
23
|
+
hasher?: HasherSync
|
|
15
24
|
}
|
|
16
25
|
|
|
17
26
|
export type GetMachineArgs = {
|
|
@@ -20,12 +29,21 @@ export type GetMachineArgs = {
|
|
|
20
29
|
stateNavigationListener?: (siopv2Machine: Siopv2MachineInterpreter, state: Siopv2MachineState, navigation?: any) => Promise<void>
|
|
21
30
|
}
|
|
22
31
|
|
|
23
|
-
export type CreateConfigArgs =
|
|
32
|
+
export type CreateConfigArgs = { url: string }
|
|
24
33
|
export type CreateConfigResult = Omit<DidAuthConfig, 'stateId' | 'idOpts'>
|
|
25
|
-
export type GetSiopRequestArgs =
|
|
34
|
+
export type GetSiopRequestArgs = { didAuthConfig?: Omit<DidAuthConfig, 'identifier'>; url: string }
|
|
35
|
+
// FIXME it would be nicer if these function are not tied to a certain machine so that we can start calling them for anywhere
|
|
26
36
|
export type RetrieveContactArgs = Pick<Siopv2MachineContext, 'url' | 'authorizationRequestData'>
|
|
37
|
+
// FIXME it would be nicer if these function are not tied to a certain machine so that we can start calling them for anywhere
|
|
27
38
|
export type AddIdentityArgs = Pick<Siopv2MachineContext, 'contact' | 'authorizationRequestData'>
|
|
28
|
-
export type SendResponseArgs =
|
|
39
|
+
export type SendResponseArgs = {
|
|
40
|
+
didAuthConfig?: Omit<DidAuthConfig, 'identifier'>
|
|
41
|
+
authorizationRequestData?: Siopv2AuthorizationRequestData
|
|
42
|
+
selectedCredentials: Array<UniqueDigitalCredential>
|
|
43
|
+
idOpts?: ManagedIdentifierOptsOrResult
|
|
44
|
+
isFirstParty?: boolean
|
|
45
|
+
}
|
|
46
|
+
// FIXME it would be nicer if these function are not tied to a certain machine so that we can start calling them for anywhere
|
|
29
47
|
export type GetSelectableCredentialsArgs = Pick<Siopv2MachineContext, 'authorizationRequestData'>
|
|
30
48
|
|
|
31
49
|
export enum Siopv2HolderEvent {
|
|
@@ -39,7 +57,7 @@ export enum SupportedLanguage {
|
|
|
39
57
|
}
|
|
40
58
|
|
|
41
59
|
export type Siopv2AuthorizationResponseData = {
|
|
42
|
-
body?: string
|
|
60
|
+
body?: string | Record<string, any>
|
|
43
61
|
url?: string
|
|
44
62
|
queryParams?: Record<string, any>
|
|
45
63
|
}
|