Skip to content

Commit a539d92

Browse files
Merge pull request #196 from Sphereon-Opensource/feature/SSISDK-41_credential_sets
feature/SSISDK-41_credential_sets
2 parents d7b55db + 74b465c commit a539d92

4 files changed

Lines changed: 78 additions & 4 deletions

File tree

packages/siop-oid4vp/lib/__tests__/IT.spec.ts

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1755,3 +1755,75 @@ describe.skip('RP and OP interaction should', () => {
17551755
expect(resState?.status).toBe('error')
17561756
})
17571757
})
1758+
1759+
1760+
describe('credential_sets tests', () => {
1761+
it('DCQL credential_sets: happy flow (single required option is satisfied)', () => {
1762+
const queryWithSet: DcqlQuery.Input = {
1763+
credentials: [
1764+
{
1765+
id: 'credA',
1766+
format: 'ldp_vc',
1767+
meta: {
1768+
type_values: [
1769+
['https://www.w3.org/2018/credentials#VerifiableCredential'],
1770+
['PermanentResidentCard']
1771+
]
1772+
},
1773+
claims: [{ path: ['givenName'], values: ['JANE'] }]
1774+
}
1775+
],
1776+
credential_sets: [
1777+
{
1778+
options: [['credA']],
1779+
required: true,
1780+
purpose: 'must include credA'
1781+
}
1782+
]
1783+
}
1784+
1785+
const parsed = DcqlQuery.parse(queryWithSet)
1786+
DcqlQuery.validate(parsed) // validates structure + credential_sets references
1787+
1788+
const dcqlCredential: DcqlW3cVcCredential = {
1789+
credential_format: 'ldp_vc',
1790+
claims: (getVCs()[0].credentialSubject as { [x: string]: Json }),
1791+
type: getVCs()[0].type,
1792+
cryptographic_holder_binding: true
1793+
}
1794+
1795+
const result: DcqlQueryResult = DcqlQuery.query(parsed, [dcqlCredential])
1796+
1797+
// set is satisfied and matching_options should include ['credA']
1798+
expect(result.can_be_satisfied).toBe(true)
1799+
expect(result.credential_sets?.[0].matching_options).toEqual([['credA']])
1800+
})
1801+
1802+
it('DCQL credential_sets: invalid rule (references unknown credential id) fails validation', () => {
1803+
const queryWithBadSet: DcqlQuery.Input = {
1804+
credentials: [
1805+
{
1806+
id: 'credA',
1807+
format: 'ldp_vc',
1808+
meta: {
1809+
type_values: [
1810+
['https://www.w3.org/2018/credentials#VerifiableCredential'],
1811+
['PermanentResidentCard']
1812+
]
1813+
},
1814+
claims: [{ path: ['givenName'], values: ['JANE'] }]
1815+
}
1816+
],
1817+
credential_sets: [
1818+
{
1819+
// This option references a non-existent credential query id
1820+
options: [['does_not_exist']],
1821+
required: true
1822+
}
1823+
]
1824+
}
1825+
1826+
const parsed = DcqlQuery.parse(queryWithBadSet)
1827+
expect(() => DcqlQuery.validate(parsed)).toThrowError(/Credential set contains undefined credential id/i)
1828+
})
1829+
})

packages/siop-oid4vp/lib/authorization-response/Dcql.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ export class Dcql {
5757
opts: {
5858
hasher?: HasherSync
5959
},
60-
) => {
60+
) : DcqlPresentationResult.Output => {
6161
const dcqlPresentation = Object.fromEntries(
6262
// FIXME SSISDK-41
6363
Object.entries(extractDcqlPresentationFromDcqlVpToken(record, opts)).map(([queryId, p]) => {

packages/siop-oid4vp/lib/authorization-response/OpenID4VP.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ export const verifyPresentations = async (
5050
verifyOpts: VerifyAuthorizationResponseOpts,
5151
): Promise<{ dcql: VerifiedOpenID4VPSubmission }> => {
5252
const dcqlQuery = DcqlQuery.parse(verifyOpts.dcqlQuery ?? authorizationResponse?.authorizationRequest.payload.dcql_query as DcqlQuery)
53+
DcqlQuery.validate(dcqlQuery)
5354
const dcqlPresentation = extractDcqlPresentationFromDcqlVpToken(authorizationResponse.payload.vp_token as string, { hasher: verifyOpts.hasher })
5455

5556
const wrappedPresentations = Object.values(dcqlPresentation)
@@ -59,7 +60,7 @@ export const verifyPresentations = async (
5960
),
6061
)
6162

62-
await Dcql.assertValidDcqlPresentationResult(authorizationResponse.payload.vp_token as string, dcqlQuery, { hasher: verifyOpts.hasher })
63+
const dcqlPresentationResult = await Dcql.assertValidDcqlPresentationResult(authorizationResponse.payload.vp_token as string, dcqlQuery, { hasher: verifyOpts.hasher })
6364

6465
if (verifiedPresentations.some((verified) => !verified)) {
6566
const message = verifiedPresentations
@@ -95,7 +96,7 @@ export const verifyPresentations = async (
9596
}
9697
}
9798

98-
return { dcql: { nonce, presentation: dcqlPresentation, dcqlQuery } }
99+
return { dcql: { nonce, presentation: dcqlPresentation, dcqlQuery , dcqlPresentationResult} }
99100
}
100101

101102
export const extractDcqlPresentationFromDcqlVpToken = (

packages/siop-oid4vp/lib/types/SIOP.types.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import {
99
W3CVerifiablePresentation,
1010
WrappedVerifiablePresentation
1111
} from '@sphereon/ssi-types'
12-
import { DcqlQuery } from 'dcql'
12+
import { DcqlPresentationResult, DcqlQuery } from 'dcql'
1313
import { z } from 'zod'
1414
import {
1515
AuthorizationRequest,
@@ -483,6 +483,7 @@ export interface VerifiedIDToken {
483483
export interface VerifiedOpenID4VPSubmission {
484484
dcqlQuery: DcqlQuery
485485
presentation: PresentationSubmission
486+
dcqlPresentationResult?: DcqlPresentationResult
486487
nonce?: string
487488
}
488489

0 commit comments

Comments
 (0)