@pagopa/io-react-native-wallet 0.28.0 → 0.28.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (149) hide show
  1. package/lib/commonjs/credential/issuance/03-start-user-authorization.js +3 -0
  2. package/lib/commonjs/credential/issuance/03-start-user-authorization.js.map +1 -1
  3. package/lib/commonjs/credential/issuance/04-complete-user-authorization.js +5 -3
  4. package/lib/commonjs/credential/issuance/04-complete-user-authorization.js.map +1 -1
  5. package/lib/commonjs/credential/presentation/01-start-flow.js +12 -23
  6. package/lib/commonjs/credential/presentation/01-start-flow.js.map +1 -1
  7. package/lib/commonjs/credential/presentation/02-evaluate-rp-trust.js +4 -2
  8. package/lib/commonjs/credential/presentation/02-evaluate-rp-trust.js.map +1 -1
  9. package/lib/commonjs/credential/presentation/03-get-request-object.js +31 -43
  10. package/lib/commonjs/credential/presentation/03-get-request-object.js.map +1 -1
  11. package/lib/commonjs/credential/presentation/04-retrieve-rp-jwks.js +32 -0
  12. package/lib/commonjs/credential/presentation/04-retrieve-rp-jwks.js.map +1 -0
  13. package/lib/commonjs/credential/presentation/05-verify-request-object.js +60 -0
  14. package/lib/commonjs/credential/presentation/05-verify-request-object.js.map +1 -0
  15. package/lib/commonjs/credential/presentation/06-fetch-presentation-definition.js +39 -0
  16. package/lib/commonjs/credential/presentation/06-fetch-presentation-definition.js.map +1 -0
  17. package/lib/commonjs/credential/presentation/07-evaluate-dcql-query.js +134 -0
  18. package/lib/commonjs/credential/presentation/07-evaluate-dcql-query.js.map +1 -0
  19. package/lib/commonjs/credential/presentation/07-evaluate-input-descriptor.js +296 -0
  20. package/lib/commonjs/credential/presentation/07-evaluate-input-descriptor.js.map +1 -0
  21. package/lib/commonjs/credential/presentation/08-send-authorization-response.js +170 -0
  22. package/lib/commonjs/credential/presentation/08-send-authorization-response.js.map +1 -0
  23. package/lib/commonjs/credential/presentation/README.md +88 -2
  24. package/lib/commonjs/credential/presentation/errors.js +68 -1
  25. package/lib/commonjs/credential/presentation/errors.js.map +1 -1
  26. package/lib/commonjs/credential/presentation/index.js +54 -1
  27. package/lib/commonjs/credential/presentation/index.js.map +1 -1
  28. package/lib/commonjs/credential/presentation/types.js +125 -4
  29. package/lib/commonjs/credential/presentation/types.js.map +1 -1
  30. package/lib/commonjs/sd-jwt/index.js +41 -1
  31. package/lib/commonjs/sd-jwt/index.js.map +1 -1
  32. package/lib/commonjs/trust/chain.js.map +1 -1
  33. package/lib/commonjs/trust/types.js +26 -6
  34. package/lib/commonjs/trust/types.js.map +1 -1
  35. package/lib/commonjs/trust/utils.js +5 -0
  36. package/lib/commonjs/trust/utils.js.map +1 -1
  37. package/lib/commonjs/utils/jwk.js +5 -1
  38. package/lib/commonjs/utils/jwk.js.map +1 -1
  39. package/lib/module/credential/issuance/03-start-user-authorization.js +3 -0
  40. package/lib/module/credential/issuance/03-start-user-authorization.js.map +1 -1
  41. package/lib/module/credential/issuance/04-complete-user-authorization.js +5 -3
  42. package/lib/module/credential/issuance/04-complete-user-authorization.js.map +1 -1
  43. package/lib/module/credential/presentation/01-start-flow.js +13 -24
  44. package/lib/module/credential/presentation/01-start-flow.js.map +1 -1
  45. package/lib/module/credential/presentation/02-evaluate-rp-trust.js +4 -2
  46. package/lib/module/credential/presentation/02-evaluate-rp-trust.js.map +1 -1
  47. package/lib/module/credential/presentation/03-get-request-object.js +32 -44
  48. package/lib/module/credential/presentation/03-get-request-object.js.map +1 -1
  49. package/lib/module/credential/presentation/04-retrieve-rp-jwks.js +25 -0
  50. package/lib/module/credential/presentation/04-retrieve-rp-jwks.js.map +1 -0
  51. package/lib/module/credential/presentation/05-verify-request-object.js +53 -0
  52. package/lib/module/credential/presentation/05-verify-request-object.js.map +1 -0
  53. package/lib/module/credential/presentation/06-fetch-presentation-definition.js +32 -0
  54. package/lib/module/credential/presentation/06-fetch-presentation-definition.js.map +1 -0
  55. package/lib/module/credential/presentation/07-evaluate-dcql-query.js +127 -0
  56. package/lib/module/credential/presentation/07-evaluate-dcql-query.js.map +1 -0
  57. package/lib/module/credential/presentation/07-evaluate-input-descriptor.js +285 -0
  58. package/lib/module/credential/presentation/07-evaluate-input-descriptor.js.map +1 -0
  59. package/lib/module/credential/presentation/08-send-authorization-response.js +158 -0
  60. package/lib/module/credential/presentation/08-send-authorization-response.js.map +1 -0
  61. package/lib/module/credential/presentation/README.md +88 -2
  62. package/lib/module/credential/presentation/errors.js +63 -0
  63. package/lib/module/credential/presentation/errors.js.map +1 -1
  64. package/lib/module/credential/presentation/index.js +7 -2
  65. package/lib/module/credential/presentation/index.js.map +1 -1
  66. package/lib/module/credential/presentation/types.js +122 -3
  67. package/lib/module/credential/presentation/types.js.map +1 -1
  68. package/lib/module/sd-jwt/index.js +40 -1
  69. package/lib/module/sd-jwt/index.js.map +1 -1
  70. package/lib/module/trust/chain.js.map +1 -1
  71. package/lib/module/trust/types.js +26 -6
  72. package/lib/module/trust/types.js.map +1 -1
  73. package/lib/module/trust/utils.js +5 -0
  74. package/lib/module/trust/utils.js.map +1 -1
  75. package/lib/module/utils/jwk.js +3 -0
  76. package/lib/module/utils/jwk.js.map +1 -1
  77. package/lib/typescript/credential/issuance/03-start-user-authorization.d.ts.map +1 -1
  78. package/lib/typescript/credential/issuance/04-complete-user-authorization.d.ts +2 -2
  79. package/lib/typescript/credential/issuance/04-complete-user-authorization.d.ts.map +1 -1
  80. package/lib/typescript/credential/presentation/01-start-flow.d.ts +23 -7
  81. package/lib/typescript/credential/presentation/01-start-flow.d.ts.map +1 -1
  82. package/lib/typescript/credential/presentation/02-evaluate-rp-trust.d.ts +1 -0
  83. package/lib/typescript/credential/presentation/02-evaluate-rp-trust.d.ts.map +1 -1
  84. package/lib/typescript/credential/presentation/03-get-request-object.d.ts +7 -11
  85. package/lib/typescript/credential/presentation/03-get-request-object.d.ts.map +1 -1
  86. package/lib/typescript/credential/presentation/04-retrieve-rp-jwks.d.ts +23 -0
  87. package/lib/typescript/credential/presentation/04-retrieve-rp-jwks.d.ts.map +1 -0
  88. package/lib/typescript/credential/presentation/05-verify-request-object.d.ts +20 -0
  89. package/lib/typescript/credential/presentation/05-verify-request-object.d.ts.map +1 -0
  90. package/lib/typescript/credential/presentation/06-fetch-presentation-definition.d.ts +21 -0
  91. package/lib/typescript/credential/presentation/06-fetch-presentation-definition.d.ts.map +1 -0
  92. package/lib/typescript/credential/presentation/07-evaluate-dcql-query.d.ts +28 -0
  93. package/lib/typescript/credential/presentation/07-evaluate-dcql-query.d.ts.map +1 -0
  94. package/lib/typescript/credential/presentation/07-evaluate-input-descriptor.d.ts +93 -0
  95. package/lib/typescript/credential/presentation/07-evaluate-input-descriptor.d.ts.map +1 -0
  96. package/lib/typescript/credential/presentation/08-send-authorization-response.d.ts +70 -0
  97. package/lib/typescript/credential/presentation/08-send-authorization-response.d.ts.map +1 -0
  98. package/lib/typescript/credential/presentation/errors.d.ts +43 -0
  99. package/lib/typescript/credential/presentation/errors.d.ts.map +1 -1
  100. package/lib/typescript/credential/presentation/index.d.ts +8 -3
  101. package/lib/typescript/credential/presentation/index.d.ts.map +1 -1
  102. package/lib/typescript/credential/presentation/types.d.ts +750 -13
  103. package/lib/typescript/credential/presentation/types.d.ts.map +1 -1
  104. package/lib/typescript/sd-jwt/index.d.ts +19 -0
  105. package/lib/typescript/sd-jwt/index.d.ts.map +1 -1
  106. package/lib/typescript/trust/index.d.ts +236 -8
  107. package/lib/typescript/trust/index.d.ts.map +1 -1
  108. package/lib/typescript/trust/types.d.ts +3046 -76
  109. package/lib/typescript/trust/types.d.ts.map +1 -1
  110. package/lib/typescript/trust/utils.d.ts +6 -6
  111. package/lib/typescript/trust/utils.d.ts.map +1 -1
  112. package/lib/typescript/utils/decoder.d.ts +1 -1
  113. package/lib/typescript/utils/decoder.d.ts.map +1 -1
  114. package/lib/typescript/utils/errors.d.ts.map +1 -1
  115. package/lib/typescript/utils/jwk.d.ts +137 -0
  116. package/lib/typescript/utils/jwk.d.ts.map +1 -1
  117. package/lib/typescript/utils/misc.d.ts.map +1 -1
  118. package/lib/typescript/wallet-instance-attestation/types.d.ts +16 -16
  119. package/package.json +15 -11
  120. package/src/credential/issuance/03-start-user-authorization.ts +3 -0
  121. package/src/credential/issuance/04-complete-user-authorization.ts +6 -3
  122. package/src/credential/presentation/01-start-flow.ts +18 -28
  123. package/src/credential/presentation/02-evaluate-rp-trust.ts +3 -2
  124. package/src/credential/presentation/03-get-request-object.ts +36 -60
  125. package/src/credential/presentation/04-retrieve-rp-jwks.ts +34 -0
  126. package/src/credential/presentation/05-verify-request-object.ts +63 -0
  127. package/src/credential/presentation/06-fetch-presentation-definition.ts +48 -0
  128. package/src/credential/presentation/07-evaluate-dcql-query.ts +174 -0
  129. package/src/credential/presentation/07-evaluate-input-descriptor.ts +393 -0
  130. package/src/credential/presentation/08-send-authorization-response.ts +222 -0
  131. package/src/credential/presentation/README.md +88 -2
  132. package/src/credential/presentation/errors.ts +64 -0
  133. package/src/credential/presentation/index.ts +40 -1
  134. package/src/credential/presentation/types.ts +135 -4
  135. package/src/sd-jwt/index.ts +49 -1
  136. package/src/trust/chain.ts +2 -2
  137. package/src/trust/types.ts +25 -5
  138. package/src/trust/utils.ts +6 -3
  139. package/src/utils/decoder.ts +1 -1
  140. package/src/utils/errors.ts +2 -2
  141. package/src/utils/jwk.ts +8 -1
  142. package/src/utils/misc.ts +2 -2
  143. package/lib/commonjs/credential/presentation/04-send-authorization-response.js +0 -138
  144. package/lib/commonjs/credential/presentation/04-send-authorization-response.js.map +0 -1
  145. package/lib/module/credential/presentation/04-send-authorization-response.js +0 -128
  146. package/lib/module/credential/presentation/04-send-authorization-response.js.map +0 -1
  147. package/lib/typescript/credential/presentation/04-send-authorization-response.d.ts +0 -34
  148. package/lib/typescript/credential/presentation/04-send-authorization-response.d.ts.map +0 -1
  149. package/src/credential/presentation/04-send-authorization-response.ts +0 -168
