@sphereon/ssi-sdk.siopv2-oid4vp-op-auth 0.34.1-next.88 → 0.36.0

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sphereon/ssi-sdk.siopv2-oid4vp-op-auth",
3
- "version": "0.34.1-next.88+7d5f01ed",
3
+ "version": "0.36.0",
4
4
  "source": "src/index.ts",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",
@@ -26,25 +26,26 @@
26
26
  "build": "tsup --config ../../tsup.config.ts --tsconfig ../../tsconfig.tsup.json"
27
27
  },
28
28
  "dependencies": {
29
- "@sphereon/did-auth-siop": "0.19.1-feat.SSISDK.34.74",
30
- "@sphereon/did-auth-siop-adapter": "0.19.1-feat.SSISDK.34.74",
31
- "@sphereon/oid4vc-common": "0.19.1-feat.SSISDK.34.74",
29
+ "@sphereon/did-auth-siop": "0.20.0",
30
+ "@sphereon/did-auth-siop-adapter": "0.20.0",
31
+ "@sphereon/oid4vc-common": "0.20.0",
32
+ "@sphereon/pex": "5.0.0-unstable.28",
32
33
  "@sphereon/pex-models": "^2.3.2",
33
- "@sphereon/ssi-sdk-ext.did-utils": "0.34.1-next.88+7d5f01ed",
34
- "@sphereon/ssi-sdk-ext.identifier-resolution": "0.34.1-next.88+7d5f01ed",
35
- "@sphereon/ssi-sdk-ext.jwt-service": "0.34.1-next.88+7d5f01ed",
36
- "@sphereon/ssi-sdk.contact-manager": "0.34.1-next.88+7d5f01ed",
37
- "@sphereon/ssi-sdk.core": "0.34.1-next.88+7d5f01ed",
38
- "@sphereon/ssi-sdk.credential-store": "0.34.1-next.88+7d5f01ed",
39
- "@sphereon/ssi-sdk.credential-validation": "0.34.1-next.88+7d5f01ed",
40
- "@sphereon/ssi-sdk.data-store": "0.34.1-next.88+7d5f01ed",
41
- "@sphereon/ssi-sdk.issuance-branding": "0.34.1-next.88+7d5f01ed",
42
- "@sphereon/ssi-sdk.pd-manager": "0.34.1-next.88+7d5f01ed",
43
- "@sphereon/ssi-sdk.presentation-exchange": "0.34.1-next.88+7d5f01ed",
44
- "@sphereon/ssi-sdk.sd-jwt": "0.34.1-next.88+7d5f01ed",
45
- "@sphereon/ssi-sdk.siopv2-oid4vp-common": "0.34.1-next.88+7d5f01ed",
46
- "@sphereon/ssi-sdk.xstate-machine-persistence": "0.34.1-next.88+7d5f01ed",
47
- "@sphereon/ssi-types": "0.34.1-next.88+7d5f01ed",
34
+ "@sphereon/ssi-sdk-ext.did-utils": "0.36.0",
35
+ "@sphereon/ssi-sdk-ext.identifier-resolution": "0.36.0",
36
+ "@sphereon/ssi-sdk-ext.jwt-service": "0.36.0",
37
+ "@sphereon/ssi-sdk.contact-manager": "0.36.0",
38
+ "@sphereon/ssi-sdk.core": "0.36.0",
39
+ "@sphereon/ssi-sdk.credential-store": "0.36.0",
40
+ "@sphereon/ssi-sdk.credential-validation": "0.36.0",
41
+ "@sphereon/ssi-sdk.data-store-types": "0.36.0",
42
+ "@sphereon/ssi-sdk.issuance-branding": "0.36.0",
43
+ "@sphereon/ssi-sdk.pd-manager": "0.36.0",
44
+ "@sphereon/ssi-sdk.presentation-exchange": "0.36.0",
45
+ "@sphereon/ssi-sdk.sd-jwt": "0.36.0",
46
+ "@sphereon/ssi-sdk.siopv2-oid4vp-common": "0.36.0",
47
+ "@sphereon/ssi-sdk.xstate-machine-persistence": "0.36.0",
48
+ "@sphereon/ssi-types": "0.36.0",
48
49
  "@sphereon/wellknown-dids-client": "^0.1.3",
49
50
  "@veramo/core": "4.2.0",
50
51
  "@veramo/credential-w3c": "4.2.0",
@@ -58,8 +59,8 @@
58
59
  },
