@sphereon/ssi-sdk.siopv2-oid4vp-op-auth 0.34.1-fix.80 → 0.34.1-next.278

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-fix.80+f71b3901",
3
+ "version": "0.34.1-next.278+0eacb25b",
4
4
  "source": "src/index.ts",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",
@@ -26,26 +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-feature.DIIPv4.86",
30
- "@sphereon/did-auth-siop-adapter": "0.19.1-feature.DIIPv4.86",
31
- "@sphereon/oid4vc-common": "0.19.1-feature.DIIPv4.86",
29
+ "@sphereon/did-auth-siop": "0.19.1-next.220",
30
+ "@sphereon/did-auth-siop-adapter": "0.19.1-next.220",
31
+ "@sphereon/oid4vc-common": "0.19.1-next.220",
32
32
  "@sphereon/pex": "5.0.0-unstable.28",
33
33
  "@sphereon/pex-models": "^2.3.2",
34
- "@sphereon/ssi-sdk-ext.did-utils": "0.34.1-fix.80+f71b3901",
35
- "@sphereon/ssi-sdk-ext.identifier-resolution": "0.34.1-fix.80+f71b3901",
36
- "@sphereon/ssi-sdk-ext.jwt-service": "0.34.1-fix.80+f71b3901",
37
- "@sphereon/ssi-sdk.contact-manager": "0.34.1-fix.80+f71b3901",
38
- "@sphereon/ssi-sdk.core": "0.34.1-fix.80+f71b3901",
39
- "@sphereon/ssi-sdk.credential-store": "0.34.1-fix.80+f71b3901",
40
- "@sphereon/ssi-sdk.credential-validation": "0.34.1-fix.80+f71b3901",
41
- "@sphereon/ssi-sdk.data-store": "0.34.1-fix.80+f71b3901",
42
- "@sphereon/ssi-sdk.issuance-branding": "0.34.1-fix.80+f71b3901",
43
- "@sphereon/ssi-sdk.pd-manager": "0.34.1-fix.80+f71b3901",
44
- "@sphereon/ssi-sdk.presentation-exchange": "0.34.1-fix.80+f71b3901",
45
- "@sphereon/ssi-sdk.sd-jwt": "0.34.1-fix.80+f71b3901",
46
- "@sphereon/ssi-sdk.siopv2-oid4vp-common": "0.34.1-fix.80+f71b3901",
47
- "@sphereon/ssi-sdk.xstate-machine-persistence": "0.34.1-fix.80+f71b3901",
48
- "@sphereon/ssi-types": "0.34.1-fix.80+f71b3901",
34
+ "@sphereon/ssi-sdk-ext.did-utils": "0.34.1-next.278+0eacb25b",
35
+ "@sphereon/ssi-sdk-ext.identifier-resolution": "0.34.1-next.278+0eacb25b",
36
+ "@sphereon/ssi-sdk-ext.jwt-service": "0.34.1-next.278+0eacb25b",
37
+ "@sphereon/ssi-sdk.contact-manager": "0.34.1-next.278+0eacb25b",
38
+ "@sphereon/ssi-sdk.core": "0.34.1-next.278+0eacb25b",
39
+ "@sphereon/ssi-sdk.credential-store": "0.34.1-next.278+0eacb25b",
40
+ "@sphereon/ssi-sdk.credential-validation": "0.34.1-next.278+0eacb25b",
41
+ "@sphereon/ssi-sdk.data-store-types": "0.34.1-next.278+0eacb25b",
42
+ "@sphereon/ssi-sdk.issuance-branding": "0.34.1-next.278+0eacb25b",
43
+ "@sphereon/ssi-sdk.pd-manager": "0.34.1-next.278+0eacb25b",
44
+ "@sphereon/ssi-sdk.presentation-exchange": "0.34.1-next.278+0eacb25b",
45
+ "@sphereon/ssi-sdk.sd-jwt": "0.34.1-next.278+0eacb25b",
46
+ "@sphereon/ssi-sdk.siopv2-oid4vp-common": "0.34.1-next.278+0eacb25b",
47
+ "@sphereon/ssi-sdk.xstate-machine-persistence": "0.34.1-next.278+0eacb25b",
48
+ "@sphereon/ssi-types": "0.34.1-next.278+0eacb25b",
49
49
  "@sphereon/wellknown-dids-client": "^0.1.3",