@@ -1,85 +1,61 @@
1
- import { v4 as uuidv4 } from "uuid";
2
- import {
3
- decode as decodeJwt,
4
- sha256ToBase64,
5
- verify,
6
- type CryptoContext,
7
- } from "@pagopa/io-react-native-jwt";
8
-
9
- import { createDPopToken } from "../../utils/dpop";
10
- import { NoSuitableKeysFoundInEntityConfiguration } from "./errors";
11
- import type { EvaluateRelyingPartyTrust } from "./02-evaluate-rp-trust";
12
1
  import { hasStatusOrThrow, type Out } from "../../utils/misc";
13
2
  import type { StartFlow } from "./01-start-flow";
14
- import { RequestObject } from "./types";
3
+ import { RequestObjectWalletCapabilities } from "./types";
15
4
 
16
5
  export type GetRequestObject = (
17
- requestUri: Out<StartFlow>["requestURI"],
18
- rpConf: Out<EvaluateRelyingPartyTrust>["rpConf"],
19
- context: {
20
- wiaCryptoContext: CryptoContext;
6
+ requestUri: Out<StartFlow>["requestUri"],
7
+ context?: {
21
8
  appFetch?: GlobalFetch["fetch"];
22
- walletInstanceAttestation: string;
9
+ walletCapabilities?: RequestObjectWalletCapabilities;
23
10
  }
24
- ) => Promise<{ requestObject: RequestObject }>;
11
+ ) => Promise<{ requestObjectEncodedJwt: string }>;
25
12
 