59
60
  "devDependencies": {
60
61
  "@sphereon/did-uni-client": "^0.6.3",
61
- "@sphereon/ssi-sdk-ext.did-resolver-jwk": "0.34.1-next.88+7d5f01ed",
62
- "@sphereon/ssi-sdk.agent-config": "0.34.1-next.88+7d5f01ed",
62
+ "@sphereon/ssi-sdk-ext.did-resolver-jwk": "0.36.0",
63
+ "@sphereon/ssi-sdk.agent-config": "0.36.0",
63
64
  "@types/i18n-js": "^3.8.9",
64
65
  "@types/lodash.memoize": "^4.1.9",
65
66
  "@types/sha.js": "^2.4.4",
@@ -101,5 +102,5 @@
101
102
  "OpenID Connect",
102
103
  "Authenticator"
103
104
  ],
104
- "gitHead": "7d5f01eddab106d7ef084dcef21191ab8dd5f20f"
105
+ "gitHead": "f713d3a83948ef69aaa7d435700b16d5655ac863"
105
106
  }
@@ -1,17 +1,6 @@
1
- import {
2
- decodeUriAsJson,
3
- PresentationSignCallback,
4
- VerifiedAuthorizationRequest } from '@sphereon/did-auth-siop'
5
- import {
6
- ConnectionType,
7
- CorrelationIdentifierType,
8
- CredentialRole,
9
- Identity,
10
- IdentityOrigin,
11
- NonPersistedIdentity,
12
- Party,
13
- } from '@sphereon/ssi-sdk.data-store'
14
- import { HasherSync, Loggers } from '@sphereon/ssi-types'
1
+ import { decodeUriAsJson, PresentationSignCallback, VerifiedAuthorizationRequest } from '@sphereon/did-auth-siop'
2
+ import { ConnectionType, CorrelationIdentifierType, Identity, IdentityOrigin, NonPersistedIdentity, Party } from '@sphereon/ssi-sdk.data-store-types'
3
+ import { HasherSync, Loggers, CredentialRole } from '@sphereon/ssi-types'
15
4
  import { IAgentPlugin } from '@veramo/core'
16
5
  import { v4 as uuidv4 } from 'uuid'
17
6
  import { OpSession } from '../session'
@@ -92,13 +81,7 @@ export class DidAuthSiopOpAuthenticator implements IAgentPlugin {
92
81
  private readonly hasher?: HasherSync
93
82
 
94
83
  constructor(options?: DidAuthSiopOpAuthenticatorOptions) {
95
- const {
96
- onContactIdentityCreated,
97
- onIdentifierCreated,
98
- hasher,
99
- customApprovals = {},
100
- presentationSignCallback
101
- } = { ...options }
84
+ const { onContactIdentityCreated, onIdentifierCreated, hasher, customApprovals = {}, presentationSignCallback } = { ...options }
102
85
 
103
86
  this.hasher = hasher
104
87
  this.onContactIdentityCreated = onContactIdentityCreated
@@ -231,7 +214,7 @@ export class DidAuthSiopOpAuthenticator implements IAgentPlugin {
231
214
  (args.url.includes('request_uri')
232
215
  ? decodeURIComponent(args.url.split('?request_uri=')[1].trim())
233
216
  : (verifiedAuthorizationRequest.issuer ?? verifiedAuthorizationRequest.registrationMetadataPayload?.client_id))
234
- const uri: URL | undefined = url.includes('://') ? new URL(url) : undefined
217
+ const uri: URL | undefined = url?.includes('://') ? new URL(url) : undefined
235
218
  const correlationId: string = uri?.hostname ?? (await this.determineCorrelationId(uri, verifiedAuthorizationRequest, clientName, context))
236
219
  const clientId: string | undefined = verifiedAuthorizationRequest.authorizationRequest.getMergedProperty<string>('client_id')
237
220
 
@@ -1,5 +1,5 @@
1
1
  import { VerifiedAuthorizationRequest } from '@sphereon/did-auth-siop'
2
- import { DidAuthConfig, Identity, Party } from '@sphereon/ssi-sdk.data-store'
2
+ import { DidAuthConfig, Identity, Party } from '@sphereon/ssi-sdk.data-store-types'
3
3
  import { assign, createMachine, DoneInvokeEvent, interpret } from 'xstate'
4
4
  import { translate } from '../localization/Localization'
5
5
  import { ErrorDetails } from '../types'
@@ -1,34 +1,17 @@
1
- import {
2
- AuthorizationRequest,
3
- Json,
4
- SupportedVersion
5
- } from '@sphereon/did-auth-siop'
1
+ import { AuthorizationRequest } from '@sphereon/did-auth-siop'
2
+ import { getOrCreatePrimaryIdentifier, SupportedDidMethodEnum } from '@sphereon/ssi-sdk-ext.did-utils'
6
3
  import { isOID4VCIssuerIdentifier, ManagedIdentifierOptsOrResult } from '@sphereon/ssi-sdk-ext.identifier-resolution'
7
- import { UniqueDigitalCredential, verifiableCredentialForRoleFilter } from '@sphereon/ssi-sdk.credential-store'
8
- import { ConnectionType, CredentialRole } from '@sphereon/ssi-sdk.data-store'
9
- import {
10
- CredentialMapper,
11
- HasherSync,
12
- Loggers,
13
- OriginalVerifiableCredential,
14
- SdJwtDecodedVerifiableCredential
15
- } from '@sphereon/ssi-types'
16
- import { OpSession } from '../session'
17
- import {
18
- LOGGER_NAMESPACE,
19
- RequiredContext,
20
- SelectableCredential,
21
- SelectableCredentialsMap,
22
- Siopv2HolderEvent
23
- } from '../types'
24
4
  import { encodeJoseBlob } from '@sphereon/ssi-sdk.core'
5
+ import { UniqueDigitalCredential, verifiableCredentialForRoleFilter } from '@sphereon/ssi-sdk.credential-store'
6
+ import { ConnectionType } from '@sphereon/ssi-sdk.data-store-types'
7
+ import { CredentialMapper, CredentialRole, HasherSync, Loggers, OriginalVerifiableCredential } from '@sphereon/ssi-types'
8
+ import { IAgentContext, IDIDManager } from '@veramo/core'
25
9
  import { DcqlPresentation, DcqlQuery } from 'dcql'
10
+ import { createVerifiablePresentationForFormat, OpSession, PresentationBuilderContext } from '../session'
11
+ import { LOGGER_NAMESPACE, RequiredContext, SelectableCredential, SelectableCredentialsMap, Siopv2HolderEvent } from '../types'
26
12
  import { convertToDcqlCredentials } from '../utils/dcql'
27
- import { IAgentContext, IDIDManager } from '@veramo/core'
28
- import {
29
- getOrCreatePrimaryIdentifier,
30
- SupportedDidMethodEnum
31
- } from '@sphereon/ssi-sdk-ext.did-utils'
13
+
14
+ const CLOCK_SKEW = 120
32
15
 
33
16
  export const logger = Loggers.DEFAULT.get(LOGGER_NAMESPACE)
34
17
 
@@ -76,68 +59,65 @@ export const siopSendAuthorizationResponse = async (
76
59
  logger.debug(`AUD: ${aud}`)
77
60
  logger.debug(JSON.stringify(request.authorizationRequest))
78
61
 
79
- const domain =
80
- ((await request.authorizationRequest.getMergedProperty('client_id')) as string) ??
81
- request.issuer ??
82
- (request.versions.includes(SupportedVersion.JWT_VC_PRESENTATION_PROFILE_v1)
83
- ? 'https://self-issued.me/v2/openid-vc'
84
- : 'https://self-issued.me/v2')
85
- logger.debug(`NONCE: ${session.nonce}, domain: ${domain}`)
86
-
87
- const firstUniqueDC = credentials[0]
88
- if (typeof firstUniqueDC !== 'object' || !('digitalCredential' in firstUniqueDC)) {
89
- return Promise.reject(Error('SiopMachine only supports UniqueDigitalCredentials for now'))
90
- }
62
+ const domain = ((await request.authorizationRequest.getMergedProperty('client_id')) as string) ?? request.issuer ?? 'https://self-issued.me/v2'
91
63
 
92
- let identifier: ManagedIdentifierOptsOrResult
93
- const digitalCredential = firstUniqueDC.digitalCredential
94
- const firstVC = firstUniqueDC.uniformVerifiableCredential
95
- const holder = CredentialMapper.isSdJwtDecodedCredential(firstVC)
96
- ? firstVC.decodedPayload.cnf?.jwk
97
- ? //TODO SDK-19: convert the JWK to hex and search for the appropriate key and associated DID
98
- //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
99
- `did:jwk:${encodeJoseBlob(firstVC.decodedPayload.cnf?.jwk)}#0`
100
- : firstVC.decodedPayload.sub
101
- : Array.isArray(firstVC.credentialSubject)
102
- ? firstVC.credentialSubject[0].id
103
- : firstVC.credentialSubject.id
104
- if (!digitalCredential.kmsKeyRef) {
105
- // In case the store does not have the kmsKeyRef lets search for the holder
106
-
107
- if (!holder) {
108
- return Promise.reject(`No holder found and no kmsKeyRef in DB. Cannot determine identifier to use`)
109
- }
110
- try {
111
- identifier = await session.context.agent.identifierManagedGet({ identifier: holder })
112
- } catch (e) {
113
- logger.debug(`Holder DID not found: ${holder}`)
114
- throw e
115
- }
116
- } else if (isOID4VCIssuerIdentifier(digitalCredential.kmsKeyRef)) {
117
- identifier = await session.context.agent.identifierManagedGetByOID4VCIssuer({
118
- identifier: firstUniqueDC.digitalCredential.kmsKeyRef,
64
+ logger.debug(`NONCE: ${session.nonce}, domain: ${domain}`)
65
+
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
+ }
70
+
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
88
+
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,
101
+ })
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,
119
108
  })
120
- } else {
121
- switch (digitalCredential.subjectCorrelationType) {
122
- case 'DID':
123
- identifier = await session.context.agent.identifierManagedGetByDid({
124
- identifier: digitalCredential.subjectCorrelationId ?? holder,
125
- kmsKeyRef: digitalCredential.kmsKeyRef,
126
- })
127
- break
128
- // TODO other implementations?
129
- default:
130
- // Since we are using the kmsKeyRef we will find the KID regardless of the identifier. We set it for later access though
131
- identifier = await session.context.agent.identifierManagedGetByKid({
132
- identifier: digitalCredential.subjectCorrelationId ?? holder ?? digitalCredential.kmsKeyRef,
133
- kmsKeyRef: digitalCredential.kmsKeyRef,
134
- })
135
- }
136
- }
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
+ })
117
+ }
118
+ }
137
119
 