50
50
  "@veramo/core": "4.2.0",
51
51
  "@veramo/credential-w3c": "4.2.0",
@@ -59,8 +59,8 @@
59
59
  },
60
60
  "devDependencies": {
61
61
  "@sphereon/did-uni-client": "^0.6.3",
62
- "@sphereon/ssi-sdk-ext.did-resolver-jwk": "0.34.1-fix.80+f71b3901",
63
- "@sphereon/ssi-sdk.agent-config": "0.34.1-fix.80+f71b3901",
62
+ "@sphereon/ssi-sdk-ext.did-resolver-jwk": "0.34.1-next.278+0eacb25b",
63
+ "@sphereon/ssi-sdk.agent-config": "0.34.1-next.278+0eacb25b",
64
64
  "@types/i18n-js": "^3.8.9",
65
65
  "@types/lodash.memoize": "^4.1.9",
66
66
  "@types/sha.js": "^2.4.4",
@@ -102,5 +102,5 @@
102
102
  "OpenID Connect",
103
103
  "Authenticator"
104
104
  ],
105
- "gitHead": "f71b39017a0bd9ac33fab56d2d61287d8d5c14f4"
105
+ "gitHead": "0eacb25b6e38cef88d04d696d84e3c2ebcf89ede"
106
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,27 @@
1
- import {
2
- AuthorizationRequest,
3
- Json,
4
- SupportedVersion
5
- } from '@sphereon/did-auth-siop'
1
+ import { AuthorizationRequest } from '@sphereon/did-auth-siop'
2
+ import type { PartialSdJwtDecodedVerifiableCredential, PartialSdJwtKbJwt } from '@sphereon/pex/dist/main/lib/index.js'
3
+ import { calculateSdHash } from '@sphereon/pex/dist/main/lib/utils/index.js'
4
+ import { getOrCreatePrimaryIdentifier, SupportedDidMethodEnum } from '@sphereon/ssi-sdk-ext.did-utils'
6
5
  import { isOID4VCIssuerIdentifier, ManagedIdentifierOptsOrResult } from '@sphereon/ssi-sdk-ext.identifier-resolution'
6
+ import { encodeJoseBlob } from '@sphereon/ssi-sdk.core'
7
7
  import { UniqueDigitalCredential, verifiableCredentialForRoleFilter } from '@sphereon/ssi-sdk.credential-store'
8
- import { ConnectionType, CredentialRole } from '@sphereon/ssi-sdk.data-store'
8
+ import { ConnectionType } from '@sphereon/ssi-sdk.data-store-types'
9
+ import { defaultGenerateDigest } from '@sphereon/ssi-sdk.sd-jwt'
9
10
  import {
10
11
  CredentialMapper,
12
+ CredentialRole,
11
13
  HasherSync,
12
14
  Loggers,
13
15
  OriginalVerifiableCredential,
14
- SdJwtDecodedVerifiableCredential
16
+ SdJwtDecodedVerifiableCredential,
15
17
  } 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
- import { encodeJoseBlob } from '@sphereon/ssi-sdk.core'
18
+ import { IAgentContext, IDIDManager } from '@veramo/core'
25
19
  import { DcqlPresentation, DcqlQuery } from 'dcql'
20
+ import { OpSession } from '../session'
21
+ import { LOGGER_NAMESPACE, RequiredContext, SelectableCredential, SelectableCredentialsMap, Siopv2HolderEvent } from '../types'
26
22
  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'
23
+
24
+ const CLOCK_SKEW = 120
32
25
 
33
26
  export const logger = Loggers.DEFAULT.get(LOGGER_NAMESPACE)
34
27
 
