@pagopa/io-react-native-wallet 0.28.2 → 0.30.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 (173) 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 +12 -0
  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 +9 -8
  13. package/lib/commonjs/credential/presentation/01-start-flow.js.map +1 -1
  14. package/lib/commonjs/credential/presentation/03-get-request-object.js +3 -2
  15. package/lib/commonjs/credential/presentation/03-get-request-object.js.map +1 -1
  16. package/lib/commonjs/credential/presentation/05-verify-request-object.js +57 -22
  17. package/lib/commonjs/credential/presentation/05-verify-request-object.js.map +1 -1
  18. package/lib/commonjs/credential/presentation/07-evaluate-dcql-query.js +43 -16
  19. package/lib/commonjs/credential/presentation/07-evaluate-dcql-query.js.map +1 -1
  20. package/lib/commonjs/credential/presentation/07-evaluate-input-descriptor.js +16 -4
  21. package/lib/commonjs/credential/presentation/07-evaluate-input-descriptor.js.map +1 -1
  22. package/lib/commonjs/credential/presentation/08-send-authorization-response.js +85 -3
  23. package/lib/commonjs/credential/presentation/08-send-authorization-response.js.map +1 -1
  24. package/lib/commonjs/credential/presentation/README.md +27 -9
  25. package/lib/commonjs/credential/presentation/errors.js +28 -23
  26. package/lib/commonjs/credential/presentation/errors.js.map +1 -1
  27. package/lib/commonjs/credential/presentation/index.js +6 -0
  28. package/lib/commonjs/credential/presentation/index.js.map +1 -1
  29. package/lib/commonjs/credential/presentation/types.js +14 -7
  30. package/lib/commonjs/credential/presentation/types.js.map +1 -1
  31. package/lib/commonjs/credential/status/02-status-attestation.js +2 -0
  32. package/lib/commonjs/credential/status/02-status-attestation.js.map +1 -1
  33. package/lib/commonjs/credential/status/03-verify-and-parse-status-attestation.js +3 -0
  34. package/lib/commonjs/credential/status/03-verify-and-parse-status-attestation.js.map +1 -1
  35. package/lib/commonjs/credential/trustmark/get-credential-trustmark.js +5 -0
  36. package/lib/commonjs/credential/trustmark/get-credential-trustmark.js.map +1 -1
  37. package/lib/commonjs/index.js +3 -1
  38. package/lib/commonjs/index.js.map +1 -1
  39. package/lib/commonjs/utils/decoder.js +2 -0
  40. package/lib/commonjs/utils/decoder.js.map +1 -1
  41. package/lib/commonjs/utils/error-codes.js +9 -1
  42. package/lib/commonjs/utils/error-codes.js.map +1 -1
  43. package/lib/commonjs/utils/errors.js +31 -14
  44. package/lib/commonjs/utils/errors.js.map +1 -1
  45. package/lib/commonjs/utils/logging.js +68 -0
  46. package/lib/commonjs/utils/logging.js.map +1 -0
  47. package/lib/commonjs/utils/misc.js +2 -0
  48. package/lib/commonjs/utils/misc.js.map +1 -1
  49. package/lib/commonjs/utils/par.js +2 -0
  50. package/lib/commonjs/utils/par.js.map +1 -1
  51. package/lib/commonjs/wallet-instance/index.js +4 -0
  52. package/lib/commonjs/wallet-instance/index.js.map +1 -1
  53. package/lib/commonjs/wallet-instance-attestation/issuing.js +5 -0
  54. package/lib/commonjs/wallet-instance-attestation/issuing.js.map +1 -1
  55. package/lib/module/credential/issuance/03-start-user-authorization.js +5 -0
  56. package/lib/module/credential/issuance/03-start-user-authorization.js.map +1 -1
  57. package/lib/module/credential/issuance/04-complete-user-authorization.js +12 -0
  58. package/lib/module/credential/issuance/04-complete-user-authorization.js.map +1 -1
  59. package/lib/module/credential/issuance/05-authorize-access.js +5 -0
  60. package/lib/module/credential/issuance/05-authorize-access.js.map +1 -1
  61. package/lib/module/credential/issuance/06-obtain-credential.js +13 -2
  62. package/lib/module/credential/issuance/06-obtain-credential.js.map +1 -1
  63. package/lib/module/credential/issuance/07-verify-and-parse-credential.js +10 -0
  64. package/lib/module/credential/issuance/07-verify-and-parse-credential.js.map +1 -1
  65. package/lib/module/credential/presentation/01-start-flow.js +9 -8
  66. package/lib/module/credential/presentation/01-start-flow.js.map +1 -1
  67. package/lib/module/credential/presentation/03-get-request-object.js +3 -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 +58 -23
  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 +44 -17
  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 +17 -5
  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 +82 -1
  76. package/lib/module/credential/presentation/08-send-authorization-response.js.map +1 -1
  77. package/lib/module/credential/presentation/README.md +27 -9
  78. package/lib/module/credential/presentation/errors.js +17 -19
  79. package/lib/module/credential/presentation/errors.js.map +1 -1
  80. package/lib/module/credential/presentation/index.js +2 -2
  81. package/lib/module/credential/presentation/index.js.map +1 -1
  82. package/lib/module/credential/presentation/types.js +12 -6
  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/error-codes.js +7 -0
  95. package/lib/module/utils/error-codes.js.map +1 -1
  96. package/lib/module/utils/errors.js +23 -14
  97. package/lib/module/utils/errors.js.map +1 -1
  98. package/lib/module/utils/logging.js +62 -0
  99. package/lib/module/utils/logging.js.map +1 -0
  100. package/lib/module/utils/misc.js +2 -0
  101. package/lib/module/utils/misc.js.map +1 -1
  102. package/lib/module/utils/par.js +2 -0
  103. package/lib/module/utils/par.js.map +1 -1
  104. package/lib/module/wallet-instance/index.js +4 -0
  105. package/lib/module/wallet-instance/index.js.map +1 -1
  106. package/lib/module/wallet-instance-attestation/issuing.js +5 -0
  107. package/lib/module/wallet-instance-attestation/issuing.js.map +1 -1
  108. package/lib/typescript/credential/issuance/03-start-user-authorization.d.ts.map +1 -1
  109. package/lib/typescript/credential/issuance/04-complete-user-authorization.d.ts.map +1 -1
  110. package/lib/typescript/credential/issuance/05-authorize-access.d.ts.map +1 -1
  111. package/lib/typescript/credential/issuance/06-obtain-credential.d.ts +1 -1
  112. package/lib/typescript/credential/issuance/06-obtain-credential.d.ts.map +1 -1
  113. package/lib/typescript/credential/issuance/07-verify-and-parse-credential.d.ts.map +1 -1
  114. package/lib/typescript/credential/presentation/01-start-flow.d.ts +17 -14
  115. package/lib/typescript/credential/presentation/01-start-flow.d.ts.map +1 -1
  116. package/lib/typescript/credential/presentation/03-get-request-object.d.ts +1 -3
  117. package/lib/typescript/credential/presentation/03-get-request-object.d.ts.map +1 -1
  118. package/lib/typescript/credential/presentation/05-verify-request-object.d.ts +2 -1
  119. package/lib/typescript/credential/presentation/05-verify-request-object.d.ts.map +1 -1
  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.map +1 -1
  122. package/lib/typescript/credential/presentation/08-send-authorization-response.d.ts +30 -2
  123. package/lib/typescript/credential/presentation/08-send-authorization-response.d.ts.map +1 -1
  124. package/lib/typescript/credential/presentation/errors.d.ts +17 -12
  125. package/lib/typescript/credential/presentation/errors.d.ts.map +1 -1
  126. package/lib/typescript/credential/presentation/index.d.ts +3 -3
  127. package/lib/typescript/credential/presentation/index.d.ts.map +1 -1
  128. package/lib/typescript/credential/presentation/types.d.ts +24 -17
  129. package/lib/typescript/credential/presentation/types.d.ts.map +1 -1
  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/error-codes.d.ts +8 -0
  137. package/lib/typescript/utils/error-codes.d.ts.map +1 -1
  138. package/lib/typescript/utils/errors.d.ts +32 -18
  139. package/lib/typescript/utils/errors.d.ts.map +1 -1
  140. package/lib/typescript/utils/logging.d.ts +35 -0
  141. package/lib/typescript/utils/logging.d.ts.map +1 -0
  142. package/lib/typescript/utils/misc.d.ts.map +1 -1
  143. package/lib/typescript/utils/par.d.ts.map +1 -1
  144. package/lib/typescript/wallet-instance/index.d.ts.map +1 -1
  145. package/lib/typescript/wallet-instance-attestation/issuing.d.ts.map +1 -1
  146. package/package.json +1 -1
  147. package/src/credential/issuance/03-start-user-authorization.ts +18 -0
  148. package/src/credential/issuance/04-complete-user-authorization.ts +51 -0
  149. package/src/credential/issuance/05-authorize-access.ts +16 -0
  150. package/src/credential/issuance/06-obtain-credential.ts +31 -2
  151. package/src/credential/issuance/07-verify-and-parse-credential.ts +27 -1
  152. package/src/credential/presentation/01-start-flow.ts +12 -11
  153. package/src/credential/presentation/03-get-request-object.ts +5 -5
  154. package/src/credential/presentation/05-verify-request-object.ts +73 -15
  155. package/src/credential/presentation/07-evaluate-dcql-query.ts +43 -18
  156. package/src/credential/presentation/07-evaluate-input-descriptor.ts +25 -13
  157. package/src/credential/presentation/08-send-authorization-response.ts +110 -3
  158. package/src/credential/presentation/README.md +27 -9
  159. package/src/credential/presentation/errors.ts +24 -17
  160. package/src/credential/presentation/index.ts +4 -0
  161. package/src/credential/presentation/types.ts +22 -10
  162. package/src/credential/status/02-status-attestation.ts +3 -0
  163. package/src/credential/status/03-verify-and-parse-status-attestation.ts +10 -0
  164. package/src/credential/trustmark/get-credential-trustmark.ts +19 -0
  165. package/src/index.ts +2 -0
  166. package/src/utils/decoder.ts +5 -0
  167. package/src/utils/error-codes.ts +11 -0
  168. package/src/utils/errors.ts +59 -29
  169. package/src/utils/logging.ts +68 -0
  170. package/src/utils/misc.ts +5 -0
  171. package/src/utils/par.ts +6 -0
  172. package/src/wallet-instance/index.ts +17 -1
  173. package/src/wallet-instance-attestation/issuing.ts +19 -0