26
13
  /**
27
- * Obtain the Request Object for RP authentication
14
+ * Obtain the Request Object for RP authentication. Both the GET and POST `request_uri_method` are supported.
28
15
  * @see https://italia.github.io/eudi-wallet-it-docs/versione-corrente/en/relying-party-solution.html
29
16
  *
30
17
  * @param requestUri The url for the Relying Party to connect with
31
- * @param rpConf The Relying Party's configuration
32
- * @param context.wiaCryptoContext The context to access the key associated with the Wallet Instance Attestation
33
- * @param context.walletInstanceAttestation The Wallet Instance Attestation token
18
+ * @param rpConf The Relying Party's configuration * @param context.walletInstanceAttestation The Wallet Instance Attestation token
19
+ * @param context.walletCapabilities (optional) An object containing the wallet technical capabilities that will be sent with a POST request
34
20
  * @param context.appFetch (optional) fetch api implementation. Default: built-in fetch
35
21
  * @returns The Request Object that describes the presentation
36
22
  */
37
23
  export const getRequestObject: GetRequestObject = async (
38
24
  requestUri,
39
- rpConf,
40
- { wiaCryptoContext, appFetch = fetch, walletInstanceAttestation }
25
+ { appFetch = fetch, walletCapabilities } = {}
41
26
  ) => {
42
- const signedWalletInstanceDPoP = await createDPopToken(
43
- {
44
- jti: `${uuidv4()}`,
45
- htm: "GET",
46
- htu: requestUri,
47
- ath: await sha256ToBase64(walletInstanceAttestation),
48
- },
49
- wiaCryptoContext
50
- );
27
+ if (walletCapabilities) {
28
+ // Validate external input
29
+ const { wallet_metadata, wallet_nonce } =
30
+ RequestObjectWalletCapabilities.parse(walletCapabilities);
31
+
32
+ const formUrlEncodedBody = new URLSearchParams({
33
+ wallet_metadata: JSON.stringify(wallet_metadata),
34
+ ...(wallet_nonce && { wallet_nonce }),
35
+ });
36
+
37
+ const requestObjectEncodedJwt = await appFetch(requestUri, {
38
+ method: "POST",
39
+ headers: {
40
+ "Content-Type": "application/x-www-form-urlencoded",
41
+ },
42
+ body: formUrlEncodedBody.toString(),
43
+ })
44
+ .then(hasStatusOrThrow(200))
45
+ .then((res) => res.text());
46
+
47
+ return {
48
+ requestObjectEncodedJwt,
49
+ };
50
+ }
51
51
 
52
- const responseEncodedJwt = await appFetch(requestUri, {
52
+ const requestObjectEncodedJwt = await appFetch(requestUri, {
53
53
  method: "GET",
54
- headers: {
55
- Authorization: `DPoP ${walletInstanceAttestation}`,
56
- DPoP: signedWalletInstanceDPoP,
57
- },
58
54
  })
59
55
  .then(hasStatusOrThrow(200))
60
- .then((res) => res.json())
61
- .then((responseJson) => responseJson.response);
62
-
63
- const responseJwt = decodeJwt(responseEncodedJwt);
64
-
65
- // verify token signature according to RP's entity configuration
66
- // to ensure the request object is authentic
67
- {
68
- const pubKey = rpConf.wallet_relying_party.jwks.keys.find(
69
- ({ kid }) => kid === responseJwt.protectedHeader.kid
70
- );
71
- if (!pubKey) {
72
- throw new NoSuitableKeysFoundInEntityConfiguration(
73
- "Request Object signature verification"
74
- );
75
- }
76
- await verify(responseEncodedJwt, pubKey);
77
- }
78
-
79
- // Ensure that the request object conforms to the expected specification.
80
- const requestObject = RequestObject.parse(responseJwt.payload);
56
+ .then((res) => res.text());
81
57
 
82
58
  return {
83
- requestObject,
59
+ requestObjectEncodedJwt,
84
60
  };
85
61
  };
@@ -0,0 +1,34 @@
1
+ import { JWK } from "../../utils/jwk";
2
+ import { RelyingPartyEntityConfiguration } from "../../trust/types";
3
+
4
+ /**
5
+ * Defines the signature for a function that retrieves JSON Web Key Sets (JWKS) from a client.
6
+ *
7
+ * @template T - The tuple type representing the function arguments.
8
+ * @param args - The arguments passed to the function.
9
+ * @returns A promise resolving to an object containing an array of JWKs.
10
+ */
11
+ export type FetchJwks<T extends Array<unknown> = []> = (...args: T) => {
12
+ keys: JWK[];
13
+ };
14
+
15
+ /**
16
+ * Retrieves the JSON Web Key Set (JWKS) from a Relying Party's entity configuration.
17
+ *
18
+ * @param rpConfig - The configuration object of the Relying Party entity.
19
+ * @returns An object containing an array of JWKs.
20
+ * @throws Will throw an error if the configuration is invalid or if JWKS is not found.
21
+ */
22
+ export const getJwksFromConfig: FetchJwks<
23
+ [RelyingPartyEntityConfiguration["payload"]["metadata"]]
24
+ > = (rpConfig) => {
25
+ const jwks = rpConfig.openid_credential_verifier.jwks;
26
+
27
+ if (!jwks || !Array.isArray(jwks.keys)) {
28
+ throw new Error("JWKS not found in Relying Party configuration.");
29
+ }
30
+
31
+ return {
32
+ keys: jwks.keys,
33
+ };
34
+ };
@@ -0,0 +1,63 @@
1
+ import { decode as decodeJwt, verify } from "@pagopa/io-react-native-jwt";
2
+ import type { RelyingPartyEntityConfiguration } from "../../trust";
3
+ import { UnverifiedEntityError } from "./errors";
4
+ import { RequestObject } from "./types";
5
+ import { getJwksFromConfig } from "./04-retrieve-rp-jwks";
6
+
7
+ export type VerifyRequestObject = (
8
+ requestObjectEncodedJwt: string,
9
+ context: {
10
+ clientId: string;
11
+ rpConf: RelyingPartyEntityConfiguration["payload"]["metadata"];
12
+ rpSubject: string;
13
+ state?: string;
14
+ }
15
+ ) => Promise<{ requestObject: RequestObject }>;
16
+
17
+ /**
18
+ * Function to verify the Request Object's signature and the client ID.
19
+ * @param requestObjectEncodedJwt The Request Object in JWT format
20
+ * @param context.clientId The client ID to verify
21
+ * @param context.rpConf The Entity Configuration of the Relying Party
22
+ * @param context.state Optional state
23
+ * @returns The verified Request Object
24
+ */
25
+ export const verifyRequestObject: VerifyRequestObject = async (
26
+ requestObjectEncodedJwt,
27
+ { clientId, rpConf, rpSubject, state }
28
+ ) => {
29
+ const requestObjectJwt = decodeJwt(requestObjectEncodedJwt);
30
+ const { keys } = getJwksFromConfig(rpConf);
31
+
32
+ // Verify token signature to ensure the request object is authentic
33
+ const pubKey = keys?.find(
34
+ ({ kid }) => kid === requestObjectJwt.protectedHeader.kid
35
+ );
36
+
37
+ if (!pubKey) {
38
+ throw new UnverifiedEntityError("Request Object signature verification!");
39
+ }
40
+
41
+ // Standard claims are verified within `verify`
42
+ await verify(requestObjectEncodedJwt, pubKey, { issuer: clientId });
43
+
44
+ const requestObject = RequestObject.parse(requestObjectJwt.payload);
45
+
46
+ const isClientIdMatch =
47
+ clientId === requestObject.client_id && clientId === rpSubject;
48
+
49
+ if (!isClientIdMatch) {
50
+ throw new UnverifiedEntityError(
51
+ "Client ID does not match Request Object or Entity Configuration"
52
+ );
53
+ }
54
+
55
+ const isStateMatch =
56
+ state && requestObject.state ? state === requestObject.state : true;
57
+
58
+ if (!isStateMatch) {
59
+ throw new UnverifiedEntityError("State does not match Request Object");
60
+ }
61
+
62
+ return { requestObject };
63
+ };
@@ -0,0 +1,48 @@
1
+ import { PresentationDefinition, RequestObject } from "./types";
2
+ import { RelyingPartyEntityConfiguration } from "../../trust/types";
3
+
4
+ export type FetchPresentationDefinition = (
5
+ requestObject: RequestObject,
6
+ rpConf?: RelyingPartyEntityConfiguration["payload"]["metadata"]
7
+ ) => Promise<{
8
+ presentationDefinition: PresentationDefinition;
9
+ }>;
10
+
11
+ /**
12
+ * Retrieves a PresentationDefinition based on the given parameters.
13
+ *
14
+ * The method attempts the following strategies in order:
15
+ * 1. Checks if `presentation_definition` is directly available in the request object.
16
+ * 2. Uses a pre-configured `presentation_definition` from the relying party configuration if the `scope` is present in the request object.
17
+ *
18
+ * If none of the above conditions are met, the function throws an error indicating the definition could not be found. Note that `presentation_definition_uri` is not supported in 0.9.x.
19
+ *
20
+ * @param {RequestObject} requestObject - The request object containing the presentation definition or references to it.
21
+ * @param {RelyingPartyEntityConfiguration["payload"]["metadata"]} [rpConf] - Optional relying party configuration.
22
+ * @returns {Promise<{ presentationDefinition: PresentationDefinition }>} - Resolves with the presentation definition.
23
+ * @throws {Error} - Throws if the presentation definition cannot be found or fetched.
24
+ */
25
+ export const fetchPresentDefinition: FetchPresentationDefinition = async (
26
+ requestObject,
27
+ rpConf
28
+ ) => {
29
+ // Check if `presentation_definition` is directly available in the request object
30
+ if (requestObject.presentation_definition) {
31
+ return {
32
+ presentationDefinition: requestObject.presentation_definition,
33
+ };
34
+ }
35
+
36
+ // Check if `scope` is present in the request object and a pre-configured presentation definition exists
37
+ if (
38
+ requestObject.scope &&
39
+ rpConf?.openid_credential_verifier?.presentation_definition
40
+ ) {
41
+ return {
42
+ presentationDefinition:
43
+ rpConf.openid_credential_verifier.presentation_definition,
44
+ };
45
+ }
46
+
47
+ throw new Error("Presentation definition not found");
48
+ };
@@ -0,0 +1,174 @@
1
+ import {
2
+ DcqlQuery,
3
+ DcqlError,
4
+ DcqlCredentialSetError,
5
+ DcqlQueryResult,
6
+ } from "dcql";
7
+ import { isValiError } from "valibot";
8
+ import { decode, prepareVpToken } from "../../sd-jwt";
9
+ import type { Disclosure } from "../../sd-jwt/types";
10
+ import { ValidationFailed } from "../../utils/errors";
11
+ import { createCryptoContextFor } from "../../utils/crypto";
12
+ import type { RemotePresentation } from "./types";
13
+
14
+ /**
15
+ * The purpose for the credential request by the RP.
16
+ */
17
+ type CredentialPurpose = {
18
+ required: boolean;
19
+ description?: string;
20
+ };
21
+
22
+ export type EvaluateDcqlQuery = (
23
+ credentialsSdJwt: [string /* keyTag */, string /* credential */][],
24
+ query: DcqlQuery.Input
25
+ ) => {
26
+ id: string;
27
+ vct: string;
28
+ credential: string;
29
+ keyTag: string;
30
+ requiredDisclosures: Disclosure[];
31
+ purposes: CredentialPurpose[];
32
+ }[];
33
+
34
+ export type PrepareRemotePresentations = (
35
+ credentials: {
36
+ id: string;
37
+ credential: string;
38
+ keyTag: string;
39
+ requestedClaims: string[];
40
+ }[],
41
+ nonce: string,
42
+ clientId: string
43
+ ) => Promise<RemotePresentation[]>;
44
+
45
+ type DcqlMatchSuccess = Extract<
46
+ DcqlQueryResult.CredentialMatch,
47
+ { success: true }
48
+ >;
49
+
50
+ /**
51
+ * Convert a credential in JWT format to an object with claims
52
+ * for correct parsing by the `dcql` library.
53
+ */
54
+ const mapCredentialToObject = (jwt: string) => {
55
+ const { sdJwt, disclosures } = decode(jwt);
56
+ const credentialFormat = sdJwt.header.typ;
57
+
58
+ // TODO [SIW-2082]: support MDOC credentials
59
+ if (credentialFormat !== "vc+sd-jwt") {
60
+ throw new Error(`Unsupported credential format: ${credentialFormat}`);
61
+ }
62
+
63
+ return {
64
+ vct: sdJwt.payload.vct,
65
+ credential_format: credentialFormat,
66
+ claims: disclosures.reduce(
67
+ (acc, disclosure) => ({
68
+ ...acc,
69
+ [disclosure.decoded[1]]: disclosure.decoded,
70
+ }),
71
+ {} as Record<string, Disclosure>
72
+ ),
73
+ };
74
+ };
75
+
76
+ /**
77
+ * Extract only successful matches from the DCQL query result.
78
+ */
79
+ const getDcqlQueryMatches = (result: DcqlQueryResult) =>
80
+ Object.entries(result.credential_matches).filter(
81
+ ([, match]) => match.success === true
82
+ ) as [string, DcqlMatchSuccess][];
83
+
84
+ export const evaluateDcqlQuery: EvaluateDcqlQuery = (
85
+ credentialsSdJwt,
86
+ query
87
+ ) => {
88
+ const credentials = credentialsSdJwt.map(([, credential]) =>
89
+ mapCredentialToObject(credential)
90
+ );
91
+
92
+ try {
93
+ // Validate the query
94
+ const parsedQuery = DcqlQuery.parse(query);
95
+ DcqlQuery.validate(parsedQuery);
96
+
97
+ const queryResult = DcqlQuery.query(parsedQuery, credentials);
98
+
99
+ if (!queryResult.canBeSatisfied) {
100
+ throw new Error("No credential can satisfy the provided DCQL query");
101
+ }
102
+ // Build an object vct:credentialJwt to map matched credentials to their JWT
103
+ const credentialsSdJwtByVct = credentials.reduce(
104
+ (acc, c, i) => ({ ...acc, [c.vct]: credentialsSdJwt[i]! }),
105
+ {} as Record<string, [string /* keyTag */, string /* credential */]>
106
+ );
107
+
108
+ return getDcqlQueryMatches(queryResult).map(([id, match]) => {
109
+ if (match.output.credential_format !== "vc+sd-jwt") {
110
+ throw new Error("Unsupported format"); // TODO [SIW-2082]: support MDOC credentials
111
+ }
112
+ const { vct, claims } = match.output;
113
+
114
+ const purposes = queryResult.credential_sets
115
+ ?.filter((set) => set.matching_options?.flat().includes(id))
116
+ ?.map<CredentialPurpose>((credentialSet) => ({
117
+ description: credentialSet.purpose?.toString(),
118
+ required: Boolean(credentialSet.required),
119
+ }));
120
+
121
+ const [keyTag, credential] = credentialsSdJwtByVct[vct]!;
122
+ const requiredDisclosures = Object.values(claims) as Disclosure[];
123
+ return {
124
+ id,
125
+ vct,
126
+ keyTag,
127
+ credential,
128
+ requiredDisclosures,
129
+ // When it is a match but no credential_sets are found, the credential is required by default
130
+ // See https://openid.net/specs/openid-4-verifiable-presentations-1_0-24.html#section-6.3.1.2-2.1
131
+ purposes: purposes ?? [{ required: true }],
132
+ };
133
+ });
134
+ } catch (error) {
135
+ // Invalid DCQL query structure
136
+ if (isValiError(error)) {
137
+ throw new ValidationFailed({
138
+ message: "Invalid DCQL query",
139
+ reason: error.issues.map((issue) => issue.message).join(", "),
140
+ });
141
+ }
142
+
143
+ if (error instanceof DcqlError) {
144
+ // TODO [SIW-2110]: handle invalid DQCL query or let the error propagate
145
+ }
146
+ if (error instanceof DcqlCredentialSetError) {
147
+ // TODO [SIW-2110]: handle missing credentials or let the error propagate
148
+ }
149
+ throw error;
150
+ }
151
+ };
152
+
153
+ export const prepareRemotePresentations: PrepareRemotePresentations = async (
154
+ credentials,
155
+ nonce,
156
+ clientId
157
+ ) => {
158
+ return Promise.all(
159
+ credentials.map(async (item) => {
160
+ const { vp_token } = await prepareVpToken(nonce, clientId, [
161
+ item.credential,
162
+ item.requestedClaims,
163
+ createCryptoContextFor(item.keyTag),
164
+ ]);
165
+
166
+ return {
167
+ credentialId: item.id,
168
+ requestedClaims: item.requestedClaims,
169
+ vpToken: vp_token,
170
+ format: "vc+sd-jwt",
171
+ };
172
+ })
173
+ );
174
+ };