@@ -76,68 +69,62 @@ export const siopSendAuthorizationResponse = async (
76
69
  logger.debug(`AUD: ${aud}`)
77
70
  logger.debug(JSON.stringify(request.authorizationRequest))
78
71
 
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
- }
72
+ const domain = ((await request.authorizationRequest.getMergedProperty('client_id')) as string) ?? request.issuer ?? 'https://self-issued.me/v2'
73
+
74
+ logger.debug(`NONCE: ${session.nonce}, domain: ${domain}`)
75
+
76
+ const firstUniqueDC = credentials[0]
77
+ if (typeof firstUniqueDC !== 'object' || !('digitalCredential' in firstUniqueDC)) {
78
+ return Promise.reject(Error('SiopMachine only supports UniqueDigitalCredentials for now'))
79
+ }
80
+
81
+ let identifier: ManagedIdentifierOptsOrResult
82
+ const digitalCredential = firstUniqueDC.digitalCredential
83
+ const firstVC = firstUniqueDC.uniformVerifiableCredential
84
+ const holder = CredentialMapper.isSdJwtDecodedCredential(firstVC)
85
+ ? firstVC.decodedPayload.cnf?.jwk
86
+ ? //TODO SDK-19: convert the JWK to hex and search for the appropriate key and associated DID
87
+ //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
88
+ `did:jwk:${encodeJoseBlob(firstVC.decodedPayload.cnf?.jwk)}#0`
89
+ : firstVC.decodedPayload.sub
90
+ : Array.isArray(firstVC.credentialSubject)
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
91
95
 
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,
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
104
+ }
105
+ } else if (isOID4VCIssuerIdentifier(digitalCredential.kmsKeyRef)) {
106
+ identifier = await session.context.agent.identifierManagedGetByOID4VCIssuer({
107
+ identifier: firstUniqueDC.digitalCredential.kmsKeyRef,
108
+ })
109
+ } else {
110
+ switch (digitalCredential.subjectCorrelationType) {
111
+ case 'DID':
112
+ identifier = await session.context.agent.identifierManagedGetByDid({
113
+ identifier: digitalCredential.subjectCorrelationId ?? holder,
114
+ kmsKeyRef: digitalCredential.kmsKeyRef,
119
115
  })
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
- }
116
+ break
117
+ // TODO other implementations?
118
+ default:
119
+ // Since we are using the kmsKeyRef we will find the KID regardless of the identifier. We set it for later access though
120
+ identifier = await session.context.agent.identifierManagedGetByKid({
121
+ identifier: digitalCredential.subjectCorrelationId ?? holder ?? digitalCredential.kmsKeyRef,
122
+ kmsKeyRef: digitalCredential.kmsKeyRef,
123
+ })
124
+ }
125
+ }
137
126
 
138
- const dcqlCredentialsWithCredentials = new Map(
139
- credentials.map((vc) => [convertToDcqlCredentials(vc), vc])
140
- )
127
+ const dcqlCredentialsWithCredentials = new Map(credentials.map((vc) => [convertToDcqlCredentials(vc), vc]))
141
128
 
142
129
  const queryResult = DcqlQuery.query(request.dcqlQuery, Array.from(dcqlCredentialsWithCredentials.keys()))
143
130
 