138
- const dcqlCredentialsWithCredentials = new Map(
139
- credentials.map((vc) => [convertToDcqlCredentials(vc), vc])
140
- )
120
+ const dcqlCredentialsWithCredentials = new Map(credentials.map((vc) => [convertToDcqlCredentials(vc), vc]))
141
121
 
142
122
  const queryResult = DcqlQuery.query(request.dcqlQuery, Array.from(dcqlCredentialsWithCredentials.keys()))
143
123
 
@@ -145,60 +125,59 @@ export const siopSendAuthorizationResponse = async (
145
125
  return Promise.reject(Error('Credentials do not match required query request'))
146
126
  }
147
127
 
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
+ }
136
+
137
+ // Build DCQL presentation with format-aware VPs
148
138
  const presentation: DcqlPresentation.Output = {}
149
139
  const uniqueCredentials = Array.from(dcqlCredentialsWithCredentials.values())
150
140
  for (const [key, value] of Object.entries(queryResult.credential_matches)) {
151
141
  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]
142
+ const matchedCredentials = value.valid_credentials.map((cred) => uniqueCredentials[cred.input_credential_index])
143
+ const vc = matchedCredentials[0] // taking the first match for now
144
+
154
145
  if (!vc) {
155
146
  continue
156
147
  }
157
- const originalVc = retrieveEncodedCredential(vc as UniqueDigitalCredential) // TODO this is not nice // also always a UniqueDigitalCredential
158
- if (!originalVc) {
159
- continue
160
- }
161
- if (originalVc) {
162
- presentation[key] = originalVc as | string | { [x: string]: Json }
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
163
156
  }