@@ -7,12 +7,18 @@ import { hasStatusOrThrow, type Out } from "../../utils/misc";
7
7
  import {
8
8
  type RemotePresentation,
9
9
  DirectAuthorizationBodyPayload,
10
+ ErrorResponse,
10
11
  type LegacyRemotePresentation,
11
- LegacyDirectAuthorizationBodyPayload,
12
12
  } from "./types";
13
13
  import * as z from "zod";
14
14
  import type { JWK } from "../../utils/jwk";
15
15
  import type { RelyingPartyEntityConfiguration } from "../../trust";
16
+ import {
17
+ RelyingPartyResponseError,
18
+ ResponseErrorBuilder,
19
+ UnexpectedStatusCodeError,
20
+ RelyingPartyResponseErrorCodes,
21
+ } from "../../utils/errors";
16
22
 
17
23
  export type AuthorizationResponse = z.infer<typeof AuthorizationResponse>;
18
24
  export const AuthorizationResponse = z.object({
@@ -61,7 +67,7 @@ export const choosePublicKeyToEncrypt = (
61
67
  export const buildDirectPostJwtBody = async (
62
68
  requestObject: Out<VerifyRequestObject>["requestObject"],
63
69
  rpConf: RelyingPartyEntityConfiguration["payload"]["metadata"],
64
- payload: DirectAuthorizationBodyPayload | LegacyDirectAuthorizationBodyPayload
70
+ payload: DirectAuthorizationBodyPayload
65
71
  ): Promise<string> => {
66
72
  type Jwe = ConstructorParameters<typeof EncryptJwe>[1];
67
73
 
@@ -98,6 +104,34 @@ export const buildDirectPostJwtBody = async (
98
104
  return formBody.toString();
99
105
  };
100
106
 
107
+ /**
108
+ * Builds a URL-encoded form body for a direct POST response without encryption.
109
+ *
110
+ * @param requestObject - Contains state, nonce, and other relevant info.
111
+ * @param payload - Object that contains either the VP token to encrypt and the stringified mapping of the credential disclosures or the error code
112
+ * @returns A URL-encoded string suitable for an `application/x-www-form-urlencoded` POST body.
113
+ */
114
+ export const buildDirectPostBody = async (
115
+ requestObject: Out<VerifyRequestObject>["requestObject"],
116
+ payload: DirectAuthorizationBodyPayload
117
+ ): Promise<string> => {
118
+ const formUrlEncodedBody = new URLSearchParams({
119
+ ...(requestObject.state && { state: requestObject.state }),
120
+ ...Object.entries(payload).reduce(
121
+ (acc, [key, value]) => ({
122
+ ...acc,
123
+ [key]:
124
+ Array.isArray(value) || typeof value === "object"
125
+ ? JSON.stringify(value)
126
+ : value,
127
+ }),
128
+ {} as Record<string, string>
129
+ ),
130
+ });
131
+
132
+ return formUrlEncodedBody.toString();
133
+ };
134
+
101
135
  /**
102
136
  * Type definition for the function that sends the authorization response
103
137
  * to the Relying Party, completing the presentation flow.
@@ -218,5 +252,78 @@ export const sendAuthorizationResponse: SendAuthorizationResponse = async (
218
252
  })
219
253
  .then(hasStatusOrThrow(200))
220
254
  .then((res) => res.json())
221
- .then(AuthorizationResponse.parse);
255
+ .then(AuthorizationResponse.parse)
256
+ .catch(handleAuthorizationResponseError);
257
+ };
258
+
259
+ /**
260
+ * Type definition for the function that sends the authorization response
261
+ * to the Relying Party, completing the presentation flow.
262
+ */
263
+ export type SendAuthorizationErrorResponse = (
264
+ requestObject: Out<VerifyRequestObject>["requestObject"],
265
+ error: { error: ErrorResponse; errorDescription: string },
266
+ context?: {
267
+ appFetch?: GlobalFetch["fetch"];
268
+ }
269
+ ) => Promise<AuthorizationResponse>;
270
+
271
+ /**
272
+ * Sends the authorization error response to the Relying Party (RP) using the specified `response_mode`.
273
+ * This function completes the presentation flow in an OpenID 4 Verifiable Presentations scenario.
274
+ *
275
+ * @param requestObject - The request details, including presentation requirements.
276
+ * @param error - The response error value, with description
277
+ * @param context - Contains optional custom fetch implementation.
278
+ * @returns Parsed and validated authorization response from the Relying Party.
279
+ */
280
+ export const sendAuthorizationErrorResponse: SendAuthorizationErrorResponse =
281
+ async (
282
+ requestObject,
283
+ { error, errorDescription },
284
+ { appFetch = fetch } = {}
285
+ ): Promise<AuthorizationResponse> => {
286
+ const requestBody = await buildDirectPostBody(requestObject, {
287
+ error,
288
+ error_description: errorDescription,
289
+ });
290
+
291
+ return await appFetch(requestObject.response_uri, {
292
+ method: "POST",
293
+ headers: {
294
+ "Content-Type": "application/x-www-form-urlencoded",
295
+ },
296
+ body: requestBody,
297
+ })
298
+ .then(hasStatusOrThrow(200, RelyingPartyResponseError))
299
+ .then((res) => res.json())
300
+ .then(AuthorizationResponse.parse);
301
+ };
302
+
303
+ /**
304
+ * Handle the the presentation error by mapping it to a custom exception.
305
+ * If the error is not an instance of {@link UnexpectedStatusCodeError}, it is thrown as is.
306
+ * @param e - The error to be handled
307
+ * @throws {RelyingPartyResponseError} with a specific code for more context
308
+ */
309
+ const handleAuthorizationResponseError = (e: unknown) => {
310
+ if (!(e instanceof UnexpectedStatusCodeError)) {
311
+ throw e;
312
+ }
313
+
314
+ throw new ResponseErrorBuilder(RelyingPartyResponseError)
315
+ .handle(400, {
316
+ code: RelyingPartyResponseErrorCodes.InvalidAuthorizationResponse,
317
+ message:
318
+ "The Authorization Response contains invalid parameters or it is malformed",
319
+ })
320
+ .handle(403, {
321
+ code: RelyingPartyResponseErrorCodes.InvalidAuthorizationResponse,
322
+ message: "The Authorization Response was forbidden",
323
+ })
324
+ .handle("*", {
325
+ code: RelyingPartyResponseErrorCodes.RelyingPartyGenericError,
326
+ message: "Unable to successfully send the Authorization Response",
327
+ })
328
+ .buildFrom(e);
222
329
  };
@@ -15,12 +15,30 @@ sequenceDiagram
15
15
  O->>+I: QR-CODE: Authorization Request (`request_uri`)
16
16
  I->>+O: GET: Verifier's Entity Configuration
17
17
  O->>+I: Respond with metadata (including public keys)
18
- I->>+O: GET: Request Object, resolved from the `request_uri`
18
+ I->>+O: GET: Request Object, resolved from `request_uri`
19
19
  O->>+I: Respond with the Request Object
20
- I->>+O: POST: VP token encrypted response
21
- O->>+I: Redirect: Authorization Response
20
+ I->>+I: Validate Request Object and give consent
21
+ I->>+O: POST: Authorization Response with encrypted VP token
22
+ O->>+I: Respond with optional `redirect_uri`
22
23
  ```
23
24
 
25
+ ## Mapped results
26
+
27
+ | Error | Description|
28
+ | --------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ |
29
+ | `InvalidRequestObject` | The Request Object is not valid, for instance it is malformed or its signature cannot be verified. |
30
+ | `DcqlError` | The DCQL query cannot be evaluated because it contains errors. |
31
+ | `CredentialsNotFoundError` | The presentation cannot be completed because the Wallet does not contain all requested credentials. The missing credentials can be found in `details`. |
32
+ | `RelyingPartyResponseError` | Error in the Relying Party's response. See the next table for more details. |
33
+
34
+ #### RelyingPartyResponseError
35
+ The following HTTP errors are mapped to a `RelyingPartyResponseError` with specific codes.
36
+
37
+ | HTTP Status | Error Code | Description |
38
+ | ------------ | --------------------------------------- | ------------------------------------------------------------------------------------------------------------ |
39
+ | `400`, `403` | `ERR_RP_INVALID_AUTHORIZATION_RESPONSE` | The Relying Party rejected the Authorization Response sent by the Wallet because it was deemed invalid. |
40
+ | `*` | `ERR_RP_GENERIC_ERROR` | This is a generic error code to map unexpected errors that occurred when interacting with the Relying Party. |
41
+
24
42
 
25
43
  ## Examples
26
44
 
@@ -35,23 +53,23 @@ const qrCodeParams = decodeQrCode(qrCode)
35
53
 
36
54
  // Start the issuance flow
37
55
  const {
38
- requestUri,
39
- clientId,
40
- requestUriMethod,
56
+ request_uri,
57
+ client_id,
58
+ request_uri_method,
41
59
  state
42
60
  } = Credential.Presentation.startFlowFromQR(qrCodeParams);
43
61
 
44
62
  // Get the Relying Party's Entity Configuration and evaluate trust
45
- const { rpConf } = await Credential.Presentation.evaluateRelyingPartyTrust(clientId);
63
+ const { rpConf } = await Credential.Presentation.evaluateRelyingPartyTrust(client_id);
46
64
 
47
65
  // Get the Request Object from the RP
48
66
  const { requestObjectEncodedJwt } =
49
- await Credential.Presentation.getRequestObject(requestUri);
67
+ await Credential.Presentation.getRequestObject(request_uri);
50
68
 
51
69
  // Validate the Request Object
52
70
  const { requestObject } = await Credential.Presentation.verifyRequestObject(
53
71
  requestObjectEncodedJwt,
54
- { clientId, rpConf }
72
+ { clientId: client_id, rpConf }
55
73
  );
56
74
 
57
75
  // All the credentials that might be requested by the Relying Party
@@ -1,4 +1,5 @@
1
1
  import { IoWalletError, serializeAttrs } from "../../utils/errors";
2
+ export { DcqlError } from "dcql";
2
3
 
3
4
  /**
4
5
  * An error subclass thrown when auth request decode fail
@@ -57,18 +58,17 @@ export class InvalidQRCodeError extends IoWalletError {
57
58
  }
58
59
 
59
60
  /**
60
- * When the entity is unverified because the Relying Party is not trusted.
61
- *
61
+ * When the Request Object sent by the Relying Party is not valid
62
62
  */
63
- export class UnverifiedEntityError extends IoWalletError {
64
- code = "ERR_UNVERIFIED_RP_ENTITY";
63
+ export class InvalidRequestObjectError extends IoWalletError {
64
+ code = "ERR_INVALID_REQUEST_OBJECT";
65
65
 
66
- /**
67
- * @param reason A description of why the entity cannot be verified.
68
- */
69
- constructor(reason: string) {
70
- const message = `Unverified entity: ${reason}.`;
66
+ /** Detailed reason for the Request Object validation failure. */
67
+ reason: string;
68
+
69
+ constructor(message: string, reason = "unspecified") {
71
70
  super(message);
71
+ this.reason = reason;
72
72
  }
73
73
  }
74
74
 
@@ -88,18 +88,25 @@ export class MissingDataError extends IoWalletError {
88
88
  }
89
89
  }
90
90
 
91
+ export type NotFoundDetail = {
92
+ id: string;
93
+ reason?: string;
94
+ vctValues?: string[];
95
+ };
96
+
91
97
  /**
92
- * When a credential is not found in the wallet.
93
- *
98
+ * Error thrown when one or more credentials cannot be found in the wallet
99
+ * and the presentation request cannot be satisfied.
94
100
  */
95
- export class CredentialNotFoundError extends IoWalletError {
96
- code = "ERR_CREDENTIAL_NOT_FOUND";
101
+ export class CredentialsNotFoundError extends IoWalletError {
102
+ code = "ERR_CREDENTIALS_NOT_FOUND";
103
+ details: NotFoundDetail[];
97
104
 
98
105
  /**
99
- * @param credentialId The ID of the credential that was not found.
106
+ * @param details The details of the credentials that could not be found.
100
107
  */
101
- constructor(credentialId: string) {
102
- const message = `Credential not found: ${credentialId}.`;
103
- super(message);
108
+ constructor(details: NotFoundDetail[]) {
109
+ super("One or more credentials cannot be found in the wallet");
110
+ this.details = details;
104
111
  }
105
112
  }
@@ -33,6 +33,8 @@ import {
33
33
  type SendAuthorizationResponse,
34
34
  sendLegacyAuthorizationResponse,
35
35
  type SendLegacyAuthorizationResponse,
36
+ sendAuthorizationErrorResponse,
37
+ type SendAuthorizationErrorResponse,
36
38
  } from "./08-send-authorization-response";
37
39
  import * as Errors from "./errors";
38
40
 
@@ -49,6 +51,7 @@ export {
49
51
  prepareRemotePresentations,
50
52
  sendAuthorizationResponse,
51
53
  sendLegacyAuthorizationResponse,
54
+ sendAuthorizationErrorResponse,
52
55
  Errors,
53
56
  };
54
57
  export type {
@@ -64,4 +67,5 @@ export type {
64
67
  PrepareRemotePresentations,
65
68
  SendAuthorizationResponse,
66
69
  SendLegacyAuthorizationResponse,
70
+ SendAuthorizationErrorResponse,
67
71
  };
@@ -133,26 +133,38 @@ export const RequestObjectWalletCapabilities = z.object({
133
133
  });
134
134
 
135
135
  /**
136
- * Authorization Response payload when using `presentation_definition`.
137
- * @deprecated Use `DirectAuthorizationBodyPayload`
136
+ * This type models the possible error responses the OpenID4VP protocol allows for a presentation of a credential.
137
+ * When the Wallet encounters one of these errors, it will notify the Relying Party through the `response_uri` endpoint.
138
+ * See https://italia.github.io/eid-wallet-it-docs/versione-corrente/en/pid-eaa-presentation.html#authorization-response-errors for more information.
138
139
  */
139
- export type LegacyDirectAuthorizationBodyPayload = z.infer<
140
- typeof LegacyDirectAuthorizationBodyPayload
141
- >;
140
+ export type ErrorResponse = z.infer<typeof ErrorResponse>;
141
+ export const ErrorResponse = z.enum([
142
+ "invalid_request_object",
143
+ "invalid_request_uri",
144
+ "vp_formats_not_supported",
145
+ "invalid_request",
146
+ "access_denied",
147
+ "invalid_client",
148
+ ]);
149
+
142
150
  /**
143
151
  * @deprecated Use `DirectAuthorizationBodyPayload`
144
152
  */
145
- export const LegacyDirectAuthorizationBodyPayload = z.object({
153
+ const LegacyDirectAuthorizationBodyPayload = z.object({
146
154
  vp_token: z.union([z.string(), z.array(z.string())]).optional(),
147
155
  presentation_submission: z.record(z.string(), z.unknown()),
148
156
  });
149
157
 
150
158
  /**
151
- * Authorization Response payload when using DCQL queries.
159
+ * Authorization Response payload sent to the Relying Party.
152
160
  */
153
161
  export type DirectAuthorizationBodyPayload = z.infer<
154
162
  typeof DirectAuthorizationBodyPayload
155
163
  >;
156
- export const DirectAuthorizationBodyPayload = z.object({
157
- vp_token: z.record(z.string(), z.string()),
158
- });
164
+ export const DirectAuthorizationBodyPayload = z.union([
165
+ z.object({
166
+ vp_token: z.record(z.string(), z.string()),
167
+ }),
168
+ z.object({ error: ErrorResponse, error_description: z.string() }),
169
+ LegacyDirectAuthorizationBodyPayload,
170
+ ]);
@@ -13,6 +13,7 @@ import {
13
13
  ResponseErrorBuilder,
14
14
  UnexpectedStatusCodeError,
15
15
  } from "../../utils/errors";
16
+ import { LogLevel, Logger } from "../../utils/logging";
16
17
 
17
18
  export type StatusAttestation = (
18
19
  issuerConf: Out<EvaluateIssuerTrust>["issuerConf"],
@@ -63,6 +64,8 @@ export const statusAttestation: StatusAttestation = async (
63
64
  credential_pop: credentialPop,
64
65
  };
65
66
 
67
+ Logger.log(LogLevel.DEBUG, `Credential pop: ${credentialPop}`);
68
+
66
69
  const result = await appFetch(statusAttUrl, {
67
70
  method: "POST",
68
71
  headers: {
@@ -4,6 +4,7 @@ import { verify, type CryptoContext } from "@pagopa/io-react-native-jwt";
4
4
  import type { EvaluateIssuerTrust, StatusAttestation } from "../status";
5
5
  import { ParsedStatusAttestation } from "./types";
6
6
  import { decode as decodeJwt } from "@pagopa/io-react-native-jwt";
7
+ import { LogLevel, Logger } from "../../utils/logging";
7
8
 
8
9
  export type VerifyAndParseStatusAttestation = (
9
10
  issuerConf: Out<EvaluateIssuerTrust>["issuerConf"],
@@ -43,9 +44,18 @@ export const verifyAndParseStatusAttestation: VerifyAndParseStatusAttestation =
43
44
  payload: decodedJwt.payload,
44
45
  });
45
46
 
47
+ Logger.log(
48
+ LogLevel.DEBUG,
49
+ `Parsed status attestation: ${JSON.stringify(parsedStatusAttestation)}`
50
+ );
51
+
46
52
  const holderBindingKey = await credentialCryptoContext.getPublicKey();
47
53
  const { cnf } = parsedStatusAttestation.payload;
48
54
  if (!cnf.jwk.kid || cnf.jwk.kid !== holderBindingKey.kid) {
55
+ Logger.log(
56
+ LogLevel.ERROR,
57
+ `Failed to verify holder binding for status attestation, expected kid: ${holderBindingKey.kid}, got: ${parsedStatusAttestation.payload.cnf.jwk.kid}`
58
+ );
49
59
  throw new IoWalletError(
50
60
  `Failed to verify holder binding for status attestation, expected kid: ${holderBindingKey.kid}, got: ${parsedStatusAttestation.payload.cnf.jwk.kid}`
51
61
  );
@@ -7,6 +7,7 @@ import {
7
7
  import * as WalletInstanceAttestation from "../../wallet-instance-attestation";
8
8
  import { IoWalletError } from "../../utils/errors";
9
9
  import { obfuscateString } from "../../utils/string";
10
+ import { LogLevel, Logger } from "../../utils/logging";
10
11
 
11
12
  export type GetCredentialTrustmarkJwt = (params: {
12
13
  /**
@@ -73,10 +74,19 @@ export const getCredentialTrustmark: GetCredentialTrustmarkJwt = async ({
73
74
  walletInstanceAttestation
74
75
  );
75
76
 
77
+ Logger.log(
78
+ LogLevel.DEBUG,
79
+ `Decoded wia ${JSON.stringify(decodedWia.payload)} with holder binding key ${JSON.stringify(holderBindingKey)}`
80
+ );
81
+
76
82
  /**
77
83
  * Check that the WIA is not expired
78
84
  */
79
85
  if (decodedWia.payload.exp * 1000 < Date.now()) {
86
+ Logger.log(
87
+ LogLevel.ERROR,
88
+ `Wallet Instance Attestation expired with exp: ${decodedWia.payload.exp}`
89
+ );
80
90
  throw new IoWalletError("Wallet Instance Attestation expired");
81
91
  }
82
92
 
@@ -87,11 +97,20 @@ export const getCredentialTrustmark: GetCredentialTrustmarkJwt = async ({
87
97
  const cryptoContextThumbprint = await thumbprint(holderBindingKey);
88
98
 
89
99
  if (wiaThumbprint !== cryptoContextThumbprint) {
100
+ Logger.log(
101
+ LogLevel.ERROR,
102
+ `Failed to verify holder binding for status attestation, expected thumbprint: ${cryptoContextThumbprint}, got: ${wiaThumbprint}`
103
+ );
90
104
  throw new IoWalletError(
91
105
  `Failed to verify holder binding for status attestation, expected thumbprint: ${cryptoContextThumbprint}, got: ${wiaThumbprint}`
92
106
  );
93
107
  }
94
108
 
109
+ Logger.log(
110
+ LogLevel.DEBUG,
111
+ `Wia thumbprint: ${wiaThumbprint} CryptoContext thumbprint: ${cryptoContextThumbprint}`
112
+ );
113
+
95
114
  /**
96
115
  * Generate Trustmark signed JWT
97
116
  */
package/src/index.ts CHANGED
@@ -11,6 +11,7 @@ import * as Errors from "./utils/errors";
11
11
  import * as WalletInstanceAttestation from "./wallet-instance-attestation";
12
12
  import * as Trust from "./trust";
13
13
  import * as WalletInstance from "./wallet-instance";
14
+ import * as Logging from "./utils/logging";
14
15
  import { AuthorizationDetail, AuthorizationDetails } from "./utils/par";
15
16
  import { createCryptoContextFor } from "./utils/crypto";
16
17
  import type { IntegrityContext } from "./utils/integrity";
@@ -27,6 +28,7 @@ export {
27
28
  AuthorizationDetail,
28
29
  AuthorizationDetails,
29
30
  fixBase64EncodingOnKey,
31
+ Logging,
30
32
  };
31
33
 
32
34
  export type { IntegrityContext, AuthorizationContext };
@@ -1,6 +1,7 @@
1
1
  import { decode as decodeJwt } from "@pagopa/io-react-native-jwt";
2
2
  import type { JWTDecodeResult } from "./jwk";
3
3
  import { ValidationFailed } from "./errors";
4
+ import { LogLevel, Logger } from "./logging";
4
5
 
5
6
  /*
6
7
  * Decode a form_post.jwt and return the final JWT.
@@ -47,6 +48,10 @@ export const getJwtFromFormPost = async (
47
48
  }
48
49
  }
49
50
 
51
+ Logger.log(
52
+ LogLevel.ERROR,
53
+ `Unable to obtain JWT from form_post.jwt. Form data: ${formData}`
54
+ );
50
55
  throw new ValidationFailed({
51
56
  message: `Unable to obtain JWT from form_post.jwt. Form data: ${formData}`,
52
57
  });
@@ -43,8 +43,19 @@ export const WalletProviderResponseErrorCodes = {
43
43
  WalletInstanceNotFound: "ERR_IO_WALLET_INSTANCE_NOT_FOUND",
44
44
  } as const;
45
45
 
46
+ export const RelyingPartyResponseErrorCodes = {
47
+ RelyingPartyGenericError: "ERR_RP_GENERIC_ERROR",
48
+ /**
49
+ * An error code thrown then the Relying Party rejects the Wallet's Authorization Response.
50
+ */
51
+ InvalidAuthorizationResponse: "ERR_RP_INVALID_AUTHORIZATION_RESPONSE",
52
+ } as const;
53
+
46
54
  export type IssuerResponseErrorCode =
47
55
  (typeof IssuerResponseErrorCodes)[keyof typeof IssuerResponseErrorCodes];
48
56
 
49
57
  export type WalletProviderResponseErrorCode =
50
58
  (typeof WalletProviderResponseErrorCodes)[keyof typeof WalletProviderResponseErrorCodes];
59
+
60
+ export type RelyingPartyResponseErrorCode =
61
+ (typeof RelyingPartyResponseErrorCodes)[keyof typeof RelyingPartyResponseErrorCodes];
@@ -3,11 +3,17 @@ import type { CredentialIssuerEntityConfiguration } from "../trust";
3
3
  import {
4
4
  IssuerResponseErrorCodes,
5
5
  WalletProviderResponseErrorCodes,
6
+ RelyingPartyResponseErrorCodes,
6
7
  type IssuerResponseErrorCode,
7
8
  type WalletProviderResponseErrorCode,
9
+ type RelyingPartyResponseErrorCode,
8
10
  } from "./error-codes";
9
11
 
10
- export { IssuerResponseErrorCodes, WalletProviderResponseErrorCodes };
12
+ export {
13
+ IssuerResponseErrorCodes,
14
+ WalletProviderResponseErrorCodes,
15
+ RelyingPartyResponseErrorCodes,
16
+ };
11
17
 
12
18
  // An error reason that supports both a string and a generic JSON object
13
19
  type GenericErrorReason = string | Record<string, unknown>;
@@ -110,8 +116,6 @@ export class UnexpectedStatusCodeError extends IoWalletError {
110
116
  /**
111
117
  * An error subclass thrown when an Issuer HTTP request fails.
112
118
  * The specific error can be found in the `code` property.
113
- *
114
- * The class is generic over the error code to narrow down the reason.
115
119
  */
116
120
  export class IssuerResponseError extends UnexpectedStatusCodeError {
117
121
  code: IssuerResponseErrorCode;
@@ -149,6 +153,25 @@ export class WalletProviderResponseError extends UnexpectedStatusCodeError {
149
153
  }
150
154
  }
151
155
 
156
+ /**
157
+ * An error subclass thrown when a Relying Party HTTP request fails.
158
+ * The specific error can be found in the `code` property.
159
+ */
160
+ export class RelyingPartyResponseError extends UnexpectedStatusCodeError {
161
+ code: RelyingPartyResponseErrorCode;
162
+
163
+ constructor(params: {
164
+ code?: RelyingPartyResponseErrorCode;
165
+ message: string;
166
+ reason: GenericErrorReason;
167
+ statusCode: number;
168
+ }) {
169
+ super(params);
170
+ this.code =
171
+ params.code ?? RelyingPartyResponseErrorCodes.RelyingPartyGenericError;
172
+ }
173
+ }
174
+
152
175
  type LocalizedIssuanceError = {
153
176
  [locale: string]: {
154
177
  title: string;
@@ -200,36 +223,43 @@ export function extractErrorMessageFromIssuerConf(
200
223
  }
201
224
 
202
225
  /**
203
- * Type guard for issuer errors.
204
- * @param error The error to check
205
- * @param code Optional code to narrow down the issuer error
226
+ * Factory function to create a type guard for specific error classes.
227
+ *
228
+ * @param errorClass The error class to create the type guard for
229
+ * @returns A type guard that checks if the error is an instance of the given class and has the expected code
206
230
  */
207
- export const isIssuerResponseError = (
208
- error: unknown,
209
- code?: IssuerResponseErrorCode
210
- ): error is IssuerResponseError =>
211
- error instanceof IssuerResponseError && error.code === (code ?? error.code);
231
+ const makeErrorTypeGuard =
232
+ <T extends typeof UnexpectedStatusCodeError>(ErrorClass: T) =>
233
+ (error: unknown, code?: ExtractErrorCode<T>): error is InstanceType<T> =>
234
+ error instanceof ErrorClass && error.code === (code ?? error.code);
235
+
236
+ export const isIssuerResponseError = makeErrorTypeGuard(IssuerResponseError);
237
+ export const isWalletProviderResponseError = makeErrorTypeGuard(
238
+ WalletProviderResponseError
239
+ );
240
+ export const isRelyingPartyResponseError = makeErrorTypeGuard(
241
+ RelyingPartyResponseError
242
+ );
243
+
244
+ // Mapping type between error classes and their allowed codes
245
+ type ErrorCodeMap =
246
+ | {
247
+ type: typeof IssuerResponseError;
248
+ code: IssuerResponseErrorCode;
249
+ }
250
+ | {
251
+ type: typeof WalletProviderResponseError;
252
+ code: WalletProviderResponseErrorCode;
253
+ }
254
+ | {
255
+ type: typeof RelyingPartyResponseError;
256
+ code: RelyingPartyResponseErrorCode;
257
+ };
212
258
 
213
- /**
214
- * Type guard for wallet provider errors.
215
- * @param error The error to check
216
- * @param code Optional code to narrow down the wallet provider error
217
- */
218
- export const isWalletProviderResponseError = (
219
- error: unknown,
220
- code?: WalletProviderResponseErrorCode
221
- ): error is WalletProviderResponseError =>
222
- error instanceof WalletProviderResponseError &&
223
- error.code === (code ?? error.code);
224
-
225
- type ErrorCodeMap<T> = T extends typeof IssuerResponseError
226
- ? IssuerResponseErrorCode
227
- : T extends typeof WalletProviderResponseError
228
- ? WalletProviderResponseErrorCode
229
- : never;
259
+ type ExtractErrorCode<T> = Extract<ErrorCodeMap, { type: T }>["code"];
230
260
 
231
261
  type ErrorCase<T> = {
232
- code: ErrorCodeMap<T>;
262
+ code: ExtractErrorCode<T>;
233
263
  message: string;
234
264
  reason?: GenericErrorReason;
235
265
  };