@@ -149,7 +136,7 @@ export const siopSendAuthorizationResponse = async (
149
136
  const uniqueCredentials = Array.from(dcqlCredentialsWithCredentials.values())
150
137
  for (const [key, value] of Object.entries(queryResult.credential_matches)) {
151
138
  if (value.success) {
152
- const matchedCredentials = value.valid_credentials.map(cred => uniqueCredentials[cred.input_credential_index])
139
+ const matchedCredentials = value.valid_credentials.map((cred) => uniqueCredentials[cred.input_credential_index])
153
140
  const vc = matchedCredentials[0] // taking the first match for now //uniqueCredentials[value.input_credential_index]
154
141
  if (!vc) {
155
142
  continue
@@ -158,23 +145,41 @@ export const siopSendAuthorizationResponse = async (
158
145
  if (!originalVc) {
159
146
  continue
160
147
  }
148
+ // FIXME SSISDK-44
149
+ const decodedSdJwt = await CredentialMapper.decodeSdJwtVcAsync(originalVc as string, defaultGenerateDigest)
150
+ const updatedSdJwt = updateSdJwtCredential(decodedSdJwt, request.requestObject?.getPayload()?.nonce, domain)
151
+
152
+ const presentationResult = await context.agent.createSdJwtPresentation({
153
+ presentation: updatedSdJwt.compactSdJwtVc,
154
+ kb: {
155
+ payload: {
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
+ },
164
+ })
165
+
161
166
  if (originalVc) {
162
- presentation[key] = originalVc as | string | { [x: string]: Json }
167
+ presentation[key] = presentationResult.presentation
163
168
  }
164
169
  }
165
170
  }
166
171
 
167
172
  const dcqlPresentation = DcqlPresentation.parse(presentation)
168
173
 
169
- const response = session.sendAuthorizationResponse({
170
- responseSignerOpts: identifier,
171
- dcqlResponse: {
172
- dcqlPresentation
173
- }
174
- })
174
+ const response = session.sendAuthorizationResponse({
175
+ responseSignerOpts: identifier,
176
+ dcqlResponse: {
177
+ dcqlPresentation,
178
+ },
179
+ })
175
180
 
176
- logger.debug(`Response: `, response)
177
- return response
181
+ logger.debug(`Response: `, response)
182
+ return response
178
183
  }
179
184
 
180
185
  const retrieveEncodedCredential = (credential: UniqueDigitalCredential): OriginalVerifiableCredential | undefined => {
@@ -186,19 +191,14 @@ const retrieveEncodedCredential = (credential: UniqueDigitalCredential): Origina
186
191
  : credential.originalVerifiableCredential
187
192
  }
188
193
 
189
- export const getSelectableCredentials = async (
190
- dcqlQuery: DcqlQuery,
191
- context: RequiredContext,
192
- ): Promise<SelectableCredentialsMap> => {
194
+ export const getSelectableCredentials = async (dcqlQuery: DcqlQuery, context: RequiredContext): Promise<SelectableCredentialsMap> => {
193
195
  const agentContext = { ...context, agent: context.agent }
194
196
  const { agent } = agentContext
195
197
  const uniqueVerifiableCredentials = await agent.crsGetUniqueCredentials({
196
198
  filter: verifiableCredentialForRoleFilter(CredentialRole.HOLDER),
197
199
  })
198
200
  const branding = await agent.ibGetCredentialBranding()
199
- const dcqlCredentialsWithCredentials = new Map(
200
- uniqueVerifiableCredentials.map((vc) => [convertToDcqlCredentials(vc), vc])
201
- )
201
+ const dcqlCredentialsWithCredentials = new Map(uniqueVerifiableCredentials.map((vc) => [convertToDcqlCredentials(vc), vc]))
202
202
  const queryResult = DcqlQuery.query(dcqlQuery, Array.from(dcqlCredentialsWithCredentials.keys()))
203
203
  const uniqueCredentials = Array.from(dcqlCredentialsWithCredentials.values())
204
204
  const selectableCredentialsMap: SelectableCredentialsMap = new Map()
@@ -208,7 +208,7 @@ export const getSelectableCredentials = async (
208
208
  continue
209
209
  }
210
210
 
211
- const mapSelectableCredentialPromises = value.valid_credentials.map(async cred => {
211
+ const mapSelectableCredentialPromises = value.valid_credentials.map(async (cred) => {
212
212
  const matchedCredential = uniqueCredentials[cred.input_credential_index]
213
213
  const credentialBranding = branding.filter((cb) => cb.vcHash === matchedCredential.hash)
214
214
  const issuerPartyIdentity = await agent.cmGetContacts({
@@ -246,3 +246,33 @@ export const translateCorrelationIdToName = async (correlationId: string, contex
246
246
 
247
247
  return contacts[0].contact.displayName
248
248
  }
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
+ }