@pagopa/io-react-native-wallet 0.28.1 → 0.29.0

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 (168) hide show
  1. package/README.md +43 -0
  2. package/lib/commonjs/credential/issuance/03-start-user-authorization.js +5 -0
  3. package/lib/commonjs/credential/issuance/03-start-user-authorization.js.map +1 -1
  4. package/lib/commonjs/credential/issuance/04-complete-user-authorization.js +17 -3
  5. package/lib/commonjs/credential/issuance/04-complete-user-authorization.js.map +1 -1
  6. package/lib/commonjs/credential/issuance/05-authorize-access.js +5 -0
  7. package/lib/commonjs/credential/issuance/05-authorize-access.js.map +1 -1
  8. package/lib/commonjs/credential/issuance/06-obtain-credential.js +13 -2
  9. package/lib/commonjs/credential/issuance/06-obtain-credential.js.map +1 -1
  10. package/lib/commonjs/credential/issuance/07-verify-and-parse-credential.js +10 -0
  11. package/lib/commonjs/credential/issuance/07-verify-and-parse-credential.js.map +1 -1
  12. package/lib/commonjs/credential/presentation/01-start-flow.js +14 -14
  13. package/lib/commonjs/credential/presentation/01-start-flow.js.map +1 -1
  14. package/lib/commonjs/credential/presentation/02-evaluate-rp-trust.js +4 -2
  15. package/lib/commonjs/credential/presentation/02-evaluate-rp-trust.js.map +1 -1
  16. package/lib/commonjs/credential/presentation/03-get-request-object.js +2 -2
  17. package/lib/commonjs/credential/presentation/03-get-request-object.js.map +1 -1
  18. package/lib/commonjs/credential/presentation/05-verify-request-object.js +11 -4
  19. package/lib/commonjs/credential/presentation/05-verify-request-object.js.map +1 -1
  20. package/lib/commonjs/credential/presentation/07-evaluate-dcql-query.js +54 -14
  21. package/lib/commonjs/credential/presentation/07-evaluate-dcql-query.js.map +1 -1
  22. package/lib/commonjs/credential/presentation/07-evaluate-input-descriptor.js +26 -7
  23. package/lib/commonjs/credential/presentation/07-evaluate-input-descriptor.js.map +1 -1
  24. package/lib/commonjs/credential/presentation/08-send-authorization-response.js +4 -4
  25. package/lib/commonjs/credential/presentation/08-send-authorization-response.js.map +1 -1
  26. package/lib/commonjs/credential/presentation/README.md +96 -2
  27. package/lib/commonjs/credential/presentation/errors.js +16 -19
  28. package/lib/commonjs/credential/presentation/errors.js.map +1 -1
  29. package/lib/commonjs/credential/presentation/index.js +27 -2
  30. package/lib/commonjs/credential/presentation/index.js.map +1 -1
  31. package/lib/commonjs/credential/presentation/types.js +1 -1
  32. package/lib/commonjs/credential/presentation/types.js.map +1 -1
  33. package/lib/commonjs/credential/status/02-status-attestation.js +2 -0
  34. package/lib/commonjs/credential/status/02-status-attestation.js.map +1 -1
  35. package/lib/commonjs/credential/status/03-verify-and-parse-status-attestation.js +3 -0
  36. package/lib/commonjs/credential/status/03-verify-and-parse-status-attestation.js.map +1 -1
  37. package/lib/commonjs/credential/trustmark/get-credential-trustmark.js +5 -0
  38. package/lib/commonjs/credential/trustmark/get-credential-trustmark.js.map +1 -1
  39. package/lib/commonjs/index.js +3 -1
  40. package/lib/commonjs/index.js.map +1 -1
  41. package/lib/commonjs/utils/decoder.js +2 -0
  42. package/lib/commonjs/utils/decoder.js.map +1 -1
  43. package/lib/commonjs/utils/logging.js +68 -0
  44. package/lib/commonjs/utils/logging.js.map +1 -0
  45. package/lib/commonjs/utils/misc.js +2 -0
  46. package/lib/commonjs/utils/misc.js.map +1 -1
  47. package/lib/commonjs/utils/par.js +2 -0
  48. package/lib/commonjs/utils/par.js.map +1 -1
  49. package/lib/commonjs/wallet-instance/index.js +4 -0
  50. package/lib/commonjs/wallet-instance/index.js.map +1 -1
  51. package/lib/commonjs/wallet-instance-attestation/issuing.js +5 -0
  52. package/lib/commonjs/wallet-instance-attestation/issuing.js.map +1 -1
  53. package/lib/module/credential/issuance/03-start-user-authorization.js +5 -0
  54. package/lib/module/credential/issuance/03-start-user-authorization.js.map +1 -1
  55. package/lib/module/credential/issuance/04-complete-user-authorization.js +17 -3
  56. package/lib/module/credential/issuance/04-complete-user-authorization.js.map +1 -1
  57. package/lib/module/credential/issuance/05-authorize-access.js +5 -0
  58. package/lib/module/credential/issuance/05-authorize-access.js.map +1 -1
  59. package/lib/module/credential/issuance/06-obtain-credential.js +13 -2
  60. package/lib/module/credential/issuance/06-obtain-credential.js.map +1 -1
  61. package/lib/module/credential/issuance/07-verify-and-parse-credential.js +10 -0
  62. package/lib/module/credential/issuance/07-verify-and-parse-credential.js.map +1 -1
  63. package/lib/module/credential/presentation/01-start-flow.js +14 -14
  64. package/lib/module/credential/presentation/01-start-flow.js.map +1 -1
  65. package/lib/module/credential/presentation/02-evaluate-rp-trust.js +4 -2
  66. package/lib/module/credential/presentation/02-evaluate-rp-trust.js.map +1 -1
  67. package/lib/module/credential/presentation/03-get-request-object.js +2 -2
  68. package/lib/module/credential/presentation/03-get-request-object.js.map +1 -1
  69. package/lib/module/credential/presentation/05-verify-request-object.js +11 -4
  70. package/lib/module/credential/presentation/05-verify-request-object.js.map +1 -1
  71. package/lib/module/credential/presentation/07-evaluate-dcql-query.js +55 -14
  72. package/lib/module/credential/presentation/07-evaluate-dcql-query.js.map +1 -1
  73. package/lib/module/credential/presentation/07-evaluate-input-descriptor.js +25 -6
  74. package/lib/module/credential/presentation/07-evaluate-input-descriptor.js.map +1 -1
  75. package/lib/module/credential/presentation/08-send-authorization-response.js +4 -4
  76. package/lib/module/credential/presentation/08-send-authorization-response.js.map +1 -1
  77. package/lib/module/credential/presentation/README.md +96 -2
  78. package/lib/module/credential/presentation/errors.js +13 -16
  79. package/lib/module/credential/presentation/errors.js.map +1 -1
  80. package/lib/module/credential/presentation/index.js +4 -3
  81. package/lib/module/credential/presentation/index.js.map +1 -1
  82. package/lib/module/credential/presentation/types.js +1 -1
  83. package/lib/module/credential/presentation/types.js.map +1 -1
  84. package/lib/module/credential/status/02-status-attestation.js +2 -0
  85. package/lib/module/credential/status/02-status-attestation.js.map +1 -1
  86. package/lib/module/credential/status/03-verify-and-parse-status-attestation.js +3 -0
  87. package/lib/module/credential/status/03-verify-and-parse-status-attestation.js.map +1 -1
  88. package/lib/module/credential/trustmark/get-credential-trustmark.js +5 -0
  89. package/lib/module/credential/trustmark/get-credential-trustmark.js.map +1 -1
  90. package/lib/module/index.js +2 -1
  91. package/lib/module/index.js.map +1 -1
  92. package/lib/module/utils/decoder.js +2 -0
  93. package/lib/module/utils/decoder.js.map +1 -1
  94. package/lib/module/utils/logging.js +62 -0
  95. package/lib/module/utils/logging.js.map +1 -0
  96. package/lib/module/utils/misc.js +2 -0
  97. package/lib/module/utils/misc.js.map +1 -1
  98. package/lib/module/utils/par.js +2 -0
  99. package/lib/module/utils/par.js.map +1 -1
  100. package/lib/module/wallet-instance/index.js +4 -0
  101. package/lib/module/wallet-instance/index.js.map +1 -1
  102. package/lib/module/wallet-instance-attestation/issuing.js +5 -0
  103. package/lib/module/wallet-instance-attestation/issuing.js.map +1 -1
  104. package/lib/typescript/credential/issuance/03-start-user-authorization.d.ts.map +1 -1
  105. package/lib/typescript/credential/issuance/04-complete-user-authorization.d.ts +2 -2
  106. package/lib/typescript/credential/issuance/04-complete-user-authorization.d.ts.map +1 -1
  107. package/lib/typescript/credential/issuance/05-authorize-access.d.ts.map +1 -1
  108. package/lib/typescript/credential/issuance/06-obtain-credential.d.ts +1 -1
  109. package/lib/typescript/credential/issuance/06-obtain-credential.d.ts.map +1 -1
  110. package/lib/typescript/credential/issuance/07-verify-and-parse-credential.d.ts.map +1 -1
  111. package/lib/typescript/credential/presentation/01-start-flow.d.ts +17 -19
  112. package/lib/typescript/credential/presentation/01-start-flow.d.ts.map +1 -1
  113. package/lib/typescript/credential/presentation/02-evaluate-rp-trust.d.ts +1 -0
  114. package/lib/typescript/credential/presentation/02-evaluate-rp-trust.d.ts.map +1 -1
  115. package/lib/typescript/credential/presentation/03-get-request-object.d.ts +1 -4
  116. package/lib/typescript/credential/presentation/03-get-request-object.d.ts.map +1 -1
  117. package/lib/typescript/credential/presentation/05-verify-request-object.d.ts +4 -2
  118. package/lib/typescript/credential/presentation/05-verify-request-object.d.ts.map +1 -1
  119. package/lib/typescript/credential/presentation/07-evaluate-dcql-query.d.ts +13 -5
  120. package/lib/typescript/credential/presentation/07-evaluate-dcql-query.d.ts.map +1 -1
  121. package/lib/typescript/credential/presentation/07-evaluate-input-descriptor.d.ts +7 -2
  122. package/lib/typescript/credential/presentation/07-evaluate-input-descriptor.d.ts.map +1 -1
  123. package/lib/typescript/credential/presentation/08-send-authorization-response.d.ts +3 -3
  124. package/lib/typescript/credential/presentation/08-send-authorization-response.d.ts.map +1 -1
  125. package/lib/typescript/credential/presentation/errors.d.ts +14 -9
  126. package/lib/typescript/credential/presentation/errors.d.ts.map +1 -1
  127. package/lib/typescript/credential/presentation/index.d.ts +5 -4
  128. package/lib/typescript/credential/presentation/index.d.ts.map +1 -1
  129. package/lib/typescript/credential/presentation/types.d.ts +3 -3
  130. package/lib/typescript/credential/status/02-status-attestation.d.ts.map +1 -1
  131. package/lib/typescript/credential/status/03-verify-and-parse-status-attestation.d.ts.map +1 -1
  132. package/lib/typescript/credential/trustmark/get-credential-trustmark.d.ts.map +1 -1
  133. package/lib/typescript/index.d.ts +2 -1
  134. package/lib/typescript/index.d.ts.map +1 -1
  135. package/lib/typescript/utils/decoder.d.ts.map +1 -1
  136. package/lib/typescript/utils/logging.d.ts +35 -0
  137. package/lib/typescript/utils/logging.d.ts.map +1 -0
  138. package/lib/typescript/utils/misc.d.ts.map +1 -1
  139. package/lib/typescript/utils/par.d.ts.map +1 -1
  140. package/lib/typescript/wallet-instance/index.d.ts.map +1 -1
  141. package/lib/typescript/wallet-instance-attestation/issuing.d.ts.map +1 -1
  142. package/package.json +3 -3
  143. package/src/credential/issuance/03-start-user-authorization.ts +18 -0
  144. package/src/credential/issuance/04-complete-user-authorization.ts +57 -3
  145. package/src/credential/issuance/05-authorize-access.ts +16 -0
  146. package/src/credential/issuance/06-obtain-credential.ts +31 -2
  147. package/src/credential/issuance/07-verify-and-parse-credential.ts +27 -1
  148. package/src/credential/presentation/01-start-flow.ts +18 -20
  149. package/src/credential/presentation/02-evaluate-rp-trust.ts +3 -2
  150. package/src/credential/presentation/03-get-request-object.ts +4 -6
  151. package/src/credential/presentation/05-verify-request-object.ts +17 -6
  152. package/src/credential/presentation/07-evaluate-dcql-query.ts +60 -17
  153. package/src/credential/presentation/07-evaluate-input-descriptor.ts +53 -39
  154. package/src/credential/presentation/08-send-authorization-response.ts +9 -7
  155. package/src/credential/presentation/README.md +96 -2
  156. package/src/credential/presentation/errors.ts +21 -14
  157. package/src/credential/presentation/index.ts +22 -4
  158. package/src/credential/presentation/types.ts +1 -1
  159. package/src/credential/status/02-status-attestation.ts +3 -0
  160. package/src/credential/status/03-verify-and-parse-status-attestation.ts +10 -0
  161. package/src/credential/trustmark/get-credential-trustmark.ts +19 -0
  162. package/src/index.ts +2 -0
  163. package/src/utils/decoder.ts +5 -0
  164. package/src/utils/logging.ts +68 -0
  165. package/src/utils/misc.ts +5 -0
  166. package/src/utils/par.ts +6 -0
  167. package/src/wallet-instance/index.ts +17 -1
  168. package/src/wallet-instance-attestation/issuing.ts +19 -0
