diff --git a/.github/workflows/build-test-on-pr.yml b/.github/workflows/build-test-on-pr.yml index fde1e54f..967279a8 100644 --- a/.github/workflows/build-test-on-pr.yml +++ b/.github/workflows/build-test-on-pr.yml @@ -25,7 +25,7 @@ jobs: node-version: '20.x' - uses: pnpm/action-setup@v4 with: - version: 9 + version: 9.15.3 # TODO remove later, we are temporary dealing with a broken pnpm version in the CI container - run: pnpm install - run: pnpm build - name: run CI tests diff --git a/.github/workflows/build-test-publish-on-push.yml b/.github/workflows/build-test-publish-on-push.yml index 450a530a..216bcc79 100644 --- a/.github/workflows/build-test-publish-on-push.yml +++ b/.github/workflows/build-test-publish-on-push.yml @@ -39,7 +39,7 @@ jobs: node-version: '20.x' - uses: pnpm/action-setup@v4 with: - version: 9 + version: 9.15.3 # TODO remove later, we are temporary dealing with a broken pnpm version in the CI container - run: pnpm install - run: pnpm build - name: run integration tests diff --git a/package.json b/package.json index f30e1f64..a13ec126 100644 --- a/package.json +++ b/package.json @@ -23,7 +23,8 @@ "node": ">=18" }, "resolutions": { - "@sphereon/ssi-types": "0.30.2-feature.mdoc.funke2.367", + "@sphereon/ssi-types": "0.32.1-feature.VDX.341.53", + "dcql": "0.2.19", "node-fetch": "2.6.12" }, "prettier": { @@ -66,4 +67,3 @@ "OID4VP" ] } - diff --git a/packages/callback-example/package.json b/packages/callback-example/package.json index 515af167..b157f874 100644 --- a/packages/callback-example/package.json +++ b/packages/callback-example/package.json @@ -19,7 +19,7 @@ "@sphereon/oid4vci-client": "workspace:*", "@sphereon/oid4vci-common": "workspace:*", "@sphereon/oid4vci-issuer": "workspace:*", - "@sphereon/ssi-types": "0.30.2-feature.mdoc.funke2.367", + "@sphereon/ssi-types": "0.32.1-feature.VDX.341.53", "jose": "^4.10.0" }, "devDependencies": { diff --git a/packages/client/lib/OpenID4VCIClient.ts b/packages/client/lib/OpenID4VCIClient.ts index 47231fae..caf9fa37 100644 --- a/packages/client/lib/OpenID4VCIClient.ts +++ b/packages/client/lib/OpenID4VCIClient.ts @@ -136,6 +136,7 @@ export class OpenID4VCIClient { pkce, authorizationRequest, createAuthorizationRequestURL, + endpointMetadata }: { credentialIssuer: string; kid?: string; @@ -145,6 +146,7 @@ export class OpenID4VCIClient { createAuthorizationRequestURL?: boolean; authorizationRequest?: AuthorizationRequestOpts; // Can be provided here, or when manually calling createAuthorizationUrl pkce?: PKCEOpts; + endpointMetadata?: EndpointMetadataResult }) { const client = new OpenID4VCIClient({ kid, @@ -153,6 +155,7 @@ export class OpenID4VCIClient { credentialIssuer, pkce, authorizationRequest, + endpointMetadata }); if (retrieveServerMetadata === undefined || retrieveServerMetadata) { await client.retrieveServerMetadata(); @@ -179,6 +182,7 @@ export class OpenID4VCIClient { createAuthorizationRequestURL, authorizationRequest, resolveOfferUri, + endpointMetadata }: { uri: string; kid?: string; @@ -189,6 +193,7 @@ export class OpenID4VCIClient { pkce?: PKCEOpts; clientId?: string; authorizationRequest?: AuthorizationRequestOpts; // Can be provided here, or when manually calling createAuthorizationUrl + endpointMetadata?: EndpointMetadataResult }): Promise { const credentialOfferClient = await CredentialOfferClient.fromURI(uri, { resolve: resolveOfferUri }); const client = new OpenID4VCIClient({ @@ -198,6 +203,7 @@ export class OpenID4VCIClient { clientId: clientId ?? authorizationRequest?.clientId ?? credentialOfferClient.clientId, pkce, authorizationRequest, + endpointMetadata }); if (retrieveServerMetadata === undefined || retrieveServerMetadata) { diff --git a/packages/client/package.json b/packages/client/package.json index 42c78f5f..0389786f 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -17,7 +17,7 @@ "dependencies": { "@sphereon/oid4vc-common": "workspace:*", "@sphereon/oid4vci-common": "workspace:*", - "@sphereon/ssi-types": "0.30.2-feature.mdoc.funke2.367", + "@sphereon/ssi-types": "0.32.1-feature.VDX.341.53", "cross-fetch": "^3.1.8", "debug": "^4.3.5" }, diff --git a/packages/common/package.json b/packages/common/package.json index 2f134698..cf2238e9 100644 --- a/packages/common/package.json +++ b/packages/common/package.json @@ -10,7 +10,7 @@ "build:clean": "tsc --build --clean && tsc --build" }, "dependencies": { - "@sphereon/ssi-types": "0.30.2-feature.mdoc.funke2.367", + "@sphereon/ssi-types": "0.32.1-feature.VDX.341.53", "jwt-decode": "^4.0.0", "sha.js": "^2.4.11", "uint8arrays": "3.1.1", diff --git a/packages/issuer-rest/package.json b/packages/issuer-rest/package.json index 909e1f0e..8cd4d858 100644 --- a/packages/issuer-rest/package.json +++ b/packages/issuer-rest/package.json @@ -15,7 +15,7 @@ "@sphereon/oid4vci-common": "workspace:*", "@sphereon/oid4vci-issuer": "workspace:*", "@sphereon/ssi-express-support": "0.30.2-feature.mdoc.funke2.367", - "@sphereon/ssi-types": "0.30.2-feature.mdoc.funke2.367", + "@sphereon/ssi-types": "0.32.1-feature.VDX.341.53", "body-parser": "^1.20.2", "cookie-parser": "^1.4.6", "cors": "^2.8.5", diff --git a/packages/issuer/package.json b/packages/issuer/package.json index ea56dee2..e35aa0c2 100644 --- a/packages/issuer/package.json +++ b/packages/issuer/package.json @@ -12,7 +12,7 @@ "dependencies": { "@sphereon/oid4vc-common": "workspace:*", "@sphereon/oid4vci-common": "workspace:*", - "@sphereon/ssi-types": "0.30.2-feature.mdoc.funke2.367", + "@sphereon/ssi-types": "0.32.1-feature.VDX.341.53", "uuid": "^9.0.0" }, "peerDependencies": { diff --git a/packages/oid4vci-common/package.json b/packages/oid4vci-common/package.json index 285a6a31..5ae90beb 100644 --- a/packages/oid4vci-common/package.json +++ b/packages/oid4vci-common/package.json @@ -11,7 +11,7 @@ }, "dependencies": { "@sphereon/oid4vc-common": "workspace:*", - "@sphereon/ssi-types": "0.30.2-feature.mdoc.funke2.367", + "@sphereon/ssi-types": "0.32.1-feature.VDX.341.53", "cross-fetch": "^3.1.8", "debug": "^4.3.5", "jwt-decode": "^4.0.0", diff --git a/packages/siop-oid4vp/lib/__tests__/AuthenticationRequest.request.spec.ts b/packages/siop-oid4vp/lib/__tests__/AuthenticationRequest.request.spec.ts index 12e487b8..3b12ff26 100644 --- a/packages/siop-oid4vp/lib/__tests__/AuthenticationRequest.request.spec.ts +++ b/packages/siop-oid4vp/lib/__tests__/AuthenticationRequest.request.spec.ts @@ -26,6 +26,7 @@ import { VERIFIERZ_PURPOSE_TO_VERIFY, VERIFIERZ_PURPOSE_TO_VERIFY_NL, } from './data/mockedData' +import { DcqlQuery } from 'dcql' const EXAMPLE_REDIRECT_URL = 'https://acme.com/hello' const EXAMPLE_REFERENCE_URL = 'https://rp.acme.com/siop/jwts' @@ -671,4 +672,85 @@ describe('create Request JWT should', () => { } await expect(URI.fromOpts(opts)).rejects.toThrow(SIOPErrors.REQUEST_CLAIMS_PRESENTATION_DEFINITION_NOT_VALID) }) + + it('should succeed when requesting with a valid dcql query', async () => { + const dcqlQuery: DcqlQuery = { + credentials: [ + { + id: 'Credentials', + format: 'jwt_vc_json', + claims: [ + { + id: 'ID Card Credential', + path: ['$.issuer.id'], + values: ['did:example:issuer'], + }, + ], + }, + ], + } + const opts: CreateAuthorizationRequestOpts = { + version: SupportedVersion.SIOPv2_ID1, + payload: { + client_id: WELL_KNOWN_OPENID_FEDERATION, + scope: 'test', + response_type: 'vp_token', + request_object_signing_alg_values_supported: [SigningAlgo.ES256, SigningAlgo.EDDSA], + redirect_uri: EXAMPLE_REDIRECT_URL, + }, + requestObject: { + jwtIssuer: { method: 'did', didUrl: KID, alg: SigningAlgo.ES256K }, + passBy: PassBy.REFERENCE, + reference_uri: EXAMPLE_REFERENCE_URL, + + createJwtCallback: getCreateJwtCallback({ + hexPrivateKey: HEX_KEY, + did: DID, + kid: KID, + alg: SigningAlgo.ES256K, + }), + payload: { + client_id: WELL_KNOWN_OPENID_FEDERATION, + scope: 'test', + response_type: 'vp_token', + redirect_uri: EXAMPLE_REDIRECT_URL, + request_object_signing_alg_values_supported: [SigningAlgo.EDDSA, SigningAlgo.ES256], + claims: { + vp_token: { + dcql_query: JSON.stringify(dcqlQuery) + }, + }, + }, + }, + clientMetadata: { + client_id: WELL_KNOWN_OPENID_FEDERATION, + idTokenSigningAlgValuesSupported: [SigningAlgo.EDDSA, SigningAlgo.ES256], + requestObjectSigningAlgValuesSupported: [SigningAlgo.EDDSA, SigningAlgo.ES256], + responseTypesSupported: [ResponseType.ID_TOKEN], + scopesSupported: [Scope.OPENID_DIDAUTHN, Scope.OPENID], + subject_syntax_types_supported: ['did:ethr:', SubjectIdentifierType.DID], + subjectTypesSupported: [SubjectType.PAIRWISE], + vpFormatsSupported: { + ldp_vc: { + proof_type: [IProofType.EcdsaSecp256k1Signature2019, IProofType.EcdsaSecp256k1Signature2019], + }, + }, + + passBy: PassBy.VALUE, + + logo_uri: VERIFIER_LOGO_FOR_CLIENT, + clientName: VERIFIER_NAME_FOR_CLIENT, + 'clientName#nl-NL': VERIFIER_NAME_FOR_CLIENT_NL + '2022100305', + clientPurpose: VERIFIERZ_PURPOSE_TO_VERIFY, + 'clientPurpose#nl-NL': VERIFIERZ_PURPOSE_TO_VERIFY_NL, + }, + } + + const uriRequest = await URI.fromOpts(opts) + + const uriDecoded = decodeURIComponent(uriRequest.encodedUri) + expect(uriDecoded.startsWith('openid4vp://?')).toBeTruthy() + expect(uriDecoded).toContain(`request_uri=https://rp.acme.com/siop/jwts`) + expect((await (await uriRequest.toAuthorizationRequest())?.requestObject?.getPayload())?.claims.vp_token).toBeDefined() + }) }) diff --git a/packages/siop-oid4vp/lib/__tests__/AuthenticationResponse.response.spec.ts b/packages/siop-oid4vp/lib/__tests__/AuthenticationResponse.response.spec.ts index 779480d3..381bf698 100644 --- a/packages/siop-oid4vp/lib/__tests__/AuthenticationResponse.response.spec.ts +++ b/packages/siop-oid4vp/lib/__tests__/AuthenticationResponse.response.spec.ts @@ -8,6 +8,7 @@ import { IVerifiablePresentation, OriginalVerifiableCredential, } from '@sphereon/ssi-types' +import { DcqlCredential, DcqlPresentation, DcqlQuery, DcqlQueryResult } from 'dcql' import { AuthorizationResponse, @@ -27,12 +28,12 @@ import { VerifyAuthorizationRequestOpts, VPTokenLocation, } from '..' -import { createPresentationSubmission } from '../authorization-response/OpenID4VP' +import { createPresentationSubmission } from '../authorization-response' import SIOPErrors from '../types/Errors' import { getCreateJwtCallback, getVerifyJwtCallback } from './DidJwtTestUtils' import { getResolver } from './ResolverTestUtils' -import { mockedGetEnterpriseAuthToken, WELL_KNOWN_OPENID_FEDERATION } from './TestUtils' +import { mockedGetEnterpriseAuthToken, pexHasher, WELL_KNOWN_OPENID_FEDERATION } from './TestUtils' import { UNIT_TEST_TIMEOUT, VERIFIER_LOGO_FOR_CLIENT, @@ -90,6 +91,7 @@ describe('create JWT from Request JWT should', () => { const resolver = getResolver('ethr') const verifyOpts: VerifyAuthorizationRequestOpts = { + hasher: pexHasher, verifyJwtCallback: getVerifyJwtCallback(resolver), verification: {}, supportedVersions: [SupportedVersion.SIOPv2_ID1], @@ -463,9 +465,6 @@ describe('create JWT from Request JWT should', () => { } const requestObject = await RequestObject.fromOpts(requestOpts) - /* console.log( - JSON.stringify(await AuthenticationResponse.createJWTFromRequestJWT(requestWithJWT.jwt, responseOpts, verifyOpts)) - );*/ const jwt = await requestObject.toJwt() if (!jwt) throw new Error('JWT is undefined') const authorizationRequest = await AuthorizationResponse.fromRequestObject(jwt, responseOpts, verifyOpts) @@ -650,4 +649,140 @@ describe('create JWT from Request JWT should', () => { const authResponse = AuthorizationResponse.fromRequestObject(jwt, responseOpts, verifyOpts) await expect(authResponse).toBeDefined() }) + + it('succeed when valid JWT with DCQL query is passed in', async () => { + expect.assertions(1) + + const mockReqEntity = await mockedGetEnterpriseAuthToken('REQ COMPANY') + const mockResEntity = await mockedGetEnterpriseAuthToken('RES COMPANY') + + const dcqlQuery: DcqlQuery = { + credentials: [ + { + id: 'Credentials', + format: 'vc+sd-jwt', + claims: [ + { + path: ['given_name'], + values: ['John'], + }, + ], + }, + ], + } + + const dcqlParsedQuery = DcqlQuery.parse(dcqlQuery) + DcqlQuery.validate(dcqlParsedQuery) + + const requestOpts: CreateAuthorizationRequestOpts = { + version: SupportedVersion.SIOPv2_D12_OID4VP_D20, + requestObject: { + passBy: PassBy.REFERENCE, + reference_uri: 'https://my-request.com/here', + jwtIssuer: { method: 'did', didUrl: mockReqEntity.did, alg: SigningAlgo.ES256K }, + createJwtCallback: getCreateJwtCallback({ + hexPrivateKey: mockReqEntity.hexPrivateKey, + did: mockReqEntity.did, + kid: `${mockReqEntity.did}#controller`, + alg: SigningAlgo.ES256K, + }), + payload: { + client_id: WELL_KNOWN_OPENID_FEDERATION, + scope: 'test', + response_type: 'id_token vp_token', + redirect_uri: EXAMPLE_REDIRECT_URL, + dcql_query: JSON.stringify(dcqlParsedQuery), + }, + }, + clientMetadata: { + client_id: WELL_KNOWN_OPENID_FEDERATION, + idTokenSigningAlgValuesSupported: [SigningAlgo.EDDSA, SigningAlgo.ES256], + subject_syntax_types_supported: ['did:ethr:', SubjectIdentifierType.DID], + requestObjectSigningAlgValuesSupported: [SigningAlgo.EDDSA, SigningAlgo.ES256], + responseTypesSupported: [ResponseType.ID_TOKEN], + scopesSupported: [Scope.OPENID_DIDAUTHN, Scope.OPENID], + subjectTypesSupported: [SubjectType.PAIRWISE], + vpFormatsSupported: { + ldp_vc: { + proof_type: [IProofType.EcdsaSecp256k1Signature2019, IProofType.EcdsaSecp256k1Signature2019], + }, + }, + passBy: PassBy.VALUE, + logo_uri: VERIFIER_LOGO_FOR_CLIENT, + clientName: VERIFIER_NAME_FOR_CLIENT, + 'clientName#nl-NL': VERIFIER_NAME_FOR_CLIENT_NL + '2022100315', + clientPurpose: VERIFIERZ_PURPOSE_TO_VERIFY, + 'clientPurpose#nl-NL': VERIFIERZ_PURPOSE_TO_VERIFY_NL, + }, + } + + const sdjwt = { + compactJwtVc: + 'eyJ0eXAiOiJ2YytzZC1qd3QiLCJraWQiOiJkaWQ6andrOmV5SmhiR2NpT2lKRlV6STFOaUlzSW5WelpTSTZJbk5wWnlJc0ltdDBlU0k2SWtWRElpd2lZM0oySWpvaVVDMHlOVFlpTENKNElqb2lTMGRwYzNodlUzaDJhVzB4YTFOSU1XSnROMnhmUkhCeVIyczNZa2RrWkVaYVdXWnRjVXB1VjJWb1NTSXNJbmtpT2lKYVEzQldUVVZSTkhsNGNUSlZiVGRDVGpoSVQyNUdlamszTTFBMFVUQlVkbmRuZVhWUlgyRmlURlZWSW4wIzAiLCJhbGciOiJFUzI1NiJ9.eyJzdWIiOiJkaWQ6andrOmV5SmhiR2NpT2lKRlV6STFOaUlzSW5WelpTSTZJbk5wWnlJc0ltdDBlU0k2SWtWRElpd2lZM0oySWpvaVVDMHlOVFlpTENKNElqb2lXRkpXUVhsNVJIQldNbXBNTm5oNlUwSktZM1JIZW0xS1pqbFFlV0Z4WHpNdFRVeHJlR0ZoUlRBNFRTSXNJbmtpT2lKQ1NtOUtWM05WYTBaQlUyVlRZMmx4VDFsNVNWTTBZMFpoZVU4emFHaEJTalZaYjJ0dU9IcFRTVEZuSW4wIzAiLCJpc3MiOiJkaWQ6andrOmV5SmhiR2NpT2lKRlV6STFOaUlzSW5WelpTSTZJbk5wWnlJc0ltdDBlU0k2SWtWRElpd2lZM0oySWpvaVVDMHlOVFlpTENKNElqb2lTMGRwYzNodlUzaDJhVzB4YTFOSU1XSnROMnhmUkhCeVIyczNZa2RrWkVaYVdXWnRjVXB1VjJWb1NTSXNJbmtpT2lKYVEzQldUVVZSTkhsNGNUSlZiVGRDVGpoSVQyNUdlamszTTFBMFVUQlVkbmRuZVhWUlgyRmlURlZWSW4wIzAiLCJpYXQiOjE3MzQ0NTgwNDIsInZjdCI6InVybjpldS5ldXJvcGEuZWMuZXVkaTpwaWQ6MSIsIl9zZCI6WyIwWWdpR25hck1LSERnVWx1QktteGdRZUJ4OF8xazMwd3NSRVN1X2t1Y0JFIiwiNEpwU2JzUEsxZndUQzRhR24zZ0hXb1BkVEJrMExOTWZMOEJXeEIxZ1JPWSIsIjRpZmplQUZveUEyQmc0STVNWEFrMVlyc1AtUVBQQXRnZGlHY0RMQTZiTFEiLCJBdTFjdURISUNISE1jUjZxM2R3d1pHbHp5dzMwSXhvTGVhWlNxRktEbmo4IiwiQ0QxbWxOY19VaVBoQUp6YkJveTVMY3dtekFNZTM3d0VLZF9iMTB6QTNxNCIsInFwZ1FOUVRac0VJWHdxUk9fT24xdUVCSVVNODBTcTJLR2tlN0JSU2N0WHciXSwiX3NkX2FsZyI6IlNIQS0yNTYifQ.P84d0CoS4M-zQ29l3S97RMatfJMYkoTgR5EqSMTdYlZAMp4e8iiuz2PXQMfJ-_undCvg4SRXxDACGiLL3Tt7Bw~WyJlNTFiNWI2NS0wNzM3LTQ0MjQtYTUxYS1jNGYzZGNlZGFmMmYiLCJnaXZlbl9uYW1lIiwiSm9obiJd~WyIxM2I1NDIwNi1kYWQ3LTQ3N2UtODYyZC03N2ZiMTQ1MDE5NjUiLCJmYW1pbHlfbmFtZSIsIkRvZSJd~WyJkMmQxNjg3Zi04ZmY4LTRlOTMtYWJjYi1hYTNlNGVjYzY0ZTMiLCJlbWFpbCIsImpvaG5kZW9AZXhhbXBsZS5jb20iXQ~WyIyZDA4YTk2YS03YzYwLTQ3NDEtYTI5YS00ZjBjYTFlNGQ3M2IiLCJwaG9uZSIsIisxLTIwMi01NTUtMDEwMSJd~WyI2YjVkN2FmOS01ZmIxLTQzNTEtYWM1ZS1hMzA1YTBkNjU0ZDUiLCJhZGRyZXNzIix7InN0cmVldF9hZGRyZXNzIjoiMTIzIE1haW4gU3QiLCJsb2NhbGl0eSI6IkFueXRvd24iLCJyZWdpb24iOiJBbnlzdGF0ZSIsImNvdW50cnkiOiJVUyJ9XQ~WyI5MmYzY2M5ZC0yMjQ2LTRiODQtYTk5OS0xYmQyM2U0OGQ0MGEiLCJiaXJ0aGRhdGUiLCIxOTQwLTAxLTAxIl0~', + decodedPayload: { + header: { + typ: 'vc+sd-jwt', + kid: 'did:jwk:eyJhbGciOiJFUzI1NiIsInVzZSI6InNpZyIsImt0eSI6IkVDIiwiY3J2IjoiUC0yNTYiLCJ4IjoiS0dpc3hvU3h2aW0xa1NIMWJtN2xfRHByR2s3YkdkZEZaWWZtcUpuV2VoSSIsInkiOiJaQ3BWTUVRNHl4cTJVbTdCTjhIT25Gejk3M1A0UTBUdndneXVRX2FiTFVVIn0#0', + alg: 'ES256', + }, + payload: { + sub: 'did:jwk:eyJhbGciOiJFUzI1NiIsInVzZSI6InNpZyIsImt0eSI6IkVDIiwiY3J2IjoiUC0yNTYiLCJ4IjoiWFJWQXl5RHBWMmpMNnh6U0JKY3RHem1KZjlQeWFxXzMtTUxreGFhRTA4TSIsInkiOiJCSm9KV3NVa0ZBU2VTY2lxT1l5SVM0Y0ZheU8zaGhBSjVZb2tuOHpTSTFnIn0#0', + iss: 'did:jwk:eyJhbGciOiJFUzI1NiIsInVzZSI6InNpZyIsImt0eSI6IkVDIiwiY3J2IjoiUC0yNTYiLCJ4IjoiS0dpc3hvU3h2aW0xa1NIMWJtN2xfRHByR2s3YkdkZEZaWWZtcUpuV2VoSSIsInkiOiJaQ3BWTUVRNHl4cTJVbTdCTjhIT25Gejk3M1A0UTBUdndneXVRX2FiTFVVIn0#0', + iat: 1734458042, + vct: 'urn:eu.europa.ec.eudi:pid:1', + given_name: 'John', + email: 'johndeo@example.com', + birthdate: '1940-01-01', + phone: '+1-202-555-0101', + address: { + street_address: '123 Main St', + locality: 'Anytown', + region: 'Anystate', + country: 'US', + }, + family_name: 'Doe', + }, + kb: undefined, + }, + } + + const vc: DcqlCredential = { + credential_format: 'vc+sd-jwt', + vct: sdjwt.decodedPayload.payload.vct, + claims: sdjwt.decodedPayload.payload, + } + + const dcqlQueryResult: DcqlQueryResult = DcqlQuery.query(dcqlQuery, [vc]) + + const presentation: DcqlPresentation.Output = {} + for (const [key, value] of Object.entries(dcqlQueryResult.credential_matches)) { + if (value.success) { + presentation[key] = sdjwt.compactJwtVc + } + } + + const dcqlPresentation = DcqlPresentation.parse(presentation) + + const responseOpts: AuthorizationResponseOpts = { + responseURI: EXAMPLE_REDIRECT_URL, + responseURIType: 'redirect_uri', + createJwtCallback: getCreateJwtCallback({ + did: mockResEntity.did, + hexPrivateKey: mockResEntity.hexPrivateKey, + kid: `${mockResEntity.did}#controller`, + alg: SigningAlgo.ES256K, + }), + jwtIssuer: { method: 'did', didUrl: `${mockResEntity.did}#controller`, alg: SigningAlgo.ES256K }, + dcqlResponse: { + dcqlPresentation + }, + responseMode: ResponseMode.DIRECT_POST, + } + + const requestObject = await RequestObject.fromOpts(requestOpts) + const jwt = await requestObject.toJwt() + if (!jwt) throw new Error('JWT is undefined') + const authorizationRequest = await AuthorizationResponse.fromRequestObject(jwt, responseOpts, verifyOpts) + expect(authorizationRequest).toBeDefined() + }) }) diff --git a/packages/siop-oid4vp/lib/__tests__/IT.spec.ts b/packages/siop-oid4vp/lib/__tests__/IT.spec.ts index 5a2311ba..b3747f0b 100644 --- a/packages/siop-oid4vp/lib/__tests__/IT.spec.ts +++ b/packages/siop-oid4vp/lib/__tests__/IT.spec.ts @@ -427,7 +427,7 @@ describe.skip('RP and OP interaction should', () => { const verifiedAuthReqWithJWT = await op.verifyAuthorizationRequest(parsedAuthReqURI.requestObjectJwt) expect(verifiedAuthReqWithJWT.issuer).toMatch(rpMockEntity.did) await expect(op.createAuthorizationResponse(verifiedAuthReqWithJWT, {})).rejects.toThrow( - Error('authentication request expects a verifiable presentation in the response'), + Error('vp_token is present, but no presentation definitions or dcql query provided'), ) expect(verifiedAuthReqWithJWT.payload?.['registration'].client_name).toEqual(VERIFIER_NAME_FOR_CLIENT) diff --git a/packages/siop-oid4vp/lib/__tests__/PresentationExchange.spec.ts b/packages/siop-oid4vp/lib/__tests__/PresentationExchange.spec.ts index 8d69fd5b..5f907d40 100644 --- a/packages/siop-oid4vp/lib/__tests__/PresentationExchange.spec.ts +++ b/packages/siop-oid4vp/lib/__tests__/PresentationExchange.spec.ts @@ -337,9 +337,7 @@ describe('presentation exchange manager tests', () => { const payload = await getPayloadVID1Val() // eslint-disable-next-line @typescript-eslint/no-explicit-any ;(payload.claims?.vp_token as any).presentation_definition_uri = EXAMPLE_PD_URL - await expect(PresentationExchange.findValidPresentationDefinitions(payload)).rejects.toThrow( - SIOPErrors.REQUEST_CLAIMS_PRESENTATION_DEFINITION_BY_REF_AND_VALUE_NON_EXCLUSIVE, - ) + await expect(PresentationExchange.findValidPresentationDefinitions(payload)).rejects.toThrow(SIOPErrors.REQUEST_CLAIMS_PRESENTATION_NON_EXCLUSIVE) }) it('validatePresentationAgainstDefinition: should pass if provided VP match the PD', async function () { diff --git a/packages/siop-oid4vp/lib/__tests__/SdJwt.spec.ts b/packages/siop-oid4vp/lib/__tests__/SdJwt.spec.ts index a5e63522..19fa4dcf 100644 --- a/packages/siop-oid4vp/lib/__tests__/SdJwt.spec.ts +++ b/packages/siop-oid4vp/lib/__tests__/SdJwt.spec.ts @@ -1,6 +1,7 @@ -import { SigningAlgo } from '@sphereon/oid4vc-common' +import { defaultHasher, SigningAlgo } from '@sphereon/oid4vc-common' import { IPresentationDefinition } from '@sphereon/pex' -import { OriginalVerifiableCredential } from '@sphereon/ssi-types' +import { decodeSdJwtVc, OriginalVerifiableCredential } from '@sphereon/ssi-types' +import { DcqlCredential, DcqlQuery } from 'dcql' import { OP, @@ -10,6 +11,7 @@ import { PresentationVerificationCallback, PropertyTarget, ResponseIss, + ResponseMode, ResponseType, RevocationVerification, RP, @@ -32,11 +34,17 @@ import { jest.setTimeout(30000) +type Json = string | number | boolean | null | { // Not exported from dcql + [key: string]: Json; +} | Json[]; + + const EXAMPLE_REDIRECT_URL = 'https://acme.com/hello' const HOLDER_DID = 'did:example:ebfeb1f712ebc6f1c276e12ec21' const SD_JWT_VC = 'eyJhbGciOiJFZERTQSIsInR5cCI6InZjK3NkLWp3dCJ9.eyJpYXQiOjE3MDA0NjQ3MzYwNzYsImlzcyI6ImRpZDprZXk6c29tZS1yYW5kb20tZGlkLWtleSIsIm5iZiI6MTcwMDQ2NDczNjE3NiwidmN0IjoiaHR0cHM6Ly9oaWdoLWFzc3VyYW5jZS5jb20vU3RhdGVCdXNpbmVzc0xpY2Vuc2UiLCJ1c2VyIjp7Il9zZCI6WyI5QmhOVDVsSG5QVmpqQUp3TnR0NDIzM216MFVVMUd3RmFmLWVNWkFQV0JNIiwiSVl5d1FQZl8tNE9hY2Z2S2l1cjRlSnFMa1ZleWRxcnQ1Y2UwMGJReWNNZyIsIlNoZWM2TUNLakIxeHlCVl91QUtvLURlS3ZvQllYbUdBd2VGTWFsd05xbUEiLCJXTXpiR3BZYmhZMkdoNU9pWTRHc2hRU1dQREtSeGVPZndaNEhaQW5YS1RZIiwiajZ6ZFg1OUJYZHlTNFFaTGJITWJ0MzJpenRzWXdkZzRjNkpzWUxNc3ZaMCIsInhKR3Radm41cFM4VEhqVFlJZ3MwS1N5VC1uR3BSR3hDVnp6c1ZEbmMyWkUiXX0sImxpY2Vuc2UiOnsibnVtYmVyIjoxMH0sImNuZiI6eyJqd2siOnsia3R5IjoiRUMiLCJjcnYiOiJQLTI1NiIsIngiOiJUQ0FFUjE5WnZ1M09IRjRqNFc0dmZTVm9ISVAxSUxpbERsczd2Q2VHZW1jIiwieSI6Ilp4amlXV2JaTVFHSFZXS1ZRNGhiU0lpcnNWZnVlY0NFNnQ0alQ5RjJIWlEifX0sIl9zZF9hbGciOiJzaGEtMjU2IiwiX3NkIjpbIl90YnpMeHBaeDBQVHVzV2hPOHRUZlVYU2ZzQjVlLUtrbzl3dmZaaFJrYVkiLCJ1WmNQaHdUTmN4LXpNQU1zemlYMkFfOXlJTGpQSEhobDhEd2pvVXJLVVdZIl19.HAcudVInhNpXkTPQGNosjKTFRJWgKj90NpfloRaDQchGd4zxc1ChWTCCPXzUXTBypASKrzgjZCiXlTr0bzmLAg~WyJHeDZHRUZvR2t6WUpWLVNRMWlDREdBIiwiZGF0ZU9mQmlydGgiLCIyMDAwMDEwMSJd~WyJ1LUt3cmJvMkZfTExQekdSZE1XLUtBIiwibmFtZSIsIkpvaG4iXQ~WyJNV1ZieGJqVFZxUXdLS3h2UGVZdWlnIiwibGFzdE5hbWUiLCJEb2UiXQ~' +const KB_SD_JWT_PRESENTATION = 'eyJ0eXAiOiJ2YytzZC1qd3QiLCJhbGciOiJFUzI1NiJ9.eyJpc3MiOiJkaWQ6a2V5OnNvbWUtcmFuZG9tLWRpZC1rZXkiLCJpYXQiOjE3MzU4MzY0NzY1NjksInZjdCI6Imh0dHBzOi8vaGlnaC1hc3N1cmFuY2UuY29tL1N0YXRlQnVzaW5lc3NMaWNlbnNlIiwiX3NkIjpbIk5ub3U2OGN6VG9qQWY0Z3dIMmJiNFBaWHB4WHJzQ29oWm5CbEZvQ293TTAiLCJ1ajZZZVZwSnRwUjhVWHRpbmVDOGM5LXpTRFJVQzJzSGhsRkNNNWtkLXlNIl0sIl9zZF9hbGciOiJzaGEtMjU2In0.X1pNBBmR-7h6SgxtmZY9GL_nSBzoIvWNw7nqqgJVCnSEbvyTjqTgu4bLSPKeaxf1jY2zHJK1jdxiDzIizRZ0gA~WyI4OTE4YjkxZGFjMzk1OTdkIiwidXNlciIseyJkYXRlX29mX2JpcnRoIjoiMDEvMDEvMTk3MCIsIl9zZCI6WyJ6UE8zb1RCT3BxMmRNcWQtekt5SmF2UlQyWVIwSHBPaV9jczZRajEtVmR3Il19XQ~WyIyOTc4NTNiODE5MTI0MTJkIiwibmFtZSIsIkpvaG4iXQ~WyJkYjEzNDQ4NTgxMzY0M2JlIiwibGljZW5zZSIseyJfc2QiOlsiWUFUR3A3TGxjNEMtTWtYWkZWTEF6RHRtbDFTMVpFWFFyTW5CdmVDWVFwayJdfV0~WyJlOTc3MzhiNmM0OGNhMWJlIiwibnVtYmVyIiwxMF0~eyJ0eXAiOiJrYitqd3QiLCJhbGciOiJFUzI1NiJ9.eyJpYXQiOjE3MzU4MzY0NzYsImF1ZCI6Imh0dHBzOi8vZXhhbXBsZS5jb20iLCJub25jZSI6InFCclI3bXFuWTNRcjQ5ZEFaeWNQRjhGemdFODNtNkgwYzJsMGJ6UDR4U2ciLCJjdXN0b20iOiJkYXRhIiwic2RfaGFzaCI6IlVwYzNYQWpzRU1mdnVmSzJ5Q3RkNDZXUFJmTDVfVDc4UThEZVNZQXlEX28ifQ.Crw56nLFFnVulRQElpq9HoskdKIyd5Mj6vg9UVNSWfhxQ0oGe10RHtifUv4BiFharSvWN99y_DnkhCPu1sPIYw' function getPresentationDefinition(): IPresentationDefinition { return { @@ -80,6 +88,21 @@ function getPresentationDefinition(): IPresentationDefinition { } } +const sdJwtVcQuery: DcqlQuery = { + credentials: [ + { + id: 'my_credential', + format: 'vc+sd-jwt', + meta: { + vct_values: ['https://high-assurance.com/StateBusinessLicense'], + }, + claims: [{ path: ['license', 'number'] }, { path: ['user', 'name'] }], + }, + ], +} + +DcqlQuery.validate(sdJwtVcQuery) + function getVCs(): OriginalVerifiableCredential[] { return [SD_JWT_VC] } @@ -326,4 +349,187 @@ describe.skip('RP and OP interaction should', () => { expect(verifiedAuthResponseWithJWT.oid4vpSubmission?.nonce).toEqual('qBrR7mqnY3Qr49dAZycPF8FzgE83m6H0c2l0bzP4xSg') expect(verifiedAuthResponseWithJWT.idToken).toBeUndefined() }) + + it('succeed when calling with DCQL and right verifiable presentation', async () => { + const opMock = await mockedGetEnterpriseAuthToken('OP') + const opMockEntity = { + ...opMock, + didKey: `${opMock.did}#controller`, + } + const rpMock = await mockedGetEnterpriseAuthToken('RP') + const rpMockEntity = { + ...rpMock, + didKey: `${rpMock.did}#controller`, + } + + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const presentationVerificationCallback: PresentationVerificationCallback = async (_args) => { + return { verified: true } + } + + const resolver = getResolver('ethr') + const rp = RP.builder({ requestVersion: SupportedVersion.SIOPv2_D12_OID4VP_D20 }) + .withClientId(rpMockEntity.did) + .withScope('test') + .withHasher(pexHasher) + .withResponseType([ResponseType.ID_TOKEN, ResponseType.VP_TOKEN]) + .withResponseMode(ResponseMode.DIRECT_POST) + .withRedirectUri(EXAMPLE_REDIRECT_URL) + .withDcqlQuery(sdJwtVcQuery, [PropertyTarget.REQUEST_OBJECT]) + .withPresentationVerification(presentationVerificationCallback) + .withRevocationVerification(RevocationVerification.NEVER) + .withRequestBy(PassBy.VALUE) + .withCreateJwtCallback(internalSignature(rpMockEntity.hexPrivateKey, rpMockEntity.did, `${rpMockEntity.did}#controller`, SigningAlgo.ES256K)) + .withAuthorizationEndpoint('www.myauthorizationendpoint.com') + .withVerifyJwtCallback(getVerifyJwtCallback(resolver)) + .withClientMetadata({ + client_id: WELL_KNOWN_OPENID_FEDERATION, + idTokenSigningAlgValuesSupported: [SigningAlgo.EDDSA], + requestObjectSigningAlgValuesSupported: [SigningAlgo.EDDSA, SigningAlgo.ES256], + responseTypesSupported: [ResponseType.ID_TOKEN], + vpFormatsSupported: { jwt_vc: { alg: [SigningAlgo.EDDSA] } }, + scopesSupported: [Scope.OPENID_DIDAUTHN, Scope.OPENID], + subjectTypesSupported: [SubjectType.PAIRWISE], + subject_syntax_types_supported: ['did', 'did:key'], + passBy: PassBy.VALUE, + logo_uri: VERIFIER_LOGO_FOR_CLIENT, + clientName: VERIFIER_NAME_FOR_CLIENT, + 'clientName#nl-NL': VERIFIER_NAME_FOR_CLIENT_NL + '2022100322', + clientPurpose: VERIFIERZ_PURPOSE_TO_VERIFY, + 'clientPurpose#nl-NL': VERIFIERZ_PURPOSE_TO_VERIFY_NL, + }) + .build() + + const op = OP.builder() + .withPresentationSignCallback(sdJwtVcPresentationSignCallback) + .withExpiresIn(1000) + .withHasher(pexHasher) + .withCreateJwtCallback(internalSignature(opMockEntity.hexPrivateKey, opMockEntity.did, `${opMockEntity.did}#controller`, SigningAlgo.ES256K)) + .withVerifyJwtCallback(getVerifyJwtCallback(resolver)) + .withRegistration({ + authorizationEndpoint: 'www.myauthorizationendpoint.com', + idTokenSigningAlgValuesSupported: [SigningAlgo.EDDSA], + issuer: ResponseIss.SELF_ISSUED_V2, + requestObjectSigningAlgValuesSupported: [SigningAlgo.EDDSA, SigningAlgo.ES256], + responseTypesSupported: [ResponseType.ID_TOKEN, ResponseType.VP_TOKEN], + vpFormats: { jwt_vc: { alg: [SigningAlgo.EDDSA] } }, + scopesSupported: [Scope.OPENID_DIDAUTHN, Scope.OPENID], + subjectTypesSupported: [SubjectType.PAIRWISE], + subject_syntax_types_supported: [], + passBy: PassBy.VALUE, + logo_uri: VERIFIER_LOGO_FOR_CLIENT, + clientName: VERIFIER_NAME_FOR_CLIENT, + 'clientName#nl-NL': VERIFIER_NAME_FOR_CLIENT_NL + '2022100323', + clientPurpose: VERIFIERZ_PURPOSE_TO_VERIFY, + 'clientPurpose#nl-NL': VERIFIERZ_PURPOSE_TO_VERIFY_NL, + }) + .withSupportedVersions(SupportedVersion.SIOPv2_D12_OID4VP_D20) + .build() + + const requestURI = await rp.createAuthorizationRequestURI({ + correlationId: '1234', + nonce: 'qBrR7mqnY3Qr49dAZycPF8FzgE83m6H0c2l0bzP4xSg', + state: 'b32f0087fc9816eb813fd11f', + }) + + // Let's test the parsing + const parsedAuthReqURI = await op.parseAuthorizationRequestURI(requestURI.encodedUri) + expect(parsedAuthReqURI.authorizationRequestPayload).toBeDefined() + expect(parsedAuthReqURI.requestObjectJwt).toBeDefined() + + if (!parsedAuthReqURI.requestObjectJwt) throw new Error('requestObjectJwt is undefined') + const verifiedAuthReqWithJWT = await op.verifyAuthorizationRequest(parsedAuthReqURI.requestObjectJwt) + expect(verifiedAuthReqWithJWT.issuer).toMatch(rpMockEntity.did) + + // The KB property is added to the JWT when the presentation is signed. Passing a VC will make the test fail + const dcqlCredentials: DcqlCredential[] = [KB_SD_JWT_PRESENTATION].map(vc => ({ credential_format: 'vc+sd-jwt', claims: decodeSdJwtVc(vc as string, defaultHasher).decodedPayload as { [x: string]: Json }, vct: decodeSdJwtVc(vc as string, defaultHasher).decodedPayload.vct } )) + + const queryResult = DcqlQuery.query(sdJwtVcQuery, dcqlCredentials) + + expect(queryResult).toEqual({ + "canBeSatisfied": true, + "credential_matches": { + "my_credential": { + "all": [ + [ + { + "credential_index": 0, + "output": { + "claims": { + "license": { + "number": 10 + }, + "user": { + "name": "John" + } + }, + "vct": "https://high-assurance.com/StateBusinessLicense" + }, + "success": true, + "typed": true + } + ] + ], + "credential_index": 0, + "output": { + "claims": { + "license": { + "number": 10 + }, + "user": { + "name": "John" + } + }, + "vct": "https://high-assurance.com/StateBusinessLicense" + }, + "success": true, + "typed": true + } + }, + "credentials": [ + { + "claims": [ + { + "path": [ + "license", + "number" + ] + }, + { + "path": [ + "user", + "name" + ] + } + ], + "format": "vc+sd-jwt", + "id": "my_credential", + "meta": { + "vct_values": [ + "https://high-assurance.com/StateBusinessLicense" + ] + } + } + ] + }) + + const dcqlPresentation: { [x: string]: string | { [x: string]: Json } } = {} + + for (const [key, _] of Object.entries(queryResult.credential_matches)) { + dcqlPresentation[key] = KB_SD_JWT_PRESENTATION as string | { [x: string]: Json } + } + + const authenticationResponseWithJWT = await op.createAuthorizationResponse(verifiedAuthReqWithJWT, { + dcqlResponse: { dcqlPresentation }, + }) + expect(authenticationResponseWithJWT.response.payload).toBeDefined() + expect(authenticationResponseWithJWT.response.idToken).toBeDefined() + + const verifiedAuthResponseWithJWT = await rp.verifyAuthorizationResponse(authenticationResponseWithJWT.response.payload, { + dcqlQuery: sdJwtVcQuery, + }) + + expect(verifiedAuthResponseWithJWT.idToken?.jwt).toBeDefined() + expect(verifiedAuthResponseWithJWT.idToken?.payload.nonce).toMatch('qBrR7mqnY3Qr49dAZycPF8FzgE83m6H0c2l0bzP4xSg') + }) }) diff --git a/packages/siop-oid4vp/lib/__tests__/TestUtils.ts b/packages/siop-oid4vp/lib/__tests__/TestUtils.ts index bf073abe..33f30091 100644 --- a/packages/siop-oid4vp/lib/__tests__/TestUtils.ts +++ b/packages/siop-oid4vp/lib/__tests__/TestUtils.ts @@ -2,9 +2,11 @@ // @ts-ignore import crypto, { createHash } from 'crypto' +import { digest, ES256, generateSalt } from '@sd-jwt/crypto-nodejs' +import { SDJwtVcInstance } from '@sd-jwt/sd-jwt-vc' import { JwtPayload, parseJWT, SigningAlgo, uuidv4 } from '@sphereon/oid4vc-common' import { PartialSdJwtDecodedVerifiableCredential } from '@sphereon/pex/dist/main/lib' -import { IProofType } from '@sphereon/ssi-types' +import { IProofType, SdJwtVcKbJwtPayload } from '@sphereon/ssi-types' // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore import base58 from 'bs58' @@ -26,7 +28,7 @@ import { RPRegistrationMetadataPayload, Scope, SubjectSyntaxTypesSupportedValues, - SubjectType, + SubjectType } from '../' import SIOPErrors from '../types/Errors' @@ -38,9 +40,10 @@ import { VERIFIER_NAME_FOR_CLIENT, VERIFIER_NAME_FOR_CLIENT_NL, VERIFIERZ_PURPOSE_TO_VERIFY, - VERIFIERZ_PURPOSE_TO_VERIFY_NL, + VERIFIERZ_PURPOSE_TO_VERIFY_NL } from './data/mockedData' + export interface TESTKEY { key: JWK did: string @@ -303,15 +306,51 @@ export const sdJwtVcPresentationSignCallback: PresentationSignCallback = async ( }, }) - const header = { - ...presentation.kbJwt.header, - alg: 'ES256K', + const createSignerVerifier = async () => { + const { privateKey, publicKey } = await ES256.generateKeyPair(); + return { + signer: await ES256.getSigner(privateKey), + verifier: await ES256.getVerifier(publicKey) + } } - const payload = { - ...presentation.kbJwt.payload, - aud: '123', + + const { signer, verifier } = await createSignerVerifier(); + + const sdjwt = new SDJwtVcInstance({ + signer, + signAlg: ES256.alg, + verifier, + hasher: digest, + saltGenerator: generateSalt, + kbSigner: signer, + kbSignAlg: ES256.alg, + kbVerifier: verifier + }) + + const claims = { + license: { + number: 10 + }, + user: { + name: 'John', + date_of_birth: '01/01/1970' + } } - const kbJwtCompact = `${Buffer.from(JSON.stringify(header)).toString('base64url')}.${Buffer.from(JSON.stringify(payload)).toString('base64url')}.signature` - return presentation.compactSdJwtVc + kbJwtCompact + const kbPayload: Omit = presentation.kbJwt.payload + + presentation.compactSdJwtVc = await sdjwt.present( + presentation.compactSdJwtVc, + { + user: { name: true }, + license: { number: true } + }, + { + kb: { + payload: kbPayload, + }, + }, + ); + + return presentation.compactSdJwtVc } diff --git a/packages/siop-oid4vp/lib/authorization-request/AuthorizationRequest.ts b/packages/siop-oid4vp/lib/authorization-request/AuthorizationRequest.ts index 1616da41..e7e1c0dc 100644 --- a/packages/siop-oid4vp/lib/authorization-request/AuthorizationRequest.ts +++ b/packages/siop-oid4vp/lib/authorization-request/AuthorizationRequest.ts @@ -1,6 +1,8 @@ import { parseJWT } from '@sphereon/oid4vc-common' +import { DcqlQuery } from 'dcql' import { PresentationDefinitionWithLocation } from '../authorization-response' +import { Dcql } from '../authorization-response' import { PresentationExchange } from '../authorization-response/PresentationExchange' import { fetchByReferenceOrUseByValue, removeNullUndefined } from '../helpers' import { authorizationRequestVersionDiscovery } from '../helpers/SIOPSpecVersion' @@ -203,6 +205,9 @@ export class AuthorizationRequest { mergedPayload, await this.getSupportedVersion(), ) + + const dcqlQuery = await Dcql.findValidDcqlQuery(mergedPayload) + return { jwt, payload: parsedJwt?.payload, @@ -213,6 +218,7 @@ export class AuthorizationRequest { correlationId: opts.correlationId, authorizationRequest: this, verifyOpts: opts, + dcqlQuery, presentationDefinitions, registrationMetadataPayload, requestObject: this.requestObject, @@ -283,4 +289,8 @@ export class AuthorizationRequest { public async getPresentationDefinitions(version?: SupportedVersion): Promise { return await PresentationExchange.findValidPresentationDefinitions(await this.mergedPayloads(), version) } + + public async getDcqlQuery(): Promise { + return await Dcql.findValidDcqlQuery(await this.mergedPayloads()) + } } diff --git a/packages/siop-oid4vp/lib/authorization-request/Payload.ts b/packages/siop-oid4vp/lib/authorization-request/Payload.ts index 3219f700..1a41ba5d 100644 --- a/packages/siop-oid4vp/lib/authorization-request/Payload.ts +++ b/packages/siop-oid4vp/lib/authorization-request/Payload.ts @@ -1,6 +1,6 @@ import { PEX } from '@sphereon/pex' -import { getNonce, removeNullUndefined } from '../helpers' +import { getNonce, getWithUrl, removeNullUndefined } from '../helpers' import { RequestObject } from '../request-object' import { isTarget, isTargetOrNoTargets } from '../rp/Opts' import { RPRegistrationMetadataPayloadSchema } from '../schemas' @@ -11,19 +11,30 @@ import { PassBy, RPRegistrationMetadataPayload, SIOPErrors, - SupportedVersion, + SupportedVersion } from '../types' import { createRequestRegistration } from './RequestRegistration' import { ClaimPayloadOptsVID1, CreateAuthorizationRequestOpts, PropertyTarget } from './types' -export const createPresentationDefinitionClaimsProperties = (opts: ClaimPayloadOptsVID1): ClaimPayloadVID1 => { - if (!opts || !opts.vp_token || (!opts.vp_token.presentation_definition && !opts.vp_token.presentation_definition_uri)) { +export const createPresentationDefinitionClaimsProperties = async (opts: ClaimPayloadOptsVID1): Promise => { + if ( + !opts || + !opts.vp_token || + (!opts.vp_token.presentation_definition && !opts.vp_token.presentation_definition_uri && !opts.vp_token.dcql_query) + ) { return undefined } - const discoveryResult = PEX.definitionVersionDiscovery(opts.vp_token.presentation_definition) - if (discoveryResult.error) { - throw new Error(SIOPErrors.REQUEST_CLAIMS_PRESENTATION_DEFINITION_NOT_VALID) + + let presentationDef = opts.vp_token.presentation_definition + if (!presentationDef && opts.vp_token.presentation_definition_uri) { + presentationDef = await getWithUrl(opts.vp_token.presentation_definition_uri, false) + } + if (presentationDef) { + const discoveryResult = PEX.definitionVersionDiscovery(presentationDef) + if (discoveryResult.error) { + return Promise.reject(new Error(SIOPErrors.REQUEST_CLAIMS_PRESENTATION_DEFINITION_NOT_VALID)) + } } return { @@ -34,6 +45,7 @@ export const createPresentationDefinitionClaimsProperties = (opts: ClaimPayloadO ...(opts.vp_token.presentation_definition_uri && { presentation_definition_uri: opts.vp_token.presentation_definition_uri }), }, }), + ...(opts.vp_token.dcql_query && { vp_token: { dcql_query: opts.vp_token.dcql_query } }), } } @@ -47,7 +59,7 @@ export const createAuthorizationRequestPayload = async ( // TODO: if opts['registration] throw Error to get rid of test code using that key const clientMetadata = opts['registration'] ?? (opts.clientMetadata as ClientMetadataOpts) const registration = await createRequestRegistration(clientMetadata, opts) - const claims = opts.version >= SupportedVersion.SIOPv2_ID1 ? opts.payload.claims : createPresentationDefinitionClaimsProperties(opts.payload.claims) + const claims = opts.version >= SupportedVersion.SIOPv2_ID1 ? opts.payload.claims : await createPresentationDefinitionClaimsProperties(opts.payload.claims) const isRequestTarget = isTargetOrNoTargets(PropertyTarget.AUTHORIZATION_REQUEST, opts.requestObject.targets) const isRequestByValue = opts.requestObject.passBy === PassBy.VALUE diff --git a/packages/siop-oid4vp/lib/authorization-request/URI.ts b/packages/siop-oid4vp/lib/authorization-request/URI.ts index 81678070..eab47c3e 100644 --- a/packages/siop-oid4vp/lib/authorization-request/URI.ts +++ b/packages/siop-oid4vp/lib/authorization-request/URI.ts @@ -1,5 +1,6 @@ import { parseJWT } from '@sphereon/oid4vc-common' +import { Dcql } from '../authorization-response' import { PresentationExchange } from '../authorization-response/PresentationExchange' import { decodeUriAsJson, encodeJsonAsURI, fetchByReferenceOrUseByValue } from '../helpers' import { assertValidRequestObjectPayload, RequestObject } from '../request-object' @@ -163,8 +164,9 @@ export class URI implements AuthorizationRequestURI { const requestObjectPayload: RequestObjectPayload = requestObjectJwt ? (parseJWT(requestObjectJwt).payload as RequestObjectPayload) : undefined if (requestObjectPayload) { - // Only used to validate if the request object contains presentation definition(s) + // Only used to validate if the request object contains presentation definition(s) | a dcql query await PresentationExchange.findValidPresentationDefinitions({ ...authorizationRequestPayload, ...requestObjectPayload }) + await Dcql.findValidDcqlQuery({ ...authorizationRequestPayload, ...requestObjectPayload }) assertValidRequestObjectPayload(requestObjectPayload) if (requestObjectPayload.registration) { @@ -235,16 +237,22 @@ export class URI implements AuthorizationRequestURI { return { scheme, authorizationRequestPayload } } - public static async parseAndResolve(uri: string) { + public static async parseAndResolve(uri: string, rpRegistrationMetadata?: RPRegistrationMetadataPayload) { if (!uri) { throw Error(SIOPErrors.BAD_PARAMS) } const { authorizationRequestPayload, scheme } = this.parse(uri) + const requestObjectJwt = await fetchByReferenceOrUseByValue(authorizationRequestPayload.request_uri, authorizationRequestPayload.request, true) - const registrationMetadata: RPRegistrationMetadataPayload = await fetchByReferenceOrUseByValue( - authorizationRequestPayload['client_metadata_uri'] ?? authorizationRequestPayload['registration_uri'], - authorizationRequestPayload['client_metadata'] ?? authorizationRequestPayload['registration'], - ) + let registrationMetadata: RPRegistrationMetadataPayload + if (rpRegistrationMetadata !== undefined && rpRegistrationMetadata !== null) { + registrationMetadata = rpRegistrationMetadata + } else { + registrationMetadata = await fetchByReferenceOrUseByValue( + authorizationRequestPayload['client_metadata_uri'] ?? authorizationRequestPayload['registration_uri'], + authorizationRequestPayload['client_metadata'] ?? authorizationRequestPayload['registration'], + ) + } assertValidRPRegistrationMedataPayload(registrationMetadata) return { scheme, authorizationRequestPayload, requestObjectJwt, registrationMetadata } } diff --git a/packages/siop-oid4vp/lib/authorization-request/types.ts b/packages/siop-oid4vp/lib/authorization-request/types.ts index 64956db9..da1abd0c 100644 --- a/packages/siop-oid4vp/lib/authorization-request/types.ts +++ b/packages/siop-oid4vp/lib/authorization-request/types.ts @@ -1,7 +1,7 @@ import { SigningAlgo } from '@sphereon/oid4vc-common' import { Hasher } from '@sphereon/ssi-types' -import { PresentationDefinitionPayloadOpts } from '../authorization-response' +import { DcqlQueryPayloadOpts, PresentationDefinitionPayloadOpts } from '../authorization-response' import { RequestObjectOpts } from '../request-object' import { ClientIdScheme, @@ -19,7 +19,7 @@ import { VerifyJwtCallback } from '../types/VpJwtVerifier' export interface ClaimPayloadOptsVID1 extends ClaimPayloadCommonOpts { id_token?: IdTokenClaimPayload - vp_token?: PresentationDefinitionPayloadOpts + vp_token?: PresentationDefinitionPayloadOpts | DcqlQueryPayloadOpts } export interface ClaimPayloadCommonOpts { diff --git a/packages/siop-oid4vp/lib/authorization-response/AuthorizationResponse.ts b/packages/siop-oid4vp/lib/authorization-response/AuthorizationResponse.ts index 4084441c..b290423e 100644 --- a/packages/siop-oid4vp/lib/authorization-response/AuthorizationResponse.ts +++ b/packages/siop-oid4vp/lib/authorization-response/AuthorizationResponse.ts @@ -1,16 +1,19 @@ -import { CredentialMapper, Hasher } from '@sphereon/ssi-types' +import { CredentialMapper, Hasher, WrappedVerifiablePresentation } from '@sphereon/ssi-types' +import { DcqlPresentation } from 'dcql' import { AuthorizationRequest, VerifyAuthorizationRequestOpts } from '../authorization-request' import { assertValidVerifyAuthorizationRequestOpts } from '../authorization-request/Opts' import { IDToken } from '../id-token' import { AuthorizationResponsePayload, ResponseType, SIOPErrors, VerifiedAuthorizationRequest, VerifiedAuthorizationResponse } from '../types' +import { Dcql } from './Dcql' import { assertValidVerifiablePresentations, extractNonceFromWrappedVerifiablePresentation, extractPresentationsFromVpToken, verifyPresentations, } from './OpenID4VP' +import { extractPresentationsFromDcqlVpToken } from './OpenID4VP' import { assertValidResponseOpts } from './Opts' import { createResponsePayload } from './Payload' import { AuthorizationResponseOpts, PresentationDefinitionWithLocation, VerifyAuthorizationResponseOpts } from './types' @@ -123,9 +126,11 @@ export class AuthorizationResponse { authorizationRequest, }) - if (hasVpToken) { + if (!hasVpToken) return response + + if (responseOpts.presentationExchange) { const wrappedPresentations = response.payload.vp_token - ? await extractPresentationsFromVpToken(response.payload.vp_token, { + ? extractPresentationsFromVpToken(response.payload.vp_token, { hasher: verifyOpts.hasher, }) : [] @@ -139,6 +144,12 @@ export class AuthorizationResponse { hasher: verifyOpts.hasher, }, }) + } else if (verifiedAuthorizationRequest.dcqlQuery) { + await Dcql.assertValidDcqlPresentationResult(responseOpts.dcqlResponse.dcqlPresentation as DcqlPresentation, verifiedAuthorizationRequest.dcqlQuery, { + hasher: verifyOpts.hasher, + }) + } else { + throw new Error('vp_token is present, but no presentation definitions or dcql query provided') } return response @@ -155,11 +166,20 @@ export class AuthorizationResponse { } const verifiedIdToken = await this.idToken?.verify(verifyOpts) - const oid4vp = await verifyPresentations(this, verifyOpts) + if (this.payload.vp_token && !verifyOpts.presentationDefinitions && !verifyOpts.dcqlQuery) { + throw new Error('vp_token is present, but no presentation definitions or dcql query provided') + } + + const emptyPresentationDefinitions = Array.isArray(verifyOpts.presentationDefinitions) && verifyOpts.presentationDefinitions.length === 0 + if (!this.payload.vp_token && ((verifyOpts.presentationDefinitions && !emptyPresentationDefinitions) || verifyOpts.dcqlQuery)) { + throw new Error('Presentation definitions or dcql query provided, but no vp_token present') + } + + const oid4vp = this.payload.vp_token ? await verifyPresentations(this, verifyOpts) : undefined // Gather all nonces const allNonces = new Set() - if (oid4vp && oid4vp.nonce) allNonces.add(oid4vp.nonce) + if (oid4vp && (oid4vp.dcql?.nonce || oid4vp.presentationExchange?.nonce)) allNonces.add(oid4vp.dcql?.nonce ?? oid4vp.presentationExchange?.nonce) if (verifiedIdToken) allNonces.add(verifiedIdToken.payload.nonce) if (merged.nonce) allNonces.add(merged.nonce) @@ -185,7 +205,8 @@ export class AuthorizationResponse { state, correlationId: verifyOpts.correlationId, ...(this.idToken && { idToken: verifiedIdToken }), - ...(oid4vp && { oid4vpSubmission: oid4vp }), + ...(oid4vp?.presentationExchange && { oid4vpSubmission: oid4vp.presentationExchange }), + ...(oid4vp?.dcql && { oid4vpSubmissionDcql: oid4vp.dcql }), } } @@ -206,14 +227,21 @@ export class AuthorizationResponse { } public async getMergedProperty(key: string, opts?: { consistencyCheck?: boolean; hasher?: Hasher }): Promise { - const merged = await this.mergedPayloads(opts) + const merged = await this.mergedPayloads(opts) // FIXME this is really bad, expensive... return merged[key] as T } public async mergedPayloads(opts?: { consistencyCheck?: boolean; hasher?: Hasher }): Promise { let nonce: string | undefined = this._payload.nonce if (this._payload?.vp_token) { - const presentations = this.payload.vp_token ? await extractPresentationsFromVpToken(this.payload.vp_token, opts) : [] + let presentations: WrappedVerifiablePresentation | WrappedVerifiablePresentation[] + + try { + presentations = extractPresentationsFromDcqlVpToken(this._payload.vp_token as string, opts) + } catch (e) { + presentations = extractPresentationsFromVpToken(this._payload.vp_token, opts) + } + if (!presentations || (Array.isArray(presentations) && presentations.length === 0)) { return Promise.reject(Error('missing presentation(s)')) } diff --git a/packages/siop-oid4vp/lib/authorization-response/Dcql.ts b/packages/siop-oid4vp/lib/authorization-response/Dcql.ts new file mode 100644 index 00000000..f051fae0 --- /dev/null +++ b/packages/siop-oid4vp/lib/authorization-response/Dcql.ts @@ -0,0 +1,78 @@ +import { Hasher } from '@sphereon/ssi-types' +import { DcqlMdocCredential, DcqlPresentation, DcqlPresentationResult, DcqlQuery, DcqlSdJwtVcCredential } from 'dcql' + +import { extractDataFromPath } from '../helpers' +import { AuthorizationRequestPayload, SIOPErrors } from '../types' + +import { extractDcqlPresentationFromDcqlVpToken } from './OpenID4VP' + +/** + * Finds a valid DcqlQuery inside the given AuthenticationRequestPayload + * throws exception if the DcqlQuery is not valid + * returns the decoded dcql query if a valid instance found + * @param authorizationRequestPayload object that can have a dcql_query inside + * @param version + */ + +export class Dcql { + + static findValidDcqlQuery = async (authorizationRequestPayload: AuthorizationRequestPayload): Promise => { + const dcqlQuery: string[] = extractDataFromPath(authorizationRequestPayload, '$.dcql_query').map((d) => d.value) + const definitions = extractDataFromPath(authorizationRequestPayload, '$.presentation_definition') + const definitionsFromList = extractDataFromPath(authorizationRequestPayload, '$.presentation_definition[*]') + const definitionRefs = extractDataFromPath(authorizationRequestPayload, '$.presentation_definition_uri') + const definitionRefsFromList = extractDataFromPath(authorizationRequestPayload, '$.presentation_definition_uri[*]') + + const hasPD = (definitions && definitions.length > 0) || (definitionsFromList && definitionsFromList.length > 0) + const hasPdRef = (definitionRefs && definitionRefs.length > 0) || (definitionRefsFromList && definitionRefsFromList.length > 0) + const hasDcql = dcqlQuery && dcqlQuery.length > 0 + + if ([hasPD, hasPdRef, hasDcql].filter(Boolean).length > 1) { + throw new Error(SIOPErrors.REQUEST_CLAIMS_PRESENTATION_NON_EXCLUSIVE) + } + + if (dcqlQuery.length === 0) return undefined + + if (dcqlQuery.length > 1) { + throw new Error('Found multiple dcql_query in vp_token. Only one is allowed') + } + + return DcqlQuery.parse(JSON.parse(dcqlQuery[0])) + } + + static getDcqlPresentationResult = (record: DcqlPresentation | string, dcqlQuery: DcqlQuery, opts: { + hasher?: Hasher + }) => { + const dcqlPresentation = Object.fromEntries( + Object.entries(extractDcqlPresentationFromDcqlVpToken(record, opts)).map(([queryId, p]) => { + if (p.format === 'mso_mdoc') { + return [ + queryId, + { + credential_format: 'mso_mdoc', + doctype: p.vcs[0].credential.toJson().docType, + namespaces: p.vcs[0].decoded + } satisfies DcqlMdocCredential, + ] + } else if (p.format === 'vc+sd-jwt') { + return [queryId, { + credential_format: 'vc+sd-jwt', + vct: p.vcs[0].decoded.vct, + claims: p.vcs[0].decoded + } satisfies DcqlSdJwtVcCredential] + } else { + throw new Error('DcqlPresentation atm only supports mso_mdoc and vc+sd-jwt') + } + }), + ) + + return DcqlPresentationResult.fromDcqlPresentation(dcqlPresentation, { dcqlQuery }) + } + + static assertValidDcqlPresentationResult = async (record: DcqlPresentation | string, dcqlQuery: DcqlQuery, opts: { + hasher?: Hasher + }) => { + const result = Dcql.getDcqlPresentationResult(record, dcqlQuery, opts) + return DcqlPresentationResult.validate(result) + } +} diff --git a/packages/siop-oid4vp/lib/authorization-response/OpenID4VP.ts b/packages/siop-oid4vp/lib/authorization-response/OpenID4VP.ts index c6b45ee9..607d647e 100644 --- a/packages/siop-oid4vp/lib/authorization-response/OpenID4VP.ts +++ b/packages/siop-oid4vp/lib/authorization-response/OpenID4VP.ts @@ -1,4 +1,3 @@ -import { parseJWT } from '@sphereon/oid4vc-common' import { IPresentationDefinition, PEX, PresentationSubmissionLocation } from '@sphereon/pex' import { Format } from '@sphereon/pex-models' import { @@ -10,6 +9,7 @@ import { W3CVerifiablePresentation, WrappedVerifiablePresentation, } from '@sphereon/ssi-types' +import { DcqlPresentation, DcqlQuery } from 'dcql' import { AuthorizationRequest } from '../authorization-request' import { verifyRevocation } from '../helpers' @@ -21,9 +21,11 @@ import { SIOPErrors, SupportedVersion, VerifiedOpenID4VPSubmission, + VerifiedOpenID4VPSubmissionDcql, } from '../types' import { AuthorizationResponse } from './AuthorizationResponse' +import { Dcql } from './Dcql' import { PresentationExchange } from './PresentationExchange' import { AuthorizationResponseOpts, @@ -40,11 +42,7 @@ export const extractNonceFromWrappedVerifiablePresentation = (wrappedVp: Wrapped // TODO: replace this once `kbJwt.payload` is available on the decoded sd-jwt (pr in ssi-sdk) // If it doesn't end with ~, it contains a kbJwt if (!wrappedVp.presentation.compactSdJwtVc.endsWith('~')) { - const kbJwt = wrappedVp.presentation.compactSdJwtVc.split('~').pop() - - const { payload } = parseJWT(kbJwt) - - return payload.nonce + return wrappedVp.presentation.kbJwt.payload.nonce } // No kb-jwt means no nonce (error will be handled later) @@ -69,50 +67,68 @@ export const extractNonceFromWrappedVerifiablePresentation = (wrappedVp: Wrapped export const verifyPresentations = async ( authorizationResponse: AuthorizationResponse, verifyOpts: VerifyAuthorizationResponseOpts, -): Promise => { - if ( - !authorizationResponse.payload.vp_token || - (Array.isArray(authorizationResponse.payload.vp_token) && authorizationResponse.payload.vp_token.length === 0) - ) { - return Promise.reject(Error('the payload is missing a vp_token')) +): Promise<{ presentationExchange?: VerifiedOpenID4VPSubmission; dcql?: VerifiedOpenID4VPSubmissionDcql }> => { + let idPayload: IDTokenPayload | undefined + if (authorizationResponse.idToken) { + idPayload = await authorizationResponse.idToken.payload() } - const presentations = await extractPresentationsFromVpToken(authorizationResponse.payload.vp_token, { hasher: verifyOpts.hasher }) + let wrappedPresentations: WrappedVerifiablePresentation[] = [] const presentationDefinitions = verifyOpts.presentationDefinitions ? Array.isArray(verifyOpts.presentationDefinitions) ? verifyOpts.presentationDefinitions : [verifyOpts.presentationDefinitions] : [] - let idPayload: IDTokenPayload | undefined - if (authorizationResponse.idToken) { - idPayload = await authorizationResponse.idToken.payload() - } - // todo: Probably wise to check against request for the location of the submission_data - const presentationSubmission = idPayload?._vp_token?.presentation_submission ?? authorizationResponse.payload.presentation_submission - - await assertValidVerifiablePresentations({ - presentationDefinitions, - presentations, - verificationCallback: verifyOpts.verification.presentationVerificationCallback, - opts: { - presentationSubmission, - restrictToFormats: verifyOpts.restrictToFormats, - restrictToDIDMethods: verifyOpts.restrictToDIDMethods, - hasher: verifyOpts.hasher, - }, - }) - - // If there are no presentations, and the `assertValidVerifiablePresentations` did not fail - // it means there's no oid4vp response and also not requested - if (Array.isArray(presentations) && presentations.length === 0) { - return null - } - if (!presentations || (Array.isArray(presentations) && presentations.length === 0)) { - return Promise.reject(Error('missing presentation(s)')) + let presentationSubmission: PresentationSubmission | undefined + + let dcqlPresentation: { [credentialQueryId: string]: WrappedVerifiablePresentation } | undefined + + let dcqlQuery = verifyOpts.dcqlQuery ?? authorizationResponse?.authorizationRequest?.payload.dcql_query + if (dcqlQuery) { + dcqlQuery = DcqlQuery.parse(dcqlQuery) + dcqlPresentation = extractDcqlPresentationFromDcqlVpToken(authorizationResponse.payload.vp_token as string, { hasher: verifyOpts.hasher }) + wrappedPresentations = Object.values(dcqlPresentation) + + const verifiedPresentations = await Promise.all( + wrappedPresentations.map((presentation) => + verifyOpts.verification.presentationVerificationCallback(presentation.original as W3CVerifiablePresentation), + ), + ) + + await Dcql.assertValidDcqlPresentationResult(authorizationResponse.payload.vp_token as string, dcqlQuery, { hasher: verifyOpts.hasher }) + + if (verifiedPresentations.some((verified) => !verified)) { + const message = verifiedPresentations + .map((verified) => verified.reason) + .filter(Boolean) + .join(', ') + + throw Error(`Failed to verify presentations. ${message}`) + } + } else { + const presentations = authorizationResponse.payload.vp_token + ? extractPresentationsFromVpToken(authorizationResponse.payload.vp_token, { hasher: verifyOpts.hasher }) + : [] + wrappedPresentations = Array.isArray(presentations) ? presentations : [presentations] + + // todo: Probably wise to check against request for the location of the submission_data + presentationSubmission = idPayload?._vp_token?.presentation_submission ?? authorizationResponse.payload.presentation_submission + + await assertValidVerifiablePresentations({ + presentationDefinitions, + presentations, + verificationCallback: verifyOpts.verification.presentationVerificationCallback, + opts: { + presentationSubmission, + restrictToFormats: verifyOpts.restrictToFormats, + restrictToDIDMethods: verifyOpts.restrictToDIDMethods, + hasher: verifyOpts.hasher, + }, + }) } - const presentationsArray = Array.isArray(presentations) ? presentations : [presentations] - const presentationsWithoutMdoc = presentationsArray.filter((p) => p.format !== 'mso_mdoc') + + const presentationsWithoutMdoc = wrappedPresentations.filter((p) => p.format !== 'mso_mdoc') const nonces = new Set(presentationsWithoutMdoc.map(extractNonceFromWrappedVerifiablePresentation)) if (presentationsWithoutMdoc.length > 0 && nonces.size !== 1) { throw Error(`${nonces.size} nonce values found for ${presentationsWithoutMdoc.length}. Should be 1`) @@ -131,17 +147,42 @@ export const verifyPresentations = async ( if (!verifyOpts.verification.revocationOpts?.revocationVerificationCallback) { throw Error(`Please provide a revocation callback as revocation checking of credentials and presentations is not disabled`) } - for (const vp of presentationsArray) { + for (const vp of wrappedPresentations) { await verifyRevocation(vp, verifyOpts.verification.revocationOpts.revocationVerificationCallback, revocationVerification) } } - return { nonce, presentations: presentationsArray, presentationDefinitions, submissionData: presentationSubmission } + if (presentationDefinitions) { + return { presentationExchange: { nonce, presentations: wrappedPresentations, presentationDefinitions, submissionData: presentationSubmission } } + } else { + return { dcql: { nonce, presentation: dcqlPresentation, dcqlQuery } } + } +} + +export const extractDcqlPresentationFromDcqlVpToken = ( + vpToken: DcqlPresentation.Input | string, + opts?: { hasher?: Hasher }, +): { [credentialQueryId: string]: WrappedVerifiablePresentation } => { + const dcqlPresentation = Object.fromEntries( + Object.entries(DcqlPresentation.parse(vpToken)).map(([credentialQueryId, vp]) => [ + credentialQueryId, + CredentialMapper.toWrappedVerifiablePresentation(vp as W3CVerifiablePresentation | CompactSdJwtVc | string, { hasher: opts.hasher }), + ]), + ) + + return dcqlPresentation +} + +export const extractPresentationsFromDcqlVpToken = ( + vpToken: DcqlPresentation.Input | string, + opts?: { hasher?: Hasher }, +): WrappedVerifiablePresentation[] => { + return Object.values(extractDcqlPresentationFromDcqlVpToken(vpToken, opts)) } -export const extractPresentationsFromVpToken = async ( +export const extractPresentationsFromVpToken = ( vpToken: Array | W3CVerifiablePresentation | CompactSdJwtVc | string, opts?: { hasher?: Hasher }, -): Promise => { +): WrappedVerifiablePresentation[] | WrappedVerifiablePresentation => { const tokens = Array.isArray(vpToken) ? vpToken : [vpToken] const wrappedTokens = tokens.map((vp) => CredentialMapper.toWrappedVerifiablePresentation(vp, { hasher: opts?.hasher })) diff --git a/packages/siop-oid4vp/lib/authorization-response/Payload.ts b/packages/siop-oid4vp/lib/authorization-response/Payload.ts index 6dc5a533..d357b786 100644 --- a/packages/siop-oid4vp/lib/authorization-response/Payload.ts +++ b/packages/siop-oid4vp/lib/authorization-response/Payload.ts @@ -1,3 +1,5 @@ +import { DcqlPresentation } from 'dcql' + import { AuthorizationRequest } from '../authorization-request' import { IDToken } from '../id-token' import { RequestObject } from '../request-object' @@ -29,7 +31,11 @@ export const createResponsePayload = async ( } // vp tokens - await putPresentationSubmissionInLocation(authorizationRequest, responsePayload, responseOpts, idTokenPayload) + if (responseOpts.dcqlResponse) { + responsePayload.vp_token = DcqlPresentation.encode(responseOpts.dcqlResponse.dcqlPresentation as DcqlPresentation) + } else { + await putPresentationSubmissionInLocation(authorizationRequest, responsePayload, responseOpts, idTokenPayload) + } if (idTokenPayload) { const idToken = await IDToken.fromIDTokenPayload(idTokenPayload, responseOpts) responsePayload.id_token = await idToken.jwt(responseOpts.jwtIssuer) diff --git a/packages/siop-oid4vp/lib/authorization-response/PresentationExchange.ts b/packages/siop-oid4vp/lib/authorization-response/PresentationExchange.ts index 66f717b2..ad74a89c 100644 --- a/packages/siop-oid4vp/lib/authorization-response/PresentationExchange.ts +++ b/packages/siop-oid4vp/lib/authorization-response/PresentationExchange.ts @@ -196,7 +196,7 @@ export class PresentationExchange { ).map((d) => d.value) const vpTokenRefs = extractDataFromPath(authorizationRequestPayload, '$..vp_token.presentation_definition_uri') if (vpTokens && vpTokens.length && vpTokenRefs && vpTokenRefs.length) { - throw new Error(SIOPErrors.REQUEST_CLAIMS_PRESENTATION_DEFINITION_BY_REF_AND_VALUE_NON_EXCLUSIVE) + throw new Error(SIOPErrors.REQUEST_CLAIMS_PRESENTATION_NON_EXCLUSIVE) } if (vpTokens && vpTokens.length) { vpTokens.forEach((vpToken: PresentationDefinitionV1 | PresentationDefinitionV2) => { @@ -253,7 +253,7 @@ export class PresentationExchange { const hasPD = (definitions && definitions.length > 0) || (definitionsFromList && definitionsFromList.length > 0) const hasPdRef = (definitionRefs && definitionRefs.length > 0) || (definitionRefsFromList && definitionRefsFromList.length > 0) if (hasPD && hasPdRef) { - throw new Error(SIOPErrors.REQUEST_CLAIMS_PRESENTATION_DEFINITION_BY_REF_AND_VALUE_NON_EXCLUSIVE) + throw new Error(SIOPErrors.REQUEST_CLAIMS_PRESENTATION_NON_EXCLUSIVE) } if (definitions && definitions.length > 0) { definitions.forEach((definition) => { @@ -397,7 +397,8 @@ export class PresentationExchange { try { verificationResult = await verifyPresentationCallback(presentation as W3CVerifiablePresentation, evaluationResults.value!) } catch (error: unknown) { - throw new Error(SIOPErrors.VERIFIABLE_PRESENTATION_SIGNATURE_NOT_VALID) + const errorMessage = error instanceof Error ? error.message : String(error) + throw new Error(`${SIOPErrors.VERIFIABLE_PRESENTATION_SIGNATURE_NOT_VALID}: ${errorMessage}`) } if (!verificationResult.verified) { diff --git a/packages/siop-oid4vp/lib/authorization-response/index.ts b/packages/siop-oid4vp/lib/authorization-response/index.ts index c8ae3c97..5fff5253 100644 --- a/packages/siop-oid4vp/lib/authorization-response/index.ts +++ b/packages/siop-oid4vp/lib/authorization-response/index.ts @@ -3,3 +3,4 @@ export * from './types' export * from './Payload' export * from './ResponseRegistration' export * from './OpenID4VP' +export * from './Dcql' diff --git a/packages/siop-oid4vp/lib/authorization-response/types.ts b/packages/siop-oid4vp/lib/authorization-response/types.ts index fe442bfc..8f6d3e2e 100644 --- a/packages/siop-oid4vp/lib/authorization-response/types.ts +++ b/packages/siop-oid4vp/lib/authorization-response/types.ts @@ -9,6 +9,7 @@ import { PresentationSubmission, W3CVerifiablePresentation, } from '@sphereon/ssi-types' +import { DcqlQuery } from 'dcql' import { ResponseMode, @@ -41,6 +42,7 @@ export interface AuthorizationResponseOpts { tokenType?: string refreshToken?: string presentationExchange?: PresentationExchangeResponseOpts + dcqlResponse?: DcqlResponseOpts isFirstParty?: boolean } @@ -60,13 +62,20 @@ export interface PresentationExchangeResponseOpts { restrictToDIDMethods?: string[] } -export interface PresentationExchangeRequestOpts { - presentationVerificationCallback?: PresentationVerificationCallback +export interface DcqlResponseOpts { + dcqlPresentation: Record | string> } export interface PresentationDefinitionPayloadOpts { presentation_definition?: IPresentationDefinition presentation_definition_uri?: string + dcql_query?: never +} + +export interface DcqlQueryPayloadOpts { + dcql_query?: string + presentation_definition?: never + presentation_definition_uri?: never } export interface PresentationDefinitionWithLocation { @@ -96,7 +105,7 @@ export type PresentationVerificationResult = { verified: boolean; reason?: strin export type PresentationVerificationCallback = ( args: W3CVerifiablePresentation | CompactSdJwtVc | MdocOid4vpIssuerSigned, - presentationSubmission: PresentationSubmission, + presentationSubmission?: PresentationSubmission, ) => Promise export type PresentationSignCallback = (args: PresentationSignCallBackParams) => Promise @@ -109,6 +118,7 @@ export interface VerifyAuthorizationResponseOpts { nonce?: string // To verify the response against the supplied nonce state?: string // To verify the response against the supplied state presentationDefinitions?: PresentationDefinitionWithLocation | PresentationDefinitionWithLocation[] // The presentation definitions to match against VPs in the response + dcqlQuery?: DcqlQuery audience?: string // The audience/redirect_uri restrictToFormats?: Format // Further restrict to certain VC formats, not expressed in the presentation definition restrictToDIDMethods?: string[] diff --git a/packages/siop-oid4vp/lib/helpers/SIOPSpecVersion.ts b/packages/siop-oid4vp/lib/helpers/SIOPSpecVersion.ts index 2e0d9d77..5e41e4e8 100644 --- a/packages/siop-oid4vp/lib/helpers/SIOPSpecVersion.ts +++ b/packages/siop-oid4vp/lib/helpers/SIOPSpecVersion.ts @@ -29,7 +29,8 @@ function isID1Payload(authorizationRequest: AuthorizationRequestPayload) { !authorizationRequest.client_metadata_uri && !authorizationRequest.client_metadata && !authorizationRequest.presentation_definition && - !authorizationRequest.presentation_definition_uri + !authorizationRequest.presentation_definition_uri && + !authorizationRequest.dcql_query ) } diff --git a/packages/siop-oid4vp/lib/id-token/IDToken.ts b/packages/siop-oid4vp/lib/id-token/IDToken.ts index ac142218..d94d36bb 100644 --- a/packages/siop-oid4vp/lib/id-token/IDToken.ts +++ b/packages/siop-oid4vp/lib/id-token/IDToken.ts @@ -99,7 +99,7 @@ export class IDToken { const did = jwtIssuer.didUrl.split('#')[0] this._payload.sub = did - const issuer = this._responseOpts.registration.issuer || this._payload.iss + const issuer = this._responseOpts.registration?.issuer || this._payload.iss if (!issuer || !(issuer.includes(ResponseIss.SELF_ISSUED_V2) || issuer === this._payload.sub)) { throw new Error(SIOPErrors.NO_SELF_ISSUED_ISS) } diff --git a/packages/siop-oid4vp/lib/op/OP.ts b/packages/siop-oid4vp/lib/op/OP.ts index 041e2637..29aa2357 100644 --- a/packages/siop-oid4vp/lib/op/OP.ts +++ b/packages/siop-oid4vp/lib/op/OP.ts @@ -10,6 +10,7 @@ import { AuthorizationResponse, AuthorizationResponseOpts, AuthorizationResponseWithCorrelationId, + DcqlResponseOpts, PresentationExchangeResponseOpts, } from '../authorization-response' import { encodeJsonAsURI, post } from '../helpers' @@ -24,7 +25,7 @@ import { RegisterEventListener, RequestObjectPayload, ResponseIss, - ResponseMode, + ResponseMode, RPRegistrationMetadataPayload, SIOPErrors, SupportedVersion, UrlEncodingFormat, @@ -106,7 +107,8 @@ export class OP { issuer?: ResponseIss | string verification?: Verification presentationExchange?: PresentationExchangeResponseOpts - isFirstParty?: boolean + dcqlResponse?: DcqlResponseOpts + isFirstParty?: boolean }, ): Promise { if ( @@ -276,9 +278,10 @@ export class OP { * Create an Authentication Request Payload from a URI string * * @param encodedUri + * @param rpRegistrationMetadata */ - public async parseAuthorizationRequestURI(encodedUri: string): Promise { - const { scheme, requestObjectJwt, authorizationRequestPayload, registrationMetadata } = await URI.parseAndResolve(encodedUri) + public async parseAuthorizationRequestURI(encodedUri: string, rpRegistrationMetadata?: RPRegistrationMetadataPayload): Promise { + const { scheme, requestObjectJwt, authorizationRequestPayload, registrationMetadata } = await URI.parseAndResolve(encodedUri, rpRegistrationMetadata) return { encodedUri, @@ -296,6 +299,7 @@ export class OP { issuer?: IIssuerId | ResponseIss audience?: string presentationExchange?: PresentationExchangeResponseOpts + dcqlResponse?: DcqlResponseOpts }): AuthorizationResponseOpts { const version = opts.version ?? this._createResponseOptions.version let issuer = opts.issuer ?? this._createResponseOptions?.registration?.issuer @@ -310,11 +314,14 @@ export class OP { } // We are taking the whole presentationExchange object from a certain location const presentationExchange = opts.presentationExchange ?? this._createResponseOptions.presentationExchange + const dcqlQuery = opts.dcqlResponse ?? this._createResponseOptions.dcqlResponse + const responseURI = opts.audience ?? this._createResponseOptions.responseURI return { ...this._createResponseOptions, ...opts, ...(presentationExchange && { presentationExchange }), + ...(dcqlQuery && { dcqlQuery }), registration: { ...this._createResponseOptions?.registration, issuer }, responseURI, responseURIType: diff --git a/packages/siop-oid4vp/lib/request-object/Payload.ts b/packages/siop-oid4vp/lib/request-object/Payload.ts index e641b03c..408d8575 100644 --- a/packages/siop-oid4vp/lib/request-object/Payload.ts +++ b/packages/siop-oid4vp/lib/request-object/Payload.ts @@ -18,7 +18,7 @@ export const createRequestObjectPayload = async (opts: CreateAuthorizationReques const state = getState(payload.state) const registration = await createRequestRegistration(opts.clientMetadata, opts) - const claims = createPresentationDefinitionClaimsProperties(payload.claims) + const claims = await createPresentationDefinitionClaimsProperties(payload.claims) const metadataKey = opts.version >= SupportedVersion.SIOPv2_D11.valueOf() ? 'client_metadata' : 'registration' const clientId = payload.client_id ?? registration.payload[metadataKey]?.client_id @@ -47,8 +47,9 @@ export const createRequestObjectPayload = async (opts: CreateAuthorizationReques state, ...registration.payload, claims, - presentation_definition_uri: payload.presentation_definition_uri, - presentation_definition: payload.presentation_definition, + ...(payload.presentation_definition_uri && { presentation_definition_uri: payload.presentation_definition_uri }), + ...(payload.presentation_definition && { presentation_definition: payload.presentation_definition }), + ...(payload.dcql_query && { dcql_query: payload.dcql_query }), client_metadata: payload.client_metadata, iat, nbf, diff --git a/packages/siop-oid4vp/lib/rp/RP.ts b/packages/siop-oid4vp/lib/rp/RP.ts index 2ead48d0..f3c1cceb 100644 --- a/packages/siop-oid4vp/lib/rp/RP.ts +++ b/packages/siop-oid4vp/lib/rp/RP.ts @@ -8,6 +8,7 @@ import { } from '@sphereon/jarm' import { decodeProtectedHeader, JwtIssuer, uuidv4 } from '@sphereon/oid4vc-common' import { Hasher } from '@sphereon/ssi-types' +import { DcqlQuery } from 'dcql' import { AuthorizationRequest, @@ -21,6 +22,7 @@ import { import { mergeVerificationOpts } from '../authorization-request/Opts' import { AuthorizationResponse, + extractPresentationsFromDcqlVpToken, extractPresentationsFromVpToken, PresentationDefinitionWithLocation, VerifyAuthorizationResponseOpts, @@ -172,7 +174,10 @@ export class RP { }, ) - const presentations = await extractPresentationsFromVpToken(validatedResponse.authResponseParams.vp_token, { hasher }) + const presentations = validatedResponse.authRequestParams.dcql_query + ? extractPresentationsFromDcqlVpToken(validatedResponse.authResponseParams.vp_token as string, { hasher }) + : extractPresentationsFromVpToken(validatedResponse.authResponseParams.vp_token, { hasher }) + const mdocVerifiablePresentations = (Array.isArray(presentations) ? presentations : [presentations]).filter((p) => p.format === 'mso_mdoc') if (mdocVerifiablePresentations.length) { @@ -206,6 +211,7 @@ export class RP { nonce?: string verification?: Verification presentationDefinitions?: PresentationDefinitionWithLocation | PresentationDefinitionWithLocation[] + dcqlQuery?: DcqlQuery }, ): Promise { const state = opts?.state || this.verifyResponseOptions.state @@ -380,6 +386,7 @@ export class RP { verification?: Verification audience?: string presentationDefinitions?: PresentationDefinitionWithLocation | PresentationDefinitionWithLocation[] + dcqlQuery?: DcqlQuery }, ): Promise { let correlationId = opts?.correlationId ?? this._verifyResponseOptions.correlationId @@ -412,6 +419,16 @@ export class RP { } } + const hasPD = (this._verifyResponseOptions.presentationDefinitions !== undefined && this._verifyResponseOptions.presentationDefinitions !== null || (Array.isArray(this._verifyResponseOptions.presentationDefinitions) && this._verifyResponseOptions.presentationDefinitions.length > 0)) || + (opts.presentationDefinitions !== undefined && opts.presentationDefinitions !== null || (Array.isArray(opts.presentationDefinitions) && opts.presentationDefinitions.length > 0)) + const hasDcql = (this._verifyResponseOptions.dcqlQuery !== undefined && this._verifyResponseOptions.dcqlQuery !== null) || (opts.dcqlQuery !== undefined && opts.dcqlQuery !== null) + + if (hasPD && hasDcql) { + throw Error(`Only Presentation Definitions or DCQL is required`) + } else if (!hasPD && !hasDcql) { + throw Error(`Either a Presentation Definition or DCQL is required`) + } + return { ...this._verifyResponseOptions, verifyJwtCallback: this._verifyResponseOptions.verifyJwtCallback, @@ -421,7 +438,13 @@ export class RP { state, nonce, verification: mergeVerificationOpts(this._verifyResponseOptions, opts), - presentationDefinitions: opts?.presentationDefinitions ?? this._verifyResponseOptions.presentationDefinitions, + ...(opts?.presentationDefinitions && !opts?.dcqlQuery && { + presentationDefinitions: this._verifyResponseOptions.presentationDefinitions ?? opts?.presentationDefinitions + }), + ...(opts?.dcqlQuery /*&& !opts?.presentationDefinitions */&& { // FIXME presentationDefinitions will be there until we fix the OID4VC-DEMO, it wants a PD purpose field for the screens + + dcqlQuery: this._verifyResponseOptions.dcqlQuery ?? opts?.dcqlQuery + }) } } diff --git a/packages/siop-oid4vp/lib/rp/RPBuilder.ts b/packages/siop-oid4vp/lib/rp/RPBuilder.ts index 09dd83c6..ddbb65fd 100644 --- a/packages/siop-oid4vp/lib/rp/RPBuilder.ts +++ b/packages/siop-oid4vp/lib/rp/RPBuilder.ts @@ -2,6 +2,7 @@ import { EventEmitter } from 'events' import { IPresentationDefinition } from '@sphereon/pex' import { Hasher } from '@sphereon/ssi-types' +import { DcqlQuery } from 'dcql' import { PropertyTarget, PropertyTargets } from '../authorization-request' import { PresentationVerificationCallback } from '../authorization-response' @@ -24,6 +25,7 @@ import { assignIfAuth, assignIfRequestObject, isTarget, isTargetOrNoTargets } fr import { RP } from './RP' import { IRPSessionManager } from './types' + export class RPBuilder { requestObjectBy: ObjectBy createJwtCallback?: CreateJwtCallback @@ -226,55 +228,88 @@ export class RPBuilder { return this } - withPresentationDefinition(definitionOpts: { definition: IPresentationDefinition; definitionUri?: string }, targets?: PropertyTargets): RPBuilder { + withDcqlQuery(dcqlQuery: DcqlQuery | string, targets?: PropertyTargets): RPBuilder { + if (this.getSupportedRequestVersion() >= SupportedVersion.SIOPv2_D12_OID4VP_D20) { + this._authorizationRequestPayload.dcql_query = assignIfAuth( + { + propertyValue: typeof dcqlQuery === 'string' ? dcqlQuery : JSON.stringify(dcqlQuery), + targets + }, + false + ) + this._requestObjectPayload.dcql_query = assignIfRequestObject( + { + propertyValue: typeof dcqlQuery === 'string' ? dcqlQuery : JSON.stringify(dcqlQuery), + targets + }, + true + ) + + // FIXME SPRIND-144 we need to find a way in the config to select dcql vs PD without breaking OID4VC-DEMO + this._authorizationRequestPayload.presentation_definition = undefined; + this._authorizationRequestPayload.presentation_definition_uri = undefined; + this._requestObjectPayload.presentation_definition = undefined; + this._requestObjectPayload.presentation_definition_uri = undefined; + } + return this + } + + withPresentationDefinition(definitionOpts: { + definition: IPresentationDefinition; + definitionUri?: string + }, targets?: PropertyTargets): RPBuilder { + if(this._authorizationRequestPayload.dcql_query) { + return this + } + const { definition, definitionUri } = definitionOpts if (this.getSupportedRequestVersion() < SupportedVersion.SIOPv2_D11) { const definitionProperties = { presentation_definition: definition, - presentation_definition_uri: definitionUri, + presentation_definition_uri: definitionUri } const vp_token = { ...definitionProperties } if (isTarget(PropertyTarget.AUTHORIZATION_REQUEST, targets)) { this._authorizationRequestPayload.claims = { ...(this._authorizationRequestPayload.claims ? this._authorizationRequestPayload.claims : {}), - vp_token: vp_token, + vp_token: vp_token } } if (isTargetOrNoTargets(PropertyTarget.REQUEST_OBJECT, targets)) { this._requestObjectPayload.claims = { ...(this._requestObjectPayload.claims ? this._requestObjectPayload.claims : {}), - vp_token: vp_token, + vp_token: vp_token } } } else { this._authorizationRequestPayload.presentation_definition = assignIfAuth( { propertyValue: definition, - targets, + targets }, - false, + false ) this._authorizationRequestPayload.presentation_definition_uri = assignIfAuth( { propertyValue: definitionUri, - targets, + targets }, - true, + true ) this._requestObjectPayload.presentation_definition = assignIfRequestObject( { propertyValue: definition, - targets, + targets }, - true, + true ) this._requestObjectPayload.presentation_definition_uri = assignIfRequestObject( { propertyValue: definitionUri, - targets, + targets }, - true, + true ) } return this diff --git a/packages/siop-oid4vp/lib/schemas/AuthorizationRequestPayloadVD11.schema.ts b/packages/siop-oid4vp/lib/schemas/AuthorizationRequestPayloadVD11.schema.ts index 020c0299..0fb7ce3a 100644 --- a/packages/siop-oid4vp/lib/schemas/AuthorizationRequestPayloadVD11.schema.ts +++ b/packages/siop-oid4vp/lib/schemas/AuthorizationRequestPayloadVD11.schema.ts @@ -699,9 +699,6 @@ export const AuthorizationRequestPayloadVD11SchemaObj = { "type": "string" } }, - "required": [ - "type" - ], "additionalProperties": false }, "OneOfNumberStringBoolean": { @@ -934,81 +931,15 @@ export const AuthorizationRequestPayloadVD11SchemaObj = { "type": "string" }, "contains": { - "$ref": "#/definitions/FilterV2Base" - }, - "items": { - "$ref": "#/definitions/FilterV2BaseItems" - } - }, - "required": [ - "type" - ], - "additionalProperties": false - }, - "FilterV2Base": { - "type": "object", - "properties": { - "const": { - "$ref": "#/definitions/OneOfNumberStringBoolean" - }, - "enum": { - "type": "array", - "items": { - "$ref": "#/definitions/OneOfNumberStringBoolean" - } - }, - "exclusiveMinimum": { - "$ref": "#/definitions/OneOfNumberString" - }, - "exclusiveMaximum": { - "$ref": "#/definitions/OneOfNumberString" - }, - "format": { - "type": "string" - }, - "formatMaximum": { - "type": "string" - }, - "formatMinimum": { - "type": "string" - }, - "formatExclusiveMaximum": { - "type": "string" - }, - "formatExclusiveMinimum": { - "type": "string" - }, - "minLength": { - "type": "number" - }, - "maxLength": { - "type": "number" - }, - "minimum": { - "$ref": "#/definitions/OneOfNumberString" - }, - "maximum": { - "$ref": "#/definitions/OneOfNumberString" - }, - "not": { - "type": "object" - }, - "pattern": { - "type": "string" - }, - "type": { - "type": "string" - }, - "contains": { - "$ref": "#/definitions/FilterV2Base" + "$ref": "#/definitions/FilterV2" }, "items": { - "$ref": "#/definitions/FilterV2BaseItems" + "$ref": "#/definitions/FilterV2Items" } }, "additionalProperties": false }, - "FilterV2BaseItems": { + "FilterV2Items": { "type": "object", "properties": { "const": { @@ -1063,15 +994,12 @@ export const AuthorizationRequestPayloadVD11SchemaObj = { "type": "string" }, "contains": { - "$ref": "#/definitions/FilterV2Base" + "$ref": "#/definitions/FilterV2" }, "items": { - "$ref": "#/definitions/FilterV2BaseItems" + "$ref": "#/definitions/FilterV2Items" } }, - "required": [ - "type" - ], "additionalProperties": false } } diff --git a/packages/siop-oid4vp/lib/schemas/AuthorizationRequestPayloadVD12OID4VPD18.schema.ts b/packages/siop-oid4vp/lib/schemas/AuthorizationRequestPayloadVD12OID4VPD18.schema.ts index bb6429d1..3191b080 100644 --- a/packages/siop-oid4vp/lib/schemas/AuthorizationRequestPayloadVD12OID4VPD18.schema.ts +++ b/packages/siop-oid4vp/lib/schemas/AuthorizationRequestPayloadVD12OID4VPD18.schema.ts @@ -705,9 +705,6 @@ export const AuthorizationRequestPayloadVD12OID4VPD18SchemaObj = { "type": "string" } }, - "required": [ - "type" - ], "additionalProperties": false }, "OneOfNumberStringBoolean": { @@ -940,81 +937,15 @@ export const AuthorizationRequestPayloadVD12OID4VPD18SchemaObj = { "type": "string" }, "contains": { - "$ref": "#/definitions/FilterV2Base" - }, - "items": { - "$ref": "#/definitions/FilterV2BaseItems" - } - }, - "required": [ - "type" - ], - "additionalProperties": false - }, - "FilterV2Base": { - "type": "object", - "properties": { - "const": { - "$ref": "#/definitions/OneOfNumberStringBoolean" - }, - "enum": { - "type": "array", - "items": { - "$ref": "#/definitions/OneOfNumberStringBoolean" - } - }, - "exclusiveMinimum": { - "$ref": "#/definitions/OneOfNumberString" - }, - "exclusiveMaximum": { - "$ref": "#/definitions/OneOfNumberString" - }, - "format": { - "type": "string" - }, - "formatMaximum": { - "type": "string" - }, - "formatMinimum": { - "type": "string" - }, - "formatExclusiveMaximum": { - "type": "string" - }, - "formatExclusiveMinimum": { - "type": "string" - }, - "minLength": { - "type": "number" - }, - "maxLength": { - "type": "number" - }, - "minimum": { - "$ref": "#/definitions/OneOfNumberString" - }, - "maximum": { - "$ref": "#/definitions/OneOfNumberString" - }, - "not": { - "type": "object" - }, - "pattern": { - "type": "string" - }, - "type": { - "type": "string" - }, - "contains": { - "$ref": "#/definitions/FilterV2Base" + "$ref": "#/definitions/FilterV2" }, "items": { - "$ref": "#/definitions/FilterV2BaseItems" + "$ref": "#/definitions/FilterV2Items" } }, "additionalProperties": false }, - "FilterV2BaseItems": { + "FilterV2Items": { "type": "object", "properties": { "const": { @@ -1069,15 +1000,12 @@ export const AuthorizationRequestPayloadVD12OID4VPD18SchemaObj = { "type": "string" }, "contains": { - "$ref": "#/definitions/FilterV2Base" + "$ref": "#/definitions/FilterV2" }, "items": { - "$ref": "#/definitions/FilterV2BaseItems" + "$ref": "#/definitions/FilterV2Items" } }, - "required": [ - "type" - ], "additionalProperties": false }, "ClientIdSchemeOID4VPD18": { diff --git a/packages/siop-oid4vp/lib/schemas/AuthorizationRequestPayloadVD12OID4VPD20.schema.ts b/packages/siop-oid4vp/lib/schemas/AuthorizationRequestPayloadVD12OID4VPD20.schema.ts index 3a74ad22..eeee9e8a 100644 --- a/packages/siop-oid4vp/lib/schemas/AuthorizationRequestPayloadVD12OID4VPD20.schema.ts +++ b/packages/siop-oid4vp/lib/schemas/AuthorizationRequestPayloadVD12OID4VPD20.schema.ts @@ -122,6 +122,9 @@ export const AuthorizationRequestPayloadVD12OID4VPD20SchemaObj = { }, "response_uri": { "type": "string" + }, + "dcql_query": { + "type": "string" } } }, @@ -705,9 +708,6 @@ export const AuthorizationRequestPayloadVD12OID4VPD20SchemaObj = { "type": "string" } }, - "required": [ - "type" - ], "additionalProperties": false }, "OneOfNumberStringBoolean": { @@ -940,81 +940,15 @@ export const AuthorizationRequestPayloadVD12OID4VPD20SchemaObj = { "type": "string" }, "contains": { - "$ref": "#/definitions/FilterV2Base" - }, - "items": { - "$ref": "#/definitions/FilterV2BaseItems" - } - }, - "required": [ - "type" - ], - "additionalProperties": false - }, - "FilterV2Base": { - "type": "object", - "properties": { - "const": { - "$ref": "#/definitions/OneOfNumberStringBoolean" - }, - "enum": { - "type": "array", - "items": { - "$ref": "#/definitions/OneOfNumberStringBoolean" - } - }, - "exclusiveMinimum": { - "$ref": "#/definitions/OneOfNumberString" - }, - "exclusiveMaximum": { - "$ref": "#/definitions/OneOfNumberString" - }, - "format": { - "type": "string" - }, - "formatMaximum": { - "type": "string" - }, - "formatMinimum": { - "type": "string" - }, - "formatExclusiveMaximum": { - "type": "string" - }, - "formatExclusiveMinimum": { - "type": "string" - }, - "minLength": { - "type": "number" - }, - "maxLength": { - "type": "number" - }, - "minimum": { - "$ref": "#/definitions/OneOfNumberString" - }, - "maximum": { - "$ref": "#/definitions/OneOfNumberString" - }, - "not": { - "type": "object" - }, - "pattern": { - "type": "string" - }, - "type": { - "type": "string" - }, - "contains": { - "$ref": "#/definitions/FilterV2Base" + "$ref": "#/definitions/FilterV2" }, "items": { - "$ref": "#/definitions/FilterV2BaseItems" + "$ref": "#/definitions/FilterV2Items" } }, "additionalProperties": false }, - "FilterV2BaseItems": { + "FilterV2Items": { "type": "object", "properties": { "const": { @@ -1069,15 +1003,12 @@ export const AuthorizationRequestPayloadVD12OID4VPD20SchemaObj = { "type": "string" }, "contains": { - "$ref": "#/definitions/FilterV2Base" + "$ref": "#/definitions/FilterV2" }, "items": { - "$ref": "#/definitions/FilterV2BaseItems" + "$ref": "#/definitions/FilterV2Items" } }, - "required": [ - "type" - ], "additionalProperties": false }, "ClientIdSchemeOID4VPD20": { diff --git a/packages/siop-oid4vp/lib/schemas/AuthorizationRequestPayloadVID1.schema.ts b/packages/siop-oid4vp/lib/schemas/AuthorizationRequestPayloadVID1.schema.ts index 0b78c774..61931b35 100644 --- a/packages/siop-oid4vp/lib/schemas/AuthorizationRequestPayloadVID1.schema.ts +++ b/packages/siop-oid4vp/lib/schemas/AuthorizationRequestPayloadVID1.schema.ts @@ -414,6 +414,9 @@ export const AuthorizationRequestPayloadVID1SchemaObj = { }, "presentation_definition_uri": { "type": "string" + }, + "dcql_query": { + "type": "string" } }, "additionalProperties": false @@ -701,9 +704,6 @@ export const AuthorizationRequestPayloadVID1SchemaObj = { "type": "string" } }, - "required": [ - "type" - ], "additionalProperties": false }, "OneOfNumberStringBoolean": { @@ -936,81 +936,15 @@ export const AuthorizationRequestPayloadVID1SchemaObj = { "type": "string" }, "contains": { - "$ref": "#/definitions/FilterV2Base" - }, - "items": { - "$ref": "#/definitions/FilterV2BaseItems" - } - }, - "required": [ - "type" - ], - "additionalProperties": false - }, - "FilterV2Base": { - "type": "object", - "properties": { - "const": { - "$ref": "#/definitions/OneOfNumberStringBoolean" - }, - "enum": { - "type": "array", - "items": { - "$ref": "#/definitions/OneOfNumberStringBoolean" - } - }, - "exclusiveMinimum": { - "$ref": "#/definitions/OneOfNumberString" - }, - "exclusiveMaximum": { - "$ref": "#/definitions/OneOfNumberString" - }, - "format": { - "type": "string" - }, - "formatMaximum": { - "type": "string" - }, - "formatMinimum": { - "type": "string" - }, - "formatExclusiveMaximum": { - "type": "string" - }, - "formatExclusiveMinimum": { - "type": "string" - }, - "minLength": { - "type": "number" - }, - "maxLength": { - "type": "number" - }, - "minimum": { - "$ref": "#/definitions/OneOfNumberString" - }, - "maximum": { - "$ref": "#/definitions/OneOfNumberString" - }, - "not": { - "type": "object" - }, - "pattern": { - "type": "string" - }, - "type": { - "type": "string" - }, - "contains": { - "$ref": "#/definitions/FilterV2Base" + "$ref": "#/definitions/FilterV2" }, "items": { - "$ref": "#/definitions/FilterV2BaseItems" + "$ref": "#/definitions/FilterV2Items" } }, "additionalProperties": false }, - "FilterV2BaseItems": { + "FilterV2Items": { "type": "object", "properties": { "const": { @@ -1065,15 +999,12 @@ export const AuthorizationRequestPayloadVID1SchemaObj = { "type": "string" }, "contains": { - "$ref": "#/definitions/FilterV2Base" + "$ref": "#/definitions/FilterV2" }, "items": { - "$ref": "#/definitions/FilterV2BaseItems" + "$ref": "#/definitions/FilterV2Items" } }, - "required": [ - "type" - ], "additionalProperties": false } } diff --git a/packages/siop-oid4vp/lib/schemas/AuthorizationResponseOpts.schema.ts b/packages/siop-oid4vp/lib/schemas/AuthorizationResponseOpts.schema.ts index 3c5afdff..4cb23d5c 100644 --- a/packages/siop-oid4vp/lib/schemas/AuthorizationResponseOpts.schema.ts +++ b/packages/siop-oid4vp/lib/schemas/AuthorizationResponseOpts.schema.ts @@ -53,6 +53,9 @@ export const AuthorizationResponseOptsSchemaObj = { "presentationExchange": { "$ref": "#/definitions/PresentationExchangeResponseOpts" }, + "dcqlResponse": { + "$ref": "#/definitions/DcqlResponseOpts" + }, "isFirstParty": { "type": "boolean" } @@ -2338,6 +2341,29 @@ export const AuthorizationResponseOptsSchemaObj = { "id_token", "token_response" ] + }, + "DcqlResponseOpts": { + "type": "object", + "properties": { + "dcqlPresentation": { + "type": "object", + "additionalProperties": { + "anyOf": [ + { + "type": "object", + "additionalProperties": {} + }, + { + "type": "string" + } + ] + } + } + }, + "required": [ + "dcqlPresentation" + ], + "additionalProperties": false } } }; \ No newline at end of file diff --git a/packages/siop-oid4vp/lib/types/Errors.ts b/packages/siop-oid4vp/lib/types/Errors.ts index e88c5171..82a6ba73 100644 --- a/packages/siop-oid4vp/lib/types/Errors.ts +++ b/packages/siop-oid4vp/lib/types/Errors.ts @@ -38,7 +38,7 @@ enum SIOPErrors { REFERENCE_URI_NO_PAYLOAD = 'referenceUri specified, but object to host there is not present', NO_SELF_ISSUED_ISS = 'The Response Token Issuer Claim (iss) MUST start with https://self-isued.me/v2', REGISTRATION_NOT_SET = 'Registration metadata not set.', - REQUEST_CLAIMS_PRESENTATION_DEFINITION_BY_REF_AND_VALUE_NON_EXCLUSIVE = "Request claims can't have both 'presentation_definition' and 'presentation_definition_uri'", + REQUEST_CLAIMS_PRESENTATION_NON_EXCLUSIVE = "Request claims can't have multiple of 'presentation_definition', 'presentation_definition_uri' and 'dcql_query", REQUEST_CLAIMS_PRESENTATION_DEFINITION_NOT_VALID = 'Presentation definition in the request claims is not valid', REQUEST_OBJECT_TYPE_NOT_SET = 'Request object type is not set.', RESPONSE_OPTS_PRESENTATIONS_SUBMISSION_IS_NOT_VALID = 'presentation_submission object inside the response opts vp should be valid', diff --git a/packages/siop-oid4vp/lib/types/SIOP.types.ts b/packages/siop-oid4vp/lib/types/SIOP.types.ts index 3314f5b1..3c1cf54b 100644 --- a/packages/siop-oid4vp/lib/types/SIOP.types.ts +++ b/packages/siop-oid4vp/lib/types/SIOP.types.ts @@ -1,6 +1,10 @@ // noinspection JSUnusedGlobalSymbols import { JarmClientMetadata } from '@sphereon/jarm' -import { DynamicRegistrationClientMetadata, JWKS, SigningAlgo } from '@sphereon/oid4vc-common' +import { + DynamicRegistrationClientMetadata, + JWKS, + SigningAlgo +} from '@sphereon/oid4vc-common' import { Format, PresentationDefinitionV1, PresentationDefinitionV2 } from '@sphereon/pex-models' import { AdditionalClaims, @@ -11,22 +15,29 @@ import { PresentationSubmission, W3CVerifiableCredential, W3CVerifiablePresentation, - WrappedVerifiablePresentation, + WrappedVerifiablePresentation } from '@sphereon/ssi-types' +import { DcqlQuery } from 'dcql' -import { AuthorizationRequest, CreateAuthorizationRequestOpts, PropertyTargets, VerifyAuthorizationRequestOpts } from '../authorization-request' +import { + AuthorizationRequest, + CreateAuthorizationRequestOpts, + PropertyTargets, + VerifyAuthorizationRequestOpts +} from '../authorization-request' import { AuthorizationResponse, AuthorizationResponseOpts, PresentationDefinitionWithLocation, PresentationVerificationCallback, - VerifyAuthorizationResponseOpts, + VerifyAuthorizationResponseOpts } from '../authorization-response' -import { JwksMetadataParams } from '../helpers/ExtractJwks' +import { JwksMetadataParams } from '../helpers' import { RequestObject, RequestObjectOpts } from '../request-object' import { IRPSessionManager } from '../rp' import { JWTPayload, VerifiedJWT } from './index' + export const DEFAULT_EXPIRATION_TIME = 10 * 60 // https://openid.net/specs/openid-connect-core-1_0.html#RequestObject @@ -99,6 +110,7 @@ export interface AuthorizationRequestPayloadVD12OID4VPD20 presentation_definition_uri?: string client_id_scheme?: ClientIdSchemeOID4VPD20 response_uri?: string // New since OID4VP18 OPTIONAL. The Response URI to which the Wallet MUST send the Authorization Response using an HTTPS POST request as defined by the Response Mode direct_post. The Response URI receives all Authorization Response parameters as defined by the respective Response Type. When the response_uri parameter is present, the redirect_uri Authorization Request parameter MUST NOT be present. If the redirect_uri Authorization Request parameter is present when the Response Mode is direct_post, the Wallet MUST return an invalid_request Authorization Response error. + dcql_query?: string } export type ClientIdSchemeOID4VPD18 = 'pre-registered' | 'redirect_uri' | 'entity_id' | 'did' @@ -140,6 +152,7 @@ export interface VerifiedAuthorizationRequest extends Partial { requestObject?: RequestObject // The Request object registrationMetadataPayload: RPRegistrationMetadataPayload presentationDefinitions?: PresentationDefinitionWithLocation[] // The optional presentation definition objects that the RP requests + dcqlQuery?: DcqlQuery verifyOpts: VerifyAuthorizationRequestOpts // The verification options for the authentication request versions: SupportedVersion[] } @@ -165,6 +178,8 @@ export interface IDTokenPayload extends JWTPayload { } } +export type EncodedDcqlQueryVpToken = string + export interface AuthorizationResponsePayload { access_token?: string token_type?: string @@ -177,6 +192,7 @@ export interface AuthorizationResponsePayload { | W3CVerifiablePresentation | CompactSdJwtVc | MdocOid4vpMdocVpToken + | EncodedDcqlQueryVpToken presentation_submission?: PresentationSubmission verifiedData?: IPresentation | AdditionalClaims is_first_party?: boolean @@ -192,6 +208,7 @@ export interface IdTokenClaimPayload { export interface VpTokenClaimPayload { presentation_definition?: PresentationDefinitionV1 | PresentationDefinitionV2 presentation_definition_uri?: string + dcql_query?: string } export interface ClaimPayloadCommon { @@ -511,6 +528,12 @@ export interface VerifiedIDToken { verifyOpts: VerifyAuthorizationResponseOpts } +export interface VerifiedOpenID4VPSubmissionDcql { + dcqlQuery: DcqlQuery + presentation: { [credentialQueryId: string]: WrappedVerifiablePresentation } + nonce?: string +} + export interface VerifiedOpenID4VPSubmission { submissionData: PresentationSubmission presentationDefinitions: PresentationDefinitionWithLocation[] @@ -524,6 +547,7 @@ export interface VerifiedAuthorizationResponse { authorizationResponse: AuthorizationResponse oid4vpSubmission?: VerifiedOpenID4VPSubmission + oid4vpSubmissionDcql?: VerifiedOpenID4VPSubmissionDcql nonce?: string state: string diff --git a/packages/siop-oid4vp/package.json b/packages/siop-oid4vp/package.json index 20a7c680..4f62f1bd 100644 --- a/packages/siop-oid4vp/package.json +++ b/packages/siop-oid4vp/package.json @@ -18,8 +18,9 @@ "@sphereon/jarm": "workspace:*", "@sphereon/oid4vc-common": "workspace:*", "@sphereon/pex": "5.0.0-unstable.28", - "@sphereon/pex-models": "^2.3.1", - "@sphereon/ssi-types": "0.30.2-feature.mdoc.funke2.367", + "dcql": "0.2.19", + "@sphereon/pex-models": "^2.3.2", + "@sphereon/ssi-types": "0.32.1-feature.VDX.341.53", "cross-fetch": "^4.0.0", "debug": "^4.3.5", "events": "^3.3.0", @@ -43,6 +44,9 @@ "@digitalcredentials/ed25519-signature-2020": "^3.0.2", "@digitalcredentials/jsonld-signatures": "^9.3.2", "@digitalcredentials/vc": "^6.0.0", + "@sd-jwt/crypto-nodejs": "0.7.2", + "@sd-jwt/sd-jwt-vc": "0.7.2", + "@sd-jwt/types": "0.7.2", "@sphereon/wellknown-dids-client": "^0.1.3", "@sphereon/did-uni-client": "^0.6.2", "@transmute/did-key-ed25519": "^0.3.0-unstable.10", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index b7f130b1..9991ff9a 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -5,7 +5,8 @@ settings: excludeLinksFromLockfile: false overrides: - '@sphereon/ssi-types': 0.30.2-feature.mdoc.funke2.367 + '@sphereon/ssi-types': 0.32.1-feature.VDX.341.53 + dcql: 0.2.19 node-fetch: 2.6.12 importers: @@ -76,8 +77,8 @@ importers: specifier: workspace:* version: link:../issuer '@sphereon/ssi-types': - specifier: 0.30.2-feature.mdoc.funke2.367 - version: 0.30.2-feature.mdoc.funke2.367 + specifier: 0.32.1-feature.VDX.341.53 + version: 0.32.1-feature.VDX.341.53 jose: specifier: ^4.10.0 version: 4.15.9 @@ -119,8 +120,8 @@ importers: specifier: workspace:* version: link:../oid4vci-common '@sphereon/ssi-types': - specifier: 0.30.2-feature.mdoc.funke2.367 - version: 0.30.2-feature.mdoc.funke2.367 + specifier: 0.32.1-feature.VDX.341.53 + version: 0.32.1-feature.VDX.341.53 cross-fetch: specifier: ^3.1.8 version: 3.1.8(encoding@0.1.13) @@ -204,8 +205,8 @@ importers: packages/common: dependencies: '@sphereon/ssi-types': - specifier: 0.30.2-feature.mdoc.funke2.367 - version: 0.30.2-feature.mdoc.funke2.367 + specifier: 0.32.1-feature.VDX.341.53 + version: 0.32.1-feature.VDX.341.53 jwt-decode: specifier: ^4.0.0 version: 4.0.0 @@ -269,8 +270,8 @@ importers: specifier: workspace:* version: link:../oid4vci-common '@sphereon/ssi-types': - specifier: 0.30.2-feature.mdoc.funke2.367 - version: 0.30.2-feature.mdoc.funke2.367 + specifier: 0.32.1-feature.VDX.341.53 + version: 0.32.1-feature.VDX.341.53 awesome-qr: specifier: ^2.1.5-rc.0 version: 2.1.5-rc.0(encoding@0.1.13) @@ -312,8 +313,8 @@ importers: specifier: 0.30.2-feature.mdoc.funke2.367 version: 0.30.2-feature.mdoc.funke2.367 '@sphereon/ssi-types': - specifier: 0.30.2-feature.mdoc.funke2.367 - version: 0.30.2-feature.mdoc.funke2.367 + specifier: 0.32.1-feature.VDX.341.53 + version: 0.32.1-feature.VDX.341.53 awesome-qr: specifier: ^2.1.5-rc.0 version: 2.1.5-rc.0(encoding@0.1.13) @@ -404,8 +405,8 @@ importers: specifier: workspace:* version: link:../common '@sphereon/ssi-types': - specifier: 0.30.2-feature.mdoc.funke2.367 - version: 0.30.2-feature.mdoc.funke2.367 + specifier: 0.32.1-feature.VDX.341.53 + version: 0.32.1-feature.VDX.341.53 cross-fetch: specifier: ^3.1.8 version: 3.1.8(encoding@0.1.13) @@ -450,14 +451,17 @@ importers: specifier: 5.0.0-unstable.28 version: 5.0.0-unstable.28 '@sphereon/pex-models': - specifier: ^2.3.1 - version: 2.3.1 + specifier: ^2.3.2 + version: 2.3.2 '@sphereon/ssi-types': - specifier: 0.30.2-feature.mdoc.funke2.367 - version: 0.30.2-feature.mdoc.funke2.367 + specifier: 0.32.1-feature.VDX.341.53 + version: 0.32.1-feature.VDX.341.53 cross-fetch: specifier: ^4.0.0 version: 4.0.0(encoding@0.1.13) + dcql: + specifier: 0.2.19 + version: 0.2.19(typescript@5.4.5) debug: specifier: ^4.3.5 version: 4.3.7 @@ -519,6 +523,15 @@ importers: '@digitalcredentials/vc': specifier: ^6.0.0 version: 6.0.1(encoding@0.1.13)(expo@48.0.21(@babel/core@7.26.0)(encoding@0.1.13))(react-native@0.71.19(@babel/core@7.26.0)(@babel/preset-env@7.26.0(@babel/core@7.26.0))(encoding@0.1.13)(react@18.3.1))(web-streams-polyfill@3.3.3) + '@sd-jwt/crypto-nodejs': + specifier: 0.7.2 + version: 0.7.2 + '@sd-jwt/sd-jwt-vc': + specifier: 0.7.2 + version: 0.7.2 + '@sd-jwt/types': + specifier: 0.7.2 + version: 0.7.2 '@sphereon/did-uni-client': specifier: ^0.6.2 version: 0.6.3(encoding@0.1.13) @@ -793,14 +806,12 @@ packages: '@babel/plugin-proposal-async-generator-functions@7.20.7': resolution: {integrity: sha512-xMbiLsn/8RK7Wq7VeVytytS2L6qE69bXPB10YCmMdDZbKF4okCqY74pI/jJQ/8U0b/F6NrT2+14b8/P9/3AMGA==} engines: {node: '>=6.9.0'} - deprecated: This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-async-generator-functions instead. peerDependencies: '@babel/core': ^7.0.0-0 '@babel/plugin-proposal-class-properties@7.18.6': resolution: {integrity: sha512-cumfXOF0+nzZrrN8Rf0t7M+tF6sZc7vhQwYQck9q1/5w2OExlD+b4v4RpMJFaV1Z7WcDRgO6FqvxqxGlwo+RHQ==} engines: {node: '>=6.9.0'} - deprecated: This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-class-properties instead. peerDependencies: '@babel/core': ^7.0.0-0 @@ -819,35 +830,30 @@ packages: '@babel/plugin-proposal-export-namespace-from@7.18.9': resolution: {integrity: sha512-k1NtHyOMvlDDFeb9G5PhUXuGj8m/wiwojgQVEhJ/fsVsMCpLyOP4h0uGEjYJKrRI+EVPlb5Jk+Gt9P97lOGwtA==} engines: {node: '>=6.9.0'} - deprecated: This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-export-namespace-from instead. peerDependencies: '@babel/core': ^7.0.0-0 '@babel/plugin-proposal-nullish-coalescing-operator@7.18.6': resolution: {integrity: sha512-wQxQzxYeJqHcfppzBDnm1yAY0jSRkUXR2z8RePZYrKwMKgMlE8+Z6LUno+bd6LvbGh8Gltvy74+9pIYkr+XkKA==} engines: {node: '>=6.9.0'} - deprecated: This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-nullish-coalescing-operator instead. peerDependencies: '@babel/core': ^7.0.0-0 '@babel/plugin-proposal-object-rest-spread@7.20.7': resolution: {integrity: sha512-d2S98yCiLxDVmBmE8UjGcfPvNEUbA1U5q5WxaWFUGRzJSVAZqm5W6MbPct0jxnegUZ0niLeNX+IOzEs7wYg9Dg==} engines: {node: '>=6.9.0'} - deprecated: This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-object-rest-spread instead. peerDependencies: '@babel/core': ^7.0.0-0 '@babel/plugin-proposal-optional-catch-binding@7.18.6': resolution: {integrity: sha512-Q40HEhs9DJQyaZfUjjn6vE8Cv4GmMHCYuMGIWUnlxH6400VGxOuwWsPt4FxXxJkC/5eOzgn0z21M9gMT4MOhbw==} engines: {node: '>=6.9.0'} - deprecated: This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-optional-catch-binding instead. peerDependencies: '@babel/core': ^7.0.0-0 '@babel/plugin-proposal-optional-chaining@7.21.0': resolution: {integrity: sha512-p4zeefM72gpmEe2fkUr/OnOXpWEf8nAgk7ZYVqqfFiyIG7oFfVZcCrU64hWn5xp4tQ9LkV4bTIa5rD0KANpKNA==} engines: {node: '>=6.9.0'} - deprecated: This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-optional-chaining instead. peerDependencies: '@babel/core': ^7.0.0-0 @@ -1969,7 +1975,6 @@ packages: '@humanwhocodes/config-array@0.13.0': resolution: {integrity: sha512-DZLEEqFWQFiyK6h5YIeynKx7JlvCYWL0cImfSRXZ9l4Sg2efkFGTuFf6vzXjK1cq6IYkU+Eg/JizXw+TD2vRNw==} engines: {node: '>=10.10.0'} - deprecated: Use @eslint/config-array instead '@humanwhocodes/module-importer@1.0.1': resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==} @@ -1977,7 +1982,6 @@ packages: '@humanwhocodes/object-schema@2.0.3': resolution: {integrity: sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==} - deprecated: Use @eslint/object-schema instead '@hutson/parse-repository-url@3.0.2': resolution: {integrity: sha512-H9XAx3hc0BQHY6l+IFSWHDySypcXsvsuLhgYLUGywmJ5pswRVQJUHpOsobnLYp2ZUaUlKiKDrgWWhosOwAEM8Q==} @@ -2224,7 +2228,6 @@ packages: '@npmcli/move-file@1.1.2': resolution: {integrity: sha512-1SUf/Cg2GzGDyaf15aR9St9TWlb+XvbZXWpDx8YKs7MLzMH/BCeopv+y9vzrzgkfykCGuWOlSu3mZhj2+FQcrg==} engines: {node: '>=10'} - deprecated: This functionality has been moved to @npmcli/fs '@npmcli/name-from-folder@2.0.0': resolution: {integrity: sha512-pwK+BfEBZJbKdNYpHHRTNBwBoqrN/iIMO0AiGvYsp3Hoaq0WbgGSWQR6SCldZovoDpY3yje5lkFUe6gsDgJ2vg==} @@ -2447,14 +2450,30 @@ packages: '@scure/base@1.2.1': resolution: {integrity: sha512-DGmGtC8Tt63J5GfHgfl5CuAXh96VF/LD8K9Hr/Gv0J2lAoRGlPOMpqMpMbCTOoOJMZCk2Xt+DskdDyn6dEFdzQ==} + '@sd-jwt/core@0.7.2': + resolution: {integrity: sha512-vix1GplUFc1A9H42r/yXkg7cKYthggyqZEwlFdsBbn4xdZNE+AHVF4N7kPa1pPxipwN3UIHd4XnQ5MJV15mhsQ==} + engines: {node: '>=18'} + + '@sd-jwt/crypto-nodejs@0.7.2': + resolution: {integrity: sha512-7DHy1WBHwvXseiX+U7XA6jX4dX4Ins3Nxd12JhBSm+FJfIwU97FU/H0KlF6lLyi4a4nbY/O6U9wJjYI1PxA9sQ==} + engines: {node: '>=18'} + '@sd-jwt/decode@0.7.2': resolution: {integrity: sha512-dan2LSvK63SKwb62031G4r7TE4TaiI0EK1KbPXqS+LCXNkNDUHqhtYp9uOpj+grXceCsMtMa2f8VnUfsjmwHHg==} engines: {node: '>=18'} + '@sd-jwt/jwt-status-list@0.7.2': + resolution: {integrity: sha512-o/Mg/Zg21poFsPXuxtPD9sdXq2b/0L+rb9gxU2k1rp1aT+DWmqD0k8v0Ttr2tlMc8l1xXQNA8FLXbL1AdLRmbQ==} + engines: {node: '>=18'} + '@sd-jwt/present@0.7.2': resolution: {integrity: sha512-mQV85u2+mLLy2VZ9Wx2zpaB6yTDnbhCfWkP7eeCrzJQHBKAAHko8GrylEFmLKewFIcajS/r4lT/zHOsCkp5pZw==} engines: {node: '>=18'} + '@sd-jwt/sd-jwt-vc@0.7.2': + resolution: {integrity: sha512-rryYmnoJHRSNqHcrs0Atta+bfJzU2yT7mYumR2D4lTfxJKWZd0OHHFq57uZSEm/wXPI6uytUJXYbEboCqLUAtw==} + engines: {node: '>=18'} + '@sd-jwt/types@0.7.2': resolution: {integrity: sha512-1NRKowiW0ZiB9SGLApLPBH4Xk8gDQJ+nA9NdZ+uy6MmJKLEwjuJxO7yTvRIv/jX/0/Ebh339S7Kq4RD2AiFuRg==} engines: {node: '>=18'} @@ -2534,12 +2553,12 @@ packages: react-native-securerandom: optional: true - '@sphereon/kmp-mdoc-core@0.2.0-SNAPSHOT.10': - resolution: {integrity: sha512-mHH7I6fWdztaNjguGJOLaerXWnQymQ/xKQ8NqClIXoI2PJNgmpQG6DxFcLRs1aYyWg1iY8bPliLJi41u94KdCA==} + '@sphereon/kmp-mdoc-core@0.2.0-SNAPSHOT.26': + resolution: {integrity: sha512-QXJ6R8ENiZV2rPMbn06cw5JKwqUYN1kzVRbYfONqE1PEXx1noQ4md7uxr2zSczi0ubKkNcbyYDNtIMTZIhGzmQ==} bundledDependencies: [] - '@sphereon/pex-models@2.3.1': - resolution: {integrity: sha512-SByU4cJ0XYA6VZQ/L6lsSiRcFtBPHbFioCeQ4GP7/W/jQ+PSBD7uK2oTnKQ9/0iEiMK/6JYqhKgLs4a9UX3UTQ==} + '@sphereon/pex-models@2.3.2': + resolution: {integrity: sha512-foFxfLkRwcn/MOp/eht46Q7wsvpQGlO7aowowIIb5Tz9u97kYZ2kz6K2h2ODxWuv5CRA7Q0MY8XUBGE2lfOhOQ==} '@sphereon/pex@5.0.0-unstable.28': resolution: {integrity: sha512-zxHCWAc7fKppS7XX0zxnI4TF+Rdjax8pHc3exrYzn3t59dlv5siEAeYdtFrWJT4UVB5wTGzIEufzV5r+tfjelg==} @@ -2562,8 +2581,8 @@ packages: '@sphereon/ssi-sdk-ext.key-utils@0.23.0': resolution: {integrity: sha512-BfULXvQmcUrBq2DqYxJHKnEoB2d5icu3TJ9GP2aP1WybSULTjL96Wv5r7QKgktcodKaL+F+oQ7r8sC9qBl1exw==} - '@sphereon/ssi-types@0.30.2-feature.mdoc.funke2.367': - resolution: {integrity: sha512-+acZDnAB08PjhrNX+0sJuGK6X3AN2zZ6dvXjfWCXovWjJwcKJBBGR+nfvguBbefR9aaB8SKPAkDFhQ/U/I7byg==} + '@sphereon/ssi-types@0.32.1-feature.VDX.341.53': + resolution: {integrity: sha512-SC5GMqMZDILQJLGUddXjkVmSHIsDevp/ANVRGtLsWQK1pt3272eOA6Xq0yBMaAFmknYlisHMOf29JlYLJy/fZw==} '@sphereon/wellknown-dids-client@0.1.3': resolution: {integrity: sha512-TAT24L3RoXD8ocrkTcsz7HuJmgjNjdoV6IXP1p3DdaI/GqkynytXE3J1+F7vUFMRYwY5nW2RaXSgDQhrFJemaA==} @@ -2992,7 +3011,6 @@ packages: '@xmldom/xmldom@0.7.13': resolution: {integrity: sha512-lm2GW5PkosIzccsaZIz7tp8cPADSIlIHWDFTR1N0SzfinhhYgeIQjFMz4rYzanCScr3DqQLeomUDArp6MWKm+g==} engines: {node: '>=10.0.0'} - deprecated: this version is no longer supported, please update to at least 0.8.* '@xmldom/xmldom@0.8.10': resolution: {integrity: sha512-2WALfTl4xo2SkGCYRt6rDTFfk9R1czmBvUQy12gK2KuRKIpWEhcbbzy8EZXtz/jkRqHX8bFEc6FC1HjX4TUWYw==} @@ -3148,7 +3166,6 @@ packages: are-we-there-yet@2.0.0: resolution: {integrity: sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw==} engines: {node: '>=10'} - deprecated: This package is no longer supported. arg@4.1.0: resolution: {integrity: sha512-ZWc51jO3qegGkVh8Hwpv636EkbesNV5ZNQPCtRa+0qytRYPEs9IYT9qITY9buezqUH5uqyzlWLcufrzU2rffdg==} @@ -3408,7 +3425,6 @@ packages: boolean@3.2.0: resolution: {integrity: sha512-d0II/GO9uf9lfUHH2BQsjxzRJZBdsjgsBiW4BvhWk/3qoKwQFjIDVN19PfX8F2D/r9PCMTtLWjYVCFrpeYUzsw==} - deprecated: Package no longer supported. Contact Support at https://www.npmjs.com/support for more info. bplist-creator@0.1.0: resolution: {integrity: sha512-sXaHZicyEEmY86WyueLTQesbeoH/mquvarJaQNbjuOQO+7gbFcDEWqKmcWA4cOTLzFlfgvkiVxolk1k5bBIpmg==} @@ -4004,6 +4020,9 @@ packages: dayjs@1.11.13: resolution: {integrity: sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg==} + dcql@0.2.19: + resolution: {integrity: sha512-/EvT8tArlg8zFsTQbRn6PijfeQ3nUwuEeCRDpptWcYqE8Wyt8J9Sb44gMPFzVCoIEb3R0M7Hl+XWkUMobC8jXQ==} + debug@2.6.9: resolution: {integrity: sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==} peerDependencies: @@ -4433,7 +4452,6 @@ packages: eslint@8.57.1: resolution: {integrity: sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - deprecated: This version is no longer supported. Please see https://eslint.org/version-support for other options. hasBin: true esm@3.2.25: @@ -4826,7 +4844,6 @@ packages: gauge@3.0.2: resolution: {integrity: sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q==} engines: {node: '>=10'} - deprecated: This package is no longer supported. gensequence@5.0.2: resolution: {integrity: sha512-JlKEZnFc6neaeSVlkzBGGgkIoIaSxMgvdamRoPN8r3ozm2r9dusqxeKqYQ7lhzmj2UhFQP8nkyfCaiLQxiLrDA==} @@ -4937,20 +4954,16 @@ packages: glob@6.0.4: resolution: {integrity: sha512-MKZeRNyYZAVVVG1oZeLaWie1uweH40m9AZwIwxyPbTSX4hHrVYSzLg0Ro5Z5R7XKkIX+Cc6oD1rqeDJnwsB8/A==} - deprecated: Glob versions prior to v9 are no longer supported glob@7.1.6: resolution: {integrity: sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==} - deprecated: Glob versions prior to v9 are no longer supported glob@7.2.3: resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} - deprecated: Glob versions prior to v9 are no longer supported glob@8.1.0: resolution: {integrity: sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==} engines: {node: '>=12'} - deprecated: Glob versions prior to v9 are no longer supported glob@9.3.5: resolution: {integrity: sha512-e1LleDykUz2Iu+MTYdkSsuWX8lvAjAcs0Xef0lNIu0S2wOAzuTxCJtcd9S3cijlwYF18EsU3rzb8jPVobxDh9Q==} @@ -5193,7 +5206,6 @@ packages: inflight@1.0.6: resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} - deprecated: This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful. inherits@2.0.4: resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} @@ -6376,7 +6388,6 @@ packages: multibase@4.0.6: resolution: {integrity: sha512-x23pDe5+svdLz/k5JPGCVdfn7Q5mZVMBETiC+ORfO+sor9Sgs0smJzAjfTbM5tckeCqnaUuMYoz+k3RXMmJClQ==} engines: {node: '>=12.0.0', npm: '>=6.0.0'} - deprecated: This module has been superseded by the multiformats module multiformats@12.1.3: resolution: {integrity: sha512-eajQ/ZH7qXZQR2AgtfpmSMizQzmyYVmCql7pdhldPuYQi4atACekbJaQplk6dWyIi10jCaFnd6pqvcEFXjbaJw==} @@ -6572,7 +6583,6 @@ packages: npmlog@5.0.1: resolution: {integrity: sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw==} - deprecated: This package is no longer supported. nullthrows@1.1.1: resolution: {integrity: sha512-2vPPEi+Z7WqML2jZYddDIfy5Dqb0r2fze2zTxNNknZaFpVHU3mFB3R+DWeJWGVx0ecvttSGlJTI+WG+8Z4cDWw==} @@ -6714,7 +6724,6 @@ packages: osenv@0.1.5: resolution: {integrity: sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==} - deprecated: This package is no longer supported. p-finally@1.0.0: resolution: {integrity: sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow==} @@ -7376,22 +7385,18 @@ packages: rimraf@2.2.8: resolution: {integrity: sha512-R5KMKHnPAQaZMqLOsyuyUmcIjSeDm+73eoqQpaXA7AZ22BL+6C+1mcUscgOsNd8WVlJuvlgAPsegcx7pjlV0Dg==} - deprecated: Rimraf versions prior to v4 are no longer supported hasBin: true rimraf@2.4.5: resolution: {integrity: sha512-J5xnxTyqaiw06JjMftq7L9ouA448dw/E7dKghkP9WpKNuwmARNNg+Gk8/u5ryb9N/Yo2+z3MCwuqFK/+qPOPfQ==} - deprecated: Rimraf versions prior to v4 are no longer supported hasBin: true rimraf@2.6.3: resolution: {integrity: sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==} - deprecated: Rimraf versions prior to v4 are no longer supported hasBin: true rimraf@3.0.2: resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==} - deprecated: Rimraf versions prior to v4 are no longer supported hasBin: true rimraf@4.4.1: @@ -7825,7 +7830,6 @@ packages: superagent@8.1.2: resolution: {integrity: sha512-6WTxW1EB6yCxV5VFOIPQruWGHqc3yI7hEmZK6h+pyk69Lk/Ut7rLUY6W/ONF2MjBuGjvmMiIpsrVJ2vjrHlslA==} engines: {node: '>=6.4.0 <13 || >=14'} - deprecated: Please upgrade to v9.0.0+ as we have fixed a public vulnerability with formidable dependency. Note that v9.0.0+ requires Node.js v14.18.0+. See https://github.com/ladjs/superagent/pull/1800 for insight. This project is supported and maintained by the team at Forward Email @ https://forwardemail.net supertest@6.3.4: resolution: {integrity: sha512-erY3HFDG0dPnhw4U+udPfrzXa4xhSG+n4rxfRuZWCUvjFWwKl+OxWf/7zk50s84/fAAs7vf5QAb9uRa0cCykxw==} @@ -8208,7 +8212,6 @@ packages: uglify-es@3.3.9: resolution: {integrity: sha512-r+MU0rfv4L/0eeW3xZrd16t4NZfK8Ld4SWVglYBb7ez5uXFWHuVRs6xCTrf1yirs9a4j4Y27nn7SRfO6v67XsQ==} engines: {node: '>=0.8.0'} - deprecated: support for ECMAScript is superseded by `uglify-js` as of v3.13.0 hasBin: true uglify-js@3.19.3: @@ -8341,7 +8344,6 @@ packages: uuid@3.4.0: resolution: {integrity: sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==} - deprecated: Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details. hasBin: true uuid@7.0.3: @@ -8371,6 +8373,14 @@ packages: typescript: optional: true + valibot@1.0.0-beta.8: + resolution: {integrity: sha512-OPAwJZtowb0j91b+bd77+ny7D1VVzsCzD7Jl9waLUlMprTsfI9Y3HHbW3hAQD7wKDKHsmGEesuiYWaYvcZL2wg==} + peerDependencies: + typescript: '>=5' + peerDependenciesMeta: + typescript: + optional: true + valid-url@1.0.9: resolution: {integrity: sha512-QQDsV8OnSf5Uc30CKSwG9lnhMPe6exHtTXLRYX8uMwKENy640pU+2BgBL0LRbDh/eYRahNCS7aewCx0wf3NYVA==} @@ -10012,7 +10022,7 @@ snapshots: '@digitalcredentials/bnid@2.1.2(react-native@0.71.19(@babel/core@7.26.0)(@babel/preset-env@7.26.0(@babel/core@7.26.0))(encoding@0.1.13)(react@18.3.1))': dependencies: '@digitalcredentials/base58-universal': 1.0.1 - react-native-securerandom: 1.0.1(react-native@0.71.19(@babel/core@7.26.0)(encoding@0.1.13)(react@18.3.1)) + react-native-securerandom: 1.0.1(react-native@0.71.19(@babel/core@7.26.0)(@babel/preset-env@7.26.0(@babel/core@7.26.0))(encoding@0.1.13)(react@18.3.1)) yargs: 15.4.1 transitivePeerDependencies: - react-native @@ -11759,17 +11769,38 @@ snapshots: '@scure/base@1.2.1': {} + '@sd-jwt/core@0.7.2': + dependencies: + '@sd-jwt/decode': 0.7.2 + '@sd-jwt/present': 0.7.2 + '@sd-jwt/types': 0.7.2 + '@sd-jwt/utils': 0.7.2 + + '@sd-jwt/crypto-nodejs@0.7.2': {} + '@sd-jwt/decode@0.7.2': dependencies: '@sd-jwt/types': 0.7.2 '@sd-jwt/utils': 0.7.2 + '@sd-jwt/jwt-status-list@0.7.2': + dependencies: + '@sd-jwt/types': 0.7.2 + base64url: 3.0.1 + pako: 2.1.0 + '@sd-jwt/present@0.7.2': dependencies: '@sd-jwt/decode': 0.7.2 '@sd-jwt/types': 0.7.2 '@sd-jwt/utils': 0.7.2 + '@sd-jwt/sd-jwt-vc@0.7.2': + dependencies: + '@sd-jwt/core': 0.7.2 + '@sd-jwt/jwt-status-list': 0.7.2 + '@sd-jwt/utils': 0.7.2 + '@sd-jwt/types@0.7.2': {} '@sd-jwt/utils@0.7.2': @@ -11861,15 +11892,15 @@ snapshots: webcrypto-shim: 0.1.7 optionalDependencies: expo: 48.0.21(@babel/core@7.26.0)(encoding@0.1.13) - react-native-securerandom: 1.0.1(react-native@0.71.19(@babel/core@7.26.0)(encoding@0.1.13)(react@18.3.1)) + react-native-securerandom: 1.0.1(react-native@0.71.19(@babel/core@7.26.0)(@babel/preset-env@7.26.0(@babel/core@7.26.0))(encoding@0.1.13)(react@18.3.1)) - '@sphereon/kmp-mdoc-core@0.2.0-SNAPSHOT.10': + '@sphereon/kmp-mdoc-core@0.2.0-SNAPSHOT.26': dependencies: '@js-joda/core': 5.6.3 '@js-joda/timezone': 2.3.0(@js-joda/core@5.6.3) format-util: 1.0.5 - '@sphereon/pex-models@2.3.1': {} + '@sphereon/pex-models@2.3.2': {} '@sphereon/pex@5.0.0-unstable.28': dependencies: @@ -11877,8 +11908,8 @@ snapshots: '@sd-jwt/decode': 0.7.2 '@sd-jwt/present': 0.7.2 '@sd-jwt/types': 0.7.2 - '@sphereon/pex-models': 2.3.1 - '@sphereon/ssi-types': 0.30.2-feature.mdoc.funke2.367 + '@sphereon/pex-models': 2.3.2 + '@sphereon/ssi-types': 0.32.1-feature.VDX.341.53 ajv: 8.17.1 ajv-formats: 2.1.1(ajv@8.17.1) jwt-decode: 3.1.2 @@ -11930,19 +11961,20 @@ snapshots: - react-native-securerandom - supports-color - '@sphereon/ssi-types@0.30.2-feature.mdoc.funke2.367': + '@sphereon/ssi-types@0.32.1-feature.VDX.341.53': dependencies: '@sd-jwt/decode': 0.7.2 - '@sphereon/kmp-mdoc-core': 0.2.0-SNAPSHOT.10 + '@sphereon/kmp-mdoc-core': 0.2.0-SNAPSHOT.26 debug: 4.3.7 events: 3.3.0 - jwt-decode: 3.1.2 + jwt-decode: 4.0.0 + uint8arrays: 3.1.1 transitivePeerDependencies: - supports-color '@sphereon/wellknown-dids-client@0.1.3(encoding@0.1.13)': dependencies: - '@sphereon/ssi-types': 0.30.2-feature.mdoc.funke2.367 + '@sphereon/ssi-types': 0.32.1-feature.VDX.341.53 cross-fetch: 3.1.8(encoding@0.1.13) jwt-decode: 3.1.2 transitivePeerDependencies: @@ -13885,6 +13917,12 @@ snapshots: dayjs@1.11.13: {} + dcql@0.2.19(typescript@5.4.5): + dependencies: + valibot: 1.0.0-beta.8(typescript@5.4.5) + transitivePeerDependencies: + - typescript + debug@2.6.9: dependencies: ms: 2.0.0 @@ -18192,7 +18230,7 @@ snapshots: react-native: 0.71.19(@babel/core@7.26.0)(@babel/preset-env@7.26.0(@babel/core@7.26.0))(encoding@0.1.13)(react@18.3.1) optional: true - react-native-securerandom@1.0.1(react-native@0.71.19(@babel/core@7.26.0)(encoding@0.1.13)(react@18.3.1)): + react-native-securerandom@1.0.1(react-native@0.71.19(@babel/core@7.26.0)(@babel/preset-env@7.26.0(@babel/core@7.26.0))(encoding@0.1.13)(react@18.3.1)): dependencies: base64-js: 1.5.1 react-native: 0.71.19(@babel/core@7.26.0)(@babel/preset-env@7.26.0(@babel/core@7.26.0))(encoding@0.1.13)(react@18.3.1) @@ -19549,6 +19587,10 @@ snapshots: optionalDependencies: typescript: 5.5.3 + valibot@1.0.0-beta.8(typescript@5.4.5): + optionalDependencies: + typescript: 5.4.5 + valid-url@1.0.9: {} validate-npm-package-license@3.0.4: