Skip to content

Commit 6541485

Browse files
committed
fix: update dcql apply disclosures for payload
1 parent 9f7bc3b commit 6541485

File tree

8 files changed

+58
-204
lines changed

8 files changed

+58
-204
lines changed

packages/core/package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626
},
2727
"dependencies": {
2828
"@digitalcredentials/jsonld": "^6.0.0",
29-
"dcql": "^0.2.13",
29+
"dcql": "^0.2.16",
3030
"@digitalcredentials/jsonld-signatures": "^9.4.0",
3131
"@digitalcredentials/vc": "^6.0.1",
3232
"@multiformats/base-x": "^4.0.1",

packages/core/src/modules/dcql/DcqlService.ts

+17-21
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,9 @@ import type { AgentContext } from '../../agent'
33
import { DcqlCredential, DcqlMdocCredential, DcqlQuery, DcqlSdJwtVcCredential } from 'dcql'
44
import { injectable } from 'tsyringe'
55

6-
import { JsonValue } from '../../types'
76
import { Mdoc, MdocApi, MdocDeviceResponse, MdocOpenId4VpSessionTranscriptOptions, MdocRecord } from '../mdoc'
8-
import { IPresentationFrame, SdJwtVcApi, SdJwtVcRecord } from '../sd-jwt-vc'
7+
import { SdJwtVcApi, SdJwtVcRecord, SdJwtVcService } from '../sd-jwt-vc'
8+
import { buildDisclosureFrameForPayload } from '../sd-jwt-vc/disclosureFrame'
99
import { ClaimFormat, W3cCredentialRecord, W3cCredentialRepository } from '../vc'
1010

1111
import { DcqlError } from './DcqlError'
@@ -100,13 +100,13 @@ export class DcqlService {
100100
const dcqlCredentials: DcqlCredential[] = credentialRecords.map((record) => {
101101
if (record.type === 'MdocRecord') {
102102
return {
103-
credentialFormat: 'mso_mdoc',
103+
credential_format: 'mso_mdoc',
104104
doctype: record.getTags().docType,
105105
namespaces: Mdoc.fromBase64Url(record.base64Url).issuerSignedNamespaces,
106106
} satisfies DcqlMdocCredential
107107
} else if (record.type === 'SdJwtVcRecord') {
108108
return {
109-
credentialFormat: 'vc+sd-jwt',
109+
credential_format: 'vc+sd-jwt',
110110
vct: record.getTags().vct,
111111
claims: this.getSdJwtVcApi(agentContext).fromCompact(record.compactSdJwtVc)
112112
.prettyClaims as DcqlSdJwtVcCredential.Claims,
@@ -120,7 +120,18 @@ export class DcqlService {
120120
const queryResult = DcqlQuery.query(DcqlQuery.parse(dcqlQuery), dcqlCredentials)
121121
const matchesWithRecord = Object.fromEntries(
122122
Object.entries(queryResult.credential_matches).map(([credential_query_id, result]) => {
123-
return [credential_query_id, { ...result, record: credentialRecords[result.credential_index] }]
123+
if (result.success) {
124+
if (result.output.credential_format === 'vc+sd-jwt') {
125+
const sdJwtVcRecord = credentialRecords[result.input_credential_index] as SdJwtVcRecord
126+
agentContext.dependencyManager
127+
.resolve(SdJwtVcService)
128+
.applyDisclosuresForPayload(sdJwtVcRecord.compactSdJwtVc, result.output.claims)
129+
}
130+
131+
return [credential_query_id, { ...result, record: credentialRecords[result.input_credential_index] }]
132+
} else {
133+
return [credential_query_id, result]
134+
}
124135
})
125136
)
126137

@@ -207,21 +218,6 @@ export class DcqlService {
207218
return DcqlQuery.parse(dcqlQuery)
208219
}
209220

210-
// TODO: this IS WRONG
211-
private createPresentationFrame(obj: Record<string, JsonValue>): IPresentationFrame {
212-
const frame: IPresentationFrame = {}
213-
214-
for (const [key, value] of Object.entries(obj)) {
215-
if (typeof value === 'object' && value !== null) {
216-
frame[key] = true
217-
} else {
218-
frame[key] = !!value
219-
}
220-
}
221-
222-
return frame
223-
}
224-
225221
public async createPresentation(
226222
agentContext: AgentContext,
227223
options: {
@@ -268,7 +264,7 @@ export class DcqlService {
268264

269265
dcqlPresentation[credentialQueryId] = MdocDeviceResponse.fromBase64Url(deviceResponseBase64Url)
270266
} else if (presentationToCreate.claimFormat === ClaimFormat.SdJwtVc) {
271-
const presentationFrame = this.createPresentationFrame(presentationToCreate.disclosedPayload)
267+
const presentationFrame = buildDisclosureFrameForPayload(presentationToCreate.disclosedPayload)
272268

273269
if (!domain) {
274270
throw new DcqlError('Missing domain property for creating SdJwtVc presentation.')

packages/core/src/modules/dcql/models/index.ts

+8-3
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,14 @@ export type DcqlMdocCredential = _DcqlMdocCredential.Model['Input']
2020
export type DcqlSdJwtVcCredential = _DcqlSdJwtVcCredential.Model['Input']
2121
export type DcqlW3cVcCredential = _DcqlW3cVcCredential.Model['Input']
2222

23-
export type DcqlMatchWithRecord = {
24-
record: W3cCredentialRecord | SdJwtVcRecord | MdocRecord
25-
} & _DcqlQueryResult['credential_matches'][number]
23+
export type DcqlMatchWithRecord =
24+
| (_DcqlQueryResult['credential_matches'][number] & {
25+
success: true
26+
record: MdocRecord | SdJwtVcRecord | W3cCredentialRecord
27+
})
28+
| (_DcqlQueryResult['credential_matches'][number] & {
29+
success: false
30+
})
2631

2732
export type DcqlQueryResult = Omit<_DcqlQueryResult.Input, 'credential_matches'> & {
2833
credential_matches: Record<string, DcqlMatchWithRecord>

packages/core/src/modules/sd-jwt-vc/SdJwtVcService.ts

+4-3
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import { AgentContext } from '../../agent'
2222
import { JwtPayload, Jwk, getJwkFromJson, getJwkFromKey, Hasher } from '../../crypto'
2323
import { CredoError } from '../../error'
2424
import { X509Service } from '../../modules/x509/X509Service'
25+
import { JsonObject } from '../../types'
2526
import { TypedArrayEncoder, nowInSeconds } from '../../utils'
2627
import { getDomainFromUrl } from '../../utils/domain'
2728
import { fetchWithTimeout } from '../../utils/fetch'
@@ -31,7 +32,7 @@ import { X509Certificate, X509ModuleConfig } from '../x509'
3132

3233
import { SdJwtVcError } from './SdJwtVcError'
3334
import { decodeSdJwtVc, sdJwtVcHasher } from './decodeSdJwtVc'
34-
import { buildDisclosureFrameFromPayload } from './disclosureFrame'
35+
import { buildDisclosureFrameForPayload } from './disclosureFrame'
3536
import { SdJwtVcRecord, SdJwtVcRepository } from './repository'
3637
import { SdJwtVcTypeMetadata } from './typeMetadata'
3738

@@ -158,9 +159,9 @@ export class SdJwtVcService {
158159
return decodeSdJwtVc(compactSdJwtVc, typeMetadata)
159160
}
160161

161-
public applyDisclosuresForPayload(compactSdJwtVc: string, requestedPayload: Record<string, unknown>): SdJwtVc {
162+
public applyDisclosuresForPayload(compactSdJwtVc: string, requestedPayload: JsonObject): SdJwtVc {
162163
const decoded = decodeSdJwtSync(compactSdJwtVc, Hasher.hash)
163-
const presentationFrame = buildDisclosureFrameFromPayload(requestedPayload) ?? {}
164+
const presentationFrame = buildDisclosureFrameForPayload(requestedPayload) ?? {}
164165

165166
if (decoded.kbJwt) {
166167
throw new SdJwtVcError('Cannot apply limit disclosure on an sd-jwt with key binding jwt')
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,23 @@
1+
import type { JsonObject } from '../../types'
2+
3+
import { isObject } from 'class-validator'
4+
15
type DisclosureFrame = {
26
[key: string]: boolean | DisclosureFrame
37
}
48

5-
export function buildDisclosureFrameFromPayload(input: Record<string, unknown>): DisclosureFrame | null {
6-
// Handle objects recursively
7-
const result: DisclosureFrame = {}
8-
9-
// Base case: input is null or undefined
10-
if (input === null || input === undefined) {
11-
return result
12-
}
13-
14-
for (const [key, value] of Object.entries(input)) {
15-
// Ignore non-value values
16-
if (value === null || value === undefined) continue
17-
18-
if (typeof value === 'object') {
9+
export function buildDisclosureFrameForPayload(input: JsonObject): DisclosureFrame {
10+
return Object.fromEntries(
11+
Object.entries(input).map(([key, value]) => {
12+
// TODO: Array disclosure frames are not yet supported - treating entire array as disclosed
1913
if (Array.isArray(value)) {
20-
// TODO: Array disclosure frames are not yet supported - treating entire array as disclosed
21-
result[key] = true
14+
return [key, true]
15+
} else if (isObject(value)) {
16+
if (Object.keys.length === 0) return [key, false]
17+
return [key, buildDisclosureFrameForPayload(value)]
2218
} else {
23-
result[key] = buildDisclosureFrameFromPayload(value as Record<string, unknown>) ?? false
19+
return [key, true]
2420
}
25-
} else {
26-
// Handle primitive values
27-
result[key] = true
28-
}
29-
}
30-
31-
return Object.keys(result).length > 0 ? result : null
21+
})
22+
)
3223
}

packages/openid4vc/package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727
},
2828
"dependencies": {
2929
"@credo-ts/core": "workspace:*",
30-
"@sphereon/did-auth-siop": "https://gitpkg.vercel.app/animo/OID4VC/packages/siop-oid4vp?funke",
30+
"@sphereon/did-auth-siop": "link:/../../../CODE/OID4VC/packages/siop-oid4vp",
3131
"@sphereon/oid4vc-common": "0.16.1-fix.173",
3232
"@sphereon/ssi-types": "0.30.2-next.135",
3333
"class-transformer": "^0.5.1",

packages/openid4vc/tests/openid4vc.e2e.test.ts

+4-4
Original file line numberDiff line numberDiff line change
@@ -2339,15 +2339,15 @@ describe('OpenId4Vc', () => {
23392339
success: true,
23402340
output: {
23412341
doctype: 'org.eu.university',
2342-
credentialFormat: 'mso_mdoc',
2342+
credential_format: 'mso_mdoc',
23432343
namespaces: {
23442344
'eu.europa.ec.eudi.pid.1': {
23452345
name: 'John Doe',
23462346
degree: 'bachelor',
23472347
},
23482348
},
23492349
},
2350-
credential_index: 0,
2350+
input_credential_index: 0,
23512351
claim_set_index: undefined,
23522352
all: expect.any(Array),
23532353
record: expect.any(MdocRecord),
@@ -2356,13 +2356,13 @@ describe('OpenId4Vc', () => {
23562356
typed: true,
23572357
success: true,
23582358
output: {
2359-
credentialFormat: 'vc+sd-jwt',
2359+
credential_format: 'vc+sd-jwt',
23602360
vct: 'OpenBadgeCredential',
23612361
claims: {
23622362
university: 'innsbruck',
23632363
},
23642364
},
2365-
credential_index: 1,
2365+
input_credential_index: 1,
23662366
claim_set_index: undefined,
23672367
all: expect.any(Array),
23682368
record: expect.any(SdJwtVcRecord),

0 commit comments

Comments
 (0)