@@ -10,6 +10,7 @@ import { ASSERTION_TYPE } from "./const";
10
10
  import { TokenResponse } from "./types";
11
11
  import { IssuerResponseError, ValidationFailed } from "../../utils/errors";
12
12
  import type { CompleteUserAuthorizationWithQueryMode } from "./04-complete-user-authorization";
13
+ import { LogLevel, Logger } from "../../utils/logging";
13
14
 
14
15
  export type AuthorizeAccess = (
15
16
  issuerConf: Out<EvaluateIssuerTrust>["issuerConf"],
@@ -76,6 +77,8 @@ export const authorizeAccess: AuthorizeAccess = async (
76
77
  dPopCryptoContext
77
78
  );
78
79
 
80
+ Logger.log(LogLevel.DEBUG, `Token request DPoP: ${tokenRequestSignedDPop}`);
81
+
79
82
  const signedWiaPoP = await createPopToken(
80
83
  {
81
84
  jti: `${uuidv4()}`,
@@ -85,6 +88,8 @@ export const authorizeAccess: AuthorizeAccess = async (
85
88
  wiaCryptoContext
86
89
  );
87
90
 
91
+ Logger.log(LogLevel.DEBUG, `WIA DPoP token: ${signedWiaPoP}`);
92
+
88
93
  const requestBody = {
89
94
  grant_type: "authorization_code",
90
95
  client_id: clientId,
@@ -96,6 +101,12 @@ export const authorizeAccess: AuthorizeAccess = async (
96
101
  };
97
102
 
98
103
  const authorizationRequestFormBody = new URLSearchParams(requestBody);
104
+
105
+ Logger.log(
106
+ LogLevel.DEBUG,
107
+ `Auth form request body: ${authorizationRequestFormBody}`
108
+ );
109
+
99
110
  const tokenRes = await appFetch(tokenUrl, {
100
111
  method: "POST",
101
112
  headers: {
@@ -109,6 +120,11 @@ export const authorizeAccess: AuthorizeAccess = async (
109
120
  .then((body) => TokenResponse.safeParse(body));
110
121
 
111
122
  if (!tokenRes.success) {
123
+ Logger.log(
124
+ LogLevel.ERROR,
125
+ `Token Response validation failed: ${tokenRes.error.message}`
126
+ );
127
+
112
128
  throw new ValidationFailed({
113
129
  message: "Token Response validation failed",
114
130
  reason: tokenRes.error.message,
@@ -17,6 +17,7 @@ import {
17
17
  import { CredentialResponse } from "./types";
18
18
  import { createDPopToken } from "../../utils/dpop";
19
19
  import { v4 as uuidv4 } from "uuid";
20
+ import { LogLevel, Logger } from "../../utils/logging";
20
21
 
21
22
  export type ObtainCredential = (
22
23
  issuerConf: Out<EvaluateIssuerTrust>["issuerConf"],
@@ -27,7 +28,8 @@ export type ObtainCredential = (
27
28
  dPopCryptoContext: CryptoContext;
28
29
  credentialCryptoContext: CryptoContext;
29
30
  appFetch?: GlobalFetch["fetch"];
30
- }
31
+ },
32
+ operationType?: "reissuing"
31
33
  ) => Promise<CredentialResponse>;
32
34
 
33
35
  export const createNonceProof = async (
@@ -73,7 +75,8 @@ export const obtainCredential: ObtainCredential = async (
73
75
  accessToken,
74
76
  clientId,
75
77
  credentialDefinition,
76
- context
78
+ context,
79
+ operationType
77
80
  ) => {
78
81
  const {
79
82
  credentialCryptoContext,
@@ -95,6 +98,8 @@ export const obtainCredential: ObtainCredential = async (
95
98
  credentialCryptoContext
96
99
  );
97
100
 
101
+ Logger.log(LogLevel.DEBUG, `Signed nonce proof: ${signedNonceProof}`);
102
+
98
103
  // Validation of accessTokenResponse.authorization_details if contain credentialDefinition
99
104
  const containsCredentialDefinition = accessToken.authorization_details.some(
100
105
  (c) =>
@@ -105,6 +110,10 @@ export const obtainCredential: ObtainCredential = async (
105
110
  );
106
111
 
107
112
  if (!containsCredentialDefinition) {
113
+ Logger.log(
114
+ LogLevel.ERROR,
115
+ `Credential definition not found in the access token response ${accessToken.authorization_details}`
116
+ );
108
117
  throw new ValidationFailed({
109
118
  message:
110
119
  "The access token response does not contain the requested credential",
@@ -123,6 +132,11 @@ export const obtainCredential: ObtainCredential = async (
123
132
  },
124
133
  };
125
134
 
135
+ Logger.log(
136
+ LogLevel.DEBUG,
137
+ `Credential request body: ${JSON.stringify(credentialRequestFormBody)}`
138
+ );
139
+
126
140
  const tokenRequestSignedDPop = await createDPopToken(
127
141
  {
128
142
  htm: "POST",
@@ -132,12 +146,16 @@ export const obtainCredential: ObtainCredential = async (
132
146
  },
133
147
  dPopCryptoContext
134
148
  );
149
+
150
+ Logger.log(LogLevel.DEBUG, `Token request DPoP: ${tokenRequestSignedDPop}`);
151
+
135
152
  const credentialRes = await appFetch(credentialUrl, {
136
153
  method: "POST",
137
154
  headers: {
138
155
  "Content-Type": "application/json",
139
156
  DPoP: tokenRequestSignedDPop,
140
157
  Authorization: `${accessToken.token_type} ${accessToken.access_token}`,
158
+ ...(operationType === "reissuing" && { operationType }),
141
159
  },
142
160
  body: JSON.stringify(credentialRequestFormBody),
143
161
  })
@@ -147,12 +165,21 @@ export const obtainCredential: ObtainCredential = async (
147
165
  .catch(handleObtainCredentialError);
148
166
 
149
167
  if (!credentialRes.success) {
168
+ Logger.log(
169
+ LogLevel.ERROR,
170
+ `Credential Response validation failed: ${credentialRes.error.message}`
171
+ );
150
172
  throw new ValidationFailed({
151
173
  message: "Credential Response validation failed",
152
174
  reason: credentialRes.error.message,
153
175
  });
154
176
  }
155
177
 
178
+ Logger.log(
179
+ LogLevel.DEBUG,
180
+ `Credential Response: ${JSON.stringify(credentialRes.data)}`
181
+ );
182
+
156
183
  return credentialRes.data;
157
184
  };
158
185
 
@@ -163,6 +190,8 @@ export const obtainCredential: ObtainCredential = async (
163
190
  * @throws {IssuerResponseError} with a specific code for more context
164
191
  */
165
192
  const handleObtainCredentialError = (e: unknown) => {
193
+ Logger.log(LogLevel.ERROR, `Error occurred while obtaining credential: ${e}`);
194
+
166
195
  if (!(e instanceof UnexpectedStatusCodeError)) {
167
196
  throw e;
168
197
  }
@@ -7,6 +7,7 @@ import { verify as verifySdJwt } from "../../sd-jwt";
7
7
  import { getValueFromDisclosures } from "../../sd-jwt/converters";
8
8
  import type { JWK } from "../../utils/jwk";
9
9
  import type { ObtainCredential } from "./06-obtain-credential";
10
+ import { LogLevel, Logger } from "../../utils/logging";
10
11
 
11
12
  export type VerifyAndParseCredential = (
12
13
  issuerConf: Out<EvaluateIssuerTrust>["issuerConf"],
@@ -62,10 +63,18 @@ const parseCredentialSdJwt = (
62
63
  const credentialSubject = credentials_supported[sdJwt.payload.vct];
63
64
 
64
65
  if (!credentialSubject) {
66
+ Logger.log(
67
+ LogLevel.ERROR,
68
+ `Credential type not supported by the issuer: ${sdJwt.payload.vct}`
69
+ );
65
70
  throw new IoWalletError("Credential type not supported by the issuer");
66
71
  }
67
72
 
68
73
  if (credentialSubject.format !== sdJwt.header.typ) {
74
+ Logger.log(
75
+ LogLevel.ERROR,
76
+ `Received credential is of an unknwown type. Expected one of [${credentialSubject.format}], received '${sdJwt.header.typ}'`
77
+ );
69
78
  throw new IoWalletError(
70
79
  `Received credential is of an unknwown type. Expected one of [${credentialSubject.format}], received '${sdJwt.header.typ}', `
71
80
  );
@@ -73,6 +82,7 @@ const parseCredentialSdJwt = (
73
82
 
74
83
  // transfrom a record { key: value } in an iterable of pairs [key, value]
75
84
  if (!credentialSubject.claims) {
85
+ Logger.log(LogLevel.ERROR, "Missing claims in the credential subject");
76
86
  throw new IoWalletError("Missing claims in the credential subject"); // TODO [SIW-1268]: should not be optional
77
87
  }
78
88
  const attrDefinitions = Object.entries(credentialSubject.claims);
@@ -85,6 +95,10 @@ const parseCredentialSdJwt = (
85
95
  const missing = attrsNotInDisclosures.map((_) => _[0 /* key */]).join(", ");
86
96
  const received = disclosures.map((_) => _[1 /* name */]).join(", ");
87
97
  if (!ignoreMissingAttributes) {
98
+ Logger.log(
99
+ LogLevel.ERROR,
100
+ `Some attributes are missing in the credential. Missing: [${missing}], received: [${received}]`
101
+ );
88
102
  throw new IoWalletError(
89
103
  `Some attributes are missing in the credential. Missing: [${missing}], received: [${received}]`
90
104
  );
@@ -172,6 +186,10 @@ async function verifyCredentialSdJwt(
172
186
  const { cnf } = decodedCredential.sdJwt.payload;
173
187
 
174
188
  if (!cnf.jwk.kid || cnf.jwk.kid !== holderBindingKey.kid) {
189
+ Logger.log(
190
+ LogLevel.ERROR,
191
+ `Failed to verify holder binding, expected kid: ${holderBindingKey.kid}, got: ${decodedCredential.sdJwt.payload.cnf.jwk.kid}`
192
+ );
175
193
  throw new IoWalletError(
176
194
  `Failed to verify holder binding, expected kid: ${holderBindingKey.kid}, got: ${decodedCredential.sdJwt.payload.cnf.jwk.kid}`
177
195
  );
@@ -204,15 +222,21 @@ const verifyAndParseCredentialSdJwt: WithFormat<"vc+sd-jwt"> = async (
204
222
  credentialCryptoContext
205
223
  );
206
224
 
225
+ Logger.log(LogLevel.DEBUG, `Decoded credential: ${JSON.stringify(decoded)}`);
226
+
207
227
  const parsedCredential = parseCredentialSdJwt(
208
228
  issuerConf.openid_credential_issuer.credential_configurations_supported,
209
229
  decoded,
210
230
  ignoreMissingAttributes,
211
231
  includeUndefinedAttributes
212
232
  );
213
-
214
233
  const maybeIssuedAt = getValueFromDisclosures(decoded.disclosures, "iat");
215
234
 
235
+ Logger.log(
236
+ LogLevel.DEBUG,
237
+ `Parsed credential: ${JSON.stringify(parsedCredential)}\nIssued at: ${maybeIssuedAt}`
238
+ );
239
+
216
240
  return {
217
241
  parsedCredential,
218
242
  expiration: new Date(decoded.sdJwt.payload.exp * 1000),
@@ -243,6 +267,7 @@ export const verifyAndParseCredential: VerifyAndParseCredential = async (
243
267
  context
244
268
  ) => {
245
269
  if (format === "vc+sd-jwt") {
270
+ Logger.log(LogLevel.DEBUG, "Parsing credential in vc+sd-jwt format");
246
271
  return verifyAndParseCredentialSdJwt(
247
272
  issuerConf,
248
273
  credential,
@@ -251,5 +276,6 @@ export const verifyAndParseCredential: VerifyAndParseCredential = async (
251
276
  );
252
277
  }
253
278
 
279
+ Logger.log(LogLevel.ERROR, `Unsupported credential format: ${format}`);
254
280
  throw new IoWalletError(`Unsupported credential format: ${format}`);
255
281
  };
@@ -1,10 +1,10 @@
1
1
  import * as z from "zod";
2
- import { ValidationFailed } from "../../utils/errors";
2
+ import { InvalidQRCodeError } from "./errors";
3
3
 
4
4
  const PresentationParams = z.object({
5
- clientId: z.string().nonempty(),
6
- requestUri: z.string().url(),
7
- requestUriMethod: z.enum(["get", "post"]),
5
+ client_id: z.string().nonempty(),
6
+ request_uri: z.string().url(),
7
+ request_uri_method: z.enum(["get", "post"]),
8
8
  state: z.string().optional(),
9
9
  });
10
10
  export type PresentationParams = z.infer<typeof PresentationParams>;
@@ -13,32 +13,30 @@ export type PresentationParams = z.infer<typeof PresentationParams>;
13
13
  * The beginning of the presentation flow.
14
14
  * To be implemented accordind to the user touchpoint
15
15
  *
16
- * @param params Presentation parameters, depending on the starting touchoint
16
+ * @param params Presentation parameters, depending on the starting touchpoint
17
17
  * @returns The url for the Relying Party to connect with
18
18
  */
19
- export type StartFlow = (params: PresentationParams) => {
20
- requestUri: string;
21
- clientId: string;
22
- requestUriMethod?: "get" | "post";
23
- state?: string;
24
- };
19
+ export type StartFlow = (params: {
20
+ [K in keyof PresentationParams]?: PresentationParams[K] | null;
21
+ }) => PresentationParams;
25
22
 
26
23
  /**
27
- * Start a presentation flow by decoding an incoming QR-code
24
+ * Start a presentation flow by validating the required parameters.
25
+ * Parameters are extracted from a url encoded in a QR code or in a deep link.
28
26
  *
29
- * @param params The encoded QR-code content
27
+ * @param params The parameters to be validated
30
28
  * @returns The url for the Relying Party to connect with
31
- * @throws If the provided qr code fails to be decoded
29
+ * @throws If the provided parameters are not valid
32
30
  */
33
31
  export const startFlowFromQR: StartFlow = (params) => {
34
- const result = PresentationParams.safeParse(params);
32
+ const result = PresentationParams.safeParse({
33
+ ...params,
34
+ request_uri_method: params.request_uri_method ?? "get",
35
+ });
35
36
 
36
37
  if (result.success) {
37
38
  return result.data;
38
- } else {
39
- throw new ValidationFailed({
40
- message: "Invalid parameters provided",
41
- reason: result.error.message,
42
- });
43
39
  }
40
+
41
+ throw new InvalidQRCodeError(result.error.message);
44
42
  };
@@ -10,6 +10,7 @@ export type EvaluateRelyingPartyTrust = (
10
10
  }
11
11
  ) => Promise<{
12
12
  rpConf: RelyingPartyEntityConfiguration["payload"]["metadata"];
13
+ subject: string;
13
14
  }>;
14
15
 
15
16
  /**
@@ -25,9 +26,9 @@ export const evaluateRelyingPartyTrust: EvaluateRelyingPartyTrust = async (
25
26
  { appFetch = fetch } = {}
26
27
  ) => {
27
28
  const {
28
- payload: { metadata: rpConf },
29
+ payload: { metadata: rpConf, sub },
29
30
  } = await getRelyingPartyEntityConfiguration(rpUrl, {
30
31
  appFetch,
31
32
  });
32
- return { rpConf };
33
+ return { rpConf, subject: sub };
33
34
  };
@@ -1,12 +1,10 @@
1
- import { hasStatusOrThrow, type Out } from "../../utils/misc";
2
- import type { StartFlow } from "./01-start-flow";
1
+ import { hasStatusOrThrow } from "../../utils/misc";
3
2
  import { RequestObjectWalletCapabilities } from "./types";
4
3
 
5
4
  export type GetRequestObject = (
6
- requestUri: Out<StartFlow>["requestUri"],
7
- context: {
5
+ requestUri: string,
6
+ context?: {
8
7
  appFetch?: GlobalFetch["fetch"];
9
- walletInstanceAttestation: string;
10
8
  walletCapabilities?: RequestObjectWalletCapabilities;
11
9
  }
12
10
  ) => Promise<{ requestObjectEncodedJwt: string }>;
@@ -23,7 +21,7 @@ export type GetRequestObject = (
23
21
  */
24
22
  export const getRequestObject: GetRequestObject = async (
25
23
  requestUri,
26
- { appFetch = fetch, walletCapabilities }
24
+ { appFetch = fetch, walletCapabilities } = {}
27
25
  ) => {
28
26
  if (walletCapabilities) {
29
27
  // Validate external input
@@ -8,8 +8,9 @@ export type VerifyRequestObject = (
8
8
  requestObjectEncodedJwt: string,
9
9
  context: {
10
10
  clientId: string;
11
- // jwkKeys: Out<FetchJwks>["keys"];
12
- rpConf: RelyingPartyEntityConfiguration["payload"];
11
+ rpConf: RelyingPartyEntityConfiguration["payload"]["metadata"];
12
+ rpSubject: string;
13
+ state?: string;
13
14
  }
14
15
  ) => Promise<{ requestObject: RequestObject }>;
15
16
 
@@ -17,16 +18,16 @@ export type VerifyRequestObject = (
17
18
  * Function to verify the Request Object's signature and the client ID.
18
19
  * @param requestObjectEncodedJwt The Request Object in JWT format
19
20
  * @param context.clientId The client ID to verify
20
- * @param context.jwkKeys The set of keys to verify the signature
21
21
  * @param context.rpConf The Entity Configuration of the Relying Party
22
+ * @param context.state Optional state
22
23
  * @returns The verified Request Object
23
24
  */
24
25
  export const verifyRequestObject: VerifyRequestObject = async (
25
26
  requestObjectEncodedJwt,
26
- { clientId, rpConf }
27
+ { clientId, rpConf, rpSubject, state }
27
28
  ) => {
28
29
  const requestObjectJwt = decodeJwt(requestObjectEncodedJwt);
29
- const { keys } = getJwksFromConfig(rpConf.metadata);
30
+ const { keys } = getJwksFromConfig(rpConf);
30
31
 
31
32
  // Verify token signature to ensure the request object is authentic
32
33
  const pubKey = keys?.find(
@@ -42,11 +43,21 @@ export const verifyRequestObject: VerifyRequestObject = async (
42
43
 
43
44
  const requestObject = RequestObject.parse(requestObjectJwt.payload);
44
45
 
45
- if (!(clientId === requestObject.client_id && clientId === rpConf.sub)) {
46
+ const isClientIdMatch =
47
+ clientId === requestObject.client_id && clientId === rpSubject;
48
+
49
+ if (!isClientIdMatch) {
46
50
  throw new UnverifiedEntityError(
47
51
  "Client ID does not match Request Object or Entity Configuration"
48
52
  );
49
53
  }
50
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
+
51
62
  return { requestObject };
52
63
  };
@@ -6,23 +6,33 @@ import {
6
6
  } from "dcql";
7
7
  import { isValiError } from "valibot";
8
8
  import { decode, prepareVpToken } from "../../sd-jwt";
9
- import type { Disclosure, DisclosureWithEncoded } from "../../sd-jwt/types";
9
+ import type { Disclosure } from "../../sd-jwt/types";
10
10
  import { ValidationFailed } from "../../utils/errors";
11
11
  import { createCryptoContextFor } from "../../utils/crypto";
12
12
  import type { RemotePresentation } from "./types";
13
+ import { CredentialsNotFoundError, type NotFoundDetail } from "./errors";
13
14
 
14
- type EvaluateDcqlQuery = (
15
+ /**
16
+ * The purpose for the credential request by the RP.
17
+ */
18
+ type CredentialPurpose = {
19
+ required: boolean;
20
+ description?: string;
21
+ };
22
+
23
+ export type EvaluateDcqlQuery = (
15
24
  credentialsSdJwt: [string /* keyTag */, string /* credential */][],
16
25
  query: DcqlQuery.Input
17
26
  ) => {
18
27
  id: string;
28
+ vct: string;
19
29
  credential: string;
20
30
  keyTag: string;
21
- requiredDisclosures: DisclosureWithEncoded[];
22
- isOptional?: boolean;
31
+ requiredDisclosures: Disclosure[];
32
+ purposes: CredentialPurpose[];
23
33
  }[];
24
34
 
25
- type PrepareRemotePresentations = (
35
+ export type PrepareRemotePresentations = (
26
36
  credentials: {
27
37
  id: string;
28
38
  credential: string;
@@ -38,6 +48,11 @@ type DcqlMatchSuccess = Extract<
38
48
  { success: true }
39
49
  >;
40
50
 
51
+ type DcqlMatchFailure = Extract<
52
+ DcqlQueryResult.CredentialMatch,
53
+ { success: false }
54
+ >;
55
+
41
56
  /**
42
57
  * Convert a credential in JWT format to an object with claims
43
58
  * for correct parsing by the `dcql` library.
@@ -72,6 +87,32 @@ const getDcqlQueryMatches = (result: DcqlQueryResult) =>
72
87
  ([, match]) => match.success === true
73
88
  ) as [string, DcqlMatchSuccess][];
74
89
 
90
+ /**
91
+ * Extract only failed matches from the DCQL query result.
92
+ */
93
+ const getDcqlQueryFailedMatches = (result: DcqlQueryResult) =>
94
+ Object.entries(result.credential_matches).filter(
95
+ ([, match]) => match.success === false
96
+ ) as [string, DcqlMatchFailure][];
97
+
98
+ /**
99
+ * Extract missing credentials from the DCQL query result.
100
+ * Note: here we are assuming a failed match is a missing credential,
101
+ * but there might be other reasons for its failure.
102
+ */
103
+ const extractMissingCredentials = (
104
+ queryResult: DcqlQueryResult,
105
+ originalQuery: DcqlQuery
106
+ ): NotFoundDetail[] => {
107
+ return getDcqlQueryFailedMatches(queryResult).map(([id]) => {
108
+ const credential = originalQuery.credentials.find((c) => c.id === id);
109
+ if (credential?.format !== "vc+sd-jwt") {
110
+ throw new Error("Unsupported format"); // TODO [SIW-2082]: support MDOC credentials
111
+ }
112
+ return { id, vctValues: credential.meta?.vct_values };
113
+ });
114
+ };
115
+
75
116
  export const evaluateDcqlQuery: EvaluateDcqlQuery = (
76
117
  credentialsSdJwt,
77
118
  query
@@ -88,7 +129,9 @@ export const evaluateDcqlQuery: EvaluateDcqlQuery = (
88
129
  const queryResult = DcqlQuery.query(parsedQuery, credentials);
89
130
 
90
131
  if (!queryResult.canBeSatisfied) {
91
- throw new Error("No credential can satisfy the provided DCQL query");
132
+ throw new CredentialsNotFoundError(
133
+ extractMissingCredentials(queryResult, parsedQuery)
134
+ );
92
135
  }
93
136
 
94
137
  // Build an object vct:credentialJwt to map matched credentials to their JWT
@@ -103,24 +146,24 @@ export const evaluateDcqlQuery: EvaluateDcqlQuery = (
103
146
  }
104
147
  const { vct, claims } = match.output;
105
148
 
106
- // Find a matching credential set to see whether the credential is optional
107
- // If no credential set is found, then the credential is required by default
108
- // NOTE: This is an extra, it might not be necessary
109
- const credentialSet = queryResult.credential_sets?.find((set) =>
110
- set.matching_options?.flat().includes(vct)
111
- );
112
- const isOptional = credentialSet ? !credentialSet.required : false;
149
+ const purposes = queryResult.credential_sets
150
+ ?.filter((set) => set.matching_options?.flat().includes(id))
151
+ ?.map<CredentialPurpose>((credentialSet) => ({
152
+ description: credentialSet.purpose?.toString(),
153
+ required: Boolean(credentialSet.required),
154
+ }));
113
155
 
114
156
  const [keyTag, credential] = credentialsSdJwtByVct[vct]!;
115
- const requiredDisclosures = Object.values(
116
- claims
117
- ) as DisclosureWithEncoded[];
157
+ const requiredDisclosures = Object.values(claims) as Disclosure[];
118
158
  return {
119
159
  id,
160
+ vct,
120
161
  keyTag,
121
162
  credential,
122
- isOptional,
123
163
  requiredDisclosures,
164
+ // When it is a match but no credential_sets are found, the credential is required by default
165
+ // See https://openid.net/specs/openid-4-verifiable-presentations-1_0-24.html#section-6.3.1.2-2.1
166
+ purposes: purposes ?? [{ required: true }],
124
167
  };
125
168
  });
126
169
  } catch (error) {
@@ -3,7 +3,7 @@ import { SdJwt4VC, type DisclosureWithEncoded } from "../../sd-jwt/types";
3
3
  import { decode, prepareVpToken } from "../../sd-jwt";
4
4
  import { createCryptoContextFor } from "../../utils/crypto";
5
5
  import { JSONPath } from "jsonpath-plus";
6
- import { CredentialNotFoundError, MissingDataError } from "./errors";
6
+ import { CredentialsNotFoundError, MissingDataError } from "./errors";
7
7
  import Ajv from "ajv";
8
8
 
9
9
  const ajv = new Ajv({ allErrors: true });
@@ -33,7 +33,10 @@ export type EvaluateInputDescriptors = (
33
33
  }[]
34
34
  >;
35
35
 
36
- export type PrepareRemotePresentations = (
36
+ /**
37
+ * @deprecated Use `prepareRemotePresentations` from DCQL
38
+ */
39
+ export type PrepareLegacyRemotePresentations = (
37
40
  credentialAndDescriptors: {
38
41
  requestedClaims: string[];
39
42
  inputDescriptor: InputDescriptor;
@@ -288,9 +291,12 @@ export const findCredentialSdJwt = (
288
291
  }
289
292
  }
290
293
 
291
- throw new CredentialNotFoundError(
292
- "None of the vc+sd-jwt credentials satisfy the requirements."
293
- );
294
+ throw new CredentialsNotFoundError([
295
+ {
296
+ id: "",
297
+ reason: "None of the vc+sd-jwt credentials satisfy the requirements.",
298
+ },
299
+ ]);
294
300
  };
295
301
 
296
302
  /**
@@ -322,9 +328,12 @@ export const evaluateInputDescriptors: EvaluateInputDescriptors = async (
322
328
  inputDescriptors.map(async (descriptor) => {
323
329
  if (descriptor.format?.["vc+sd-jwt"]) {
324
330
  if (!decodedSdJwtCredentials.length) {
325
- throw new CredentialNotFoundError(
326
- "vc+sd-jwt credential is not supported."
327
- );
331
+ throw new CredentialsNotFoundError([
332
+ {
333
+ id: descriptor.id,
334
+ reason: "vc+sd-jwt credential is not supported.",
335
+ },
336
+ ]);
328
337
  }
329
338
 
330
339
  const { matchedEvaluation, matchedKeyTag, matchedCredential } =
@@ -338,9 +347,12 @@ export const evaluateInputDescriptors: EvaluateInputDescriptors = async (
338
347
  };
339
348
  }
340
349
 
341
- throw new CredentialNotFoundError(
342
- `${descriptor.format} format is not supported.`
343
- );
350
+ throw new CredentialsNotFoundError([
351
+ {
352
+ id: descriptor.id,
353
+ reason: `${descriptor.format} format is not supported.`,
354
+ },
355
+ ]);
344
356
  })
345
357
  );
346
358
  };
@@ -352,6 +364,8 @@ export const evaluateInputDescriptors: EvaluateInputDescriptors = async (
352
364
  * - Validates the credential format.
353
365
  * - Generates a verifiable presentation token (vpToken) using the provided nonce and client identifier.
354
366
  *
367
+ * @deprecated Use `prepareRemotePresentations` from DCQL
368
+ *
355
369
  * @param credentialAndDescriptors - An array containing objects with requested claims,
356
370
  * input descriptor, credential, and keyTag.
357
371
  * @param nonce - A unique nonce for the verifiable presentation token.
@@ -359,33 +373,33 @@ export const evaluateInputDescriptors: EvaluateInputDescriptors = async (
359
373
  * @returns A promise that resolves to an array of RemotePresentation objects.
360
374
  * @throws {CredentialNotFoundError} When the credential format is unsupported.
361
375
  */
362
- export const prepareRemotePresentations: PrepareRemotePresentations = async (
363
- credentialAndDescriptors,
364
- nonce,
365
- client_id
366
- ) => {
367
- return Promise.all(
368
- credentialAndDescriptors.map(async (item) => {
369
- const descriptor = item.inputDescriptor;
376
+ export const prepareLegacyRemotePresentations: PrepareLegacyRemotePresentations =
377
+ async (credentialAndDescriptors, nonce, client_id) => {
378
+ return Promise.all(
379
+ credentialAndDescriptors.map(async (item) => {
380
+ const descriptor = item.inputDescriptor;
381
+
382
+ if (descriptor.format?.["vc+sd-jwt"]) {
383
+ const { vp_token } = await prepareVpToken(nonce, client_id, [
384
+ item.credential,
385
+ item.requestedClaims,
386
+ createCryptoContextFor(item.keyTag),
387
+ ]);
388
+
389
+ return {
390
+ requestedClaims: item.requestedClaims,
391
+ inputDescriptor: descriptor,
392
+ vpToken: vp_token,
393
+ format: "vc+sd-jwt",
394
+ };
395
+ }
370
396
 
371
- if (descriptor.format?.["vc+sd-jwt"]) {
372
- const { vp_token } = await prepareVpToken(nonce, client_id, [
373
- item.credential,
374
- item.requestedClaims,
375
- createCryptoContextFor(item.keyTag),
397
+ throw new CredentialsNotFoundError([
398
+ {
399
+ id: descriptor.id,
400
+ reason: `${descriptor.format} format is not supported.`,
401
+ },
376
402
  ]);
377
-
378
- return {
379
- requestedClaims: item.requestedClaims,
380
- inputDescriptor: descriptor,
381
- vpToken: vp_token,
382
- format: "vc+sd-jwt",
383
- };
384
- }
385
-
386
- throw new CredentialNotFoundError(
387
- `${descriptor.format} format is not supported.`
388
- );
389
- })
390
- );
391
- };
403
+ })
404
+ );
405
+ };