164
157
  }
165
158
  }
166
159
 
167
160
  const dcqlPresentation = DcqlPresentation.parse(presentation)
168
161
 
169
- const response = session.sendAuthorizationResponse({
170
- responseSignerOpts: identifier,
171
- dcqlResponse: {
172
- dcqlPresentation
173
- }
174
- })
175
-
176
- logger.debug(`Response: `, response)
177
- return response
178
- }
162
+ const response = session.sendAuthorizationResponse({
163
+ responseSignerOpts: identifier,
164
+ dcqlResponse: {
165
+ dcqlPresentation,
166
+ },
167
+ })
179
168
 
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
169
+ logger.debug(`Response: `, response)
170
+ return response
187
171
  }
188
172
 
189
- export const getSelectableCredentials = async (
190
- dcqlQuery: DcqlQuery,
191
- context: RequiredContext,
192
- ): Promise<SelectableCredentialsMap> => {
173
+ export const getSelectableCredentials = async (dcqlQuery: DcqlQuery, context: RequiredContext): Promise<SelectableCredentialsMap> => {
193
174
  const agentContext = { ...context, agent: context.agent }
194
175
  const { agent } = agentContext
195
176
  const uniqueVerifiableCredentials = await agent.crsGetUniqueCredentials({
196
177
  filter: verifiableCredentialForRoleFilter(CredentialRole.HOLDER),
197
178
  })
198
179
  const branding = await agent.ibGetCredentialBranding()
199
- const dcqlCredentialsWithCredentials = new Map(
200
- uniqueVerifiableCredentials.map((vc) => [convertToDcqlCredentials(vc), vc])
201
- )
180
+ const dcqlCredentialsWithCredentials = new Map(uniqueVerifiableCredentials.map((vc) => [convertToDcqlCredentials(vc), vc]))
202
181
  const queryResult = DcqlQuery.query(dcqlQuery, Array.from(dcqlCredentialsWithCredentials.keys()))
203
182
  const uniqueCredentials = Array.from(dcqlCredentialsWithCredentials.values())
204
183
  const selectableCredentialsMap: SelectableCredentialsMap = new Map()
@@ -208,7 +187,7 @@ export const getSelectableCredentials = async (
208
187
  continue
209
188
  }
210
189
 
211
- const mapSelectableCredentialPromises = value.valid_credentials.map(async cred => {
190
+ const mapSelectableCredentialPromises = value.valid_credentials.map(async (cred) => {
212
191
  const matchedCredential = uniqueCredentials[cred.input_credential_index]
213
192
  const credentialBranding = branding.filter((cb) => cb.vcHash === matchedCredential.hash)
214
193
  const issuerPartyIdentity = await agent.cmGetContacts({