@pagopa/io-react-native-wallet 0.24.1 → 0.26.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (186) hide show
  1. package/README.md +0 -32
  2. package/lib/commonjs/client/generated/wallet-provider.js +39 -16
  3. package/lib/commonjs/client/generated/wallet-provider.js.map +1 -1
  4. package/lib/commonjs/client/index.js +25 -10
  5. package/lib/commonjs/client/index.js.map +1 -1
  6. package/lib/commonjs/credential/issuance/03-start-user-authorization.js +2 -2
  7. package/lib/commonjs/credential/issuance/04-complete-user-authorization.js +36 -67
  8. package/lib/commonjs/credential/issuance/04-complete-user-authorization.js.map +1 -1
  9. package/lib/commonjs/credential/issuance/05-authorize-access.js +5 -2
  10. package/lib/commonjs/credential/issuance/05-authorize-access.js.map +1 -1
  11. package/lib/commonjs/credential/issuance/06-obtain-credential.js +26 -17
  12. package/lib/commonjs/credential/issuance/06-obtain-credential.js.map +1 -1
  13. package/lib/commonjs/credential/issuance/README.md +8 -14
  14. package/lib/commonjs/credential/issuance/errors.js +52 -0
  15. package/lib/commonjs/credential/issuance/errors.js.map +1 -0
  16. package/lib/commonjs/credential/issuance/index.js +13 -2
  17. package/lib/commonjs/credential/issuance/index.js.map +1 -1
  18. package/lib/commonjs/credential/issuance/types.js +1 -5
  19. package/lib/commonjs/credential/issuance/types.js.map +1 -1
  20. package/lib/commonjs/credential/presentation/01-start-flow.js +1 -1
  21. package/lib/commonjs/credential/presentation/01-start-flow.js.map +1 -1
  22. package/lib/commonjs/credential/presentation/03-get-request-object.js +2 -2
  23. package/lib/commonjs/credential/presentation/03-get-request-object.js.map +1 -1
  24. package/lib/commonjs/credential/presentation/04-send-authorization-response.js +2 -2
  25. package/lib/commonjs/credential/presentation/04-send-authorization-response.js.map +1 -1
  26. package/lib/commonjs/credential/presentation/errors.js +49 -0
  27. package/lib/commonjs/credential/presentation/errors.js.map +1 -0
  28. package/lib/commonjs/credential/presentation/index.js +5 -0
  29. package/lib/commonjs/credential/presentation/index.js.map +1 -1
  30. package/lib/commonjs/credential/status/02-status-attestation.js +8 -6
  31. package/lib/commonjs/credential/status/02-status-attestation.js.map +1 -1
  32. package/lib/commonjs/credential/status/README.md +5 -2
  33. package/lib/commonjs/credential/status/types.js +1 -14
  34. package/lib/commonjs/credential/status/types.js.map +1 -1
  35. package/lib/commonjs/sd-jwt/errors.js +40 -0
  36. package/lib/commonjs/sd-jwt/errors.js.map +1 -0
  37. package/lib/commonjs/sd-jwt/index.js +8 -4
  38. package/lib/commonjs/sd-jwt/index.js.map +1 -1
  39. package/lib/commonjs/sd-jwt/verifier.js +5 -1
  40. package/lib/commonjs/sd-jwt/verifier.js.map +1 -1
  41. package/lib/commonjs/trust/index.js +2 -2
  42. package/lib/commonjs/trust/index.js.map +1 -1
  43. package/lib/commonjs/utils/decoder.js +3 -1
  44. package/lib/commonjs/utils/decoder.js.map +1 -1
  45. package/lib/commonjs/utils/error-codes.js +51 -0
  46. package/lib/commonjs/utils/error-codes.js.map +1 -0
  47. package/lib/commonjs/utils/errors.js +119 -463
  48. package/lib/commonjs/utils/errors.js.map +1 -1
  49. package/lib/commonjs/utils/misc.js +23 -55
  50. package/lib/commonjs/utils/misc.js.map +1 -1
  51. package/lib/commonjs/utils/par.js +2 -1
  52. package/lib/commonjs/utils/par.js.map +1 -1
  53. package/lib/commonjs/wallet-instance/README.md +26 -5
  54. package/lib/commonjs/wallet-instance/index.js +33 -7
  55. package/lib/commonjs/wallet-instance/index.js.map +1 -1
  56. package/lib/commonjs/wallet-instance-attestation/README.md +8 -2
  57. package/lib/commonjs/wallet-instance-attestation/issuing.js +13 -10
  58. package/lib/commonjs/wallet-instance-attestation/issuing.js.map +1 -1
  59. package/lib/module/client/generated/wallet-provider.js +31 -11
  60. package/lib/module/client/generated/wallet-provider.js.map +1 -1
  61. package/lib/module/client/index.js +22 -8
  62. package/lib/module/client/index.js.map +1 -1
  63. package/lib/module/credential/issuance/03-start-user-authorization.js +2 -2
  64. package/lib/module/credential/issuance/04-complete-user-authorization.js +33 -65
  65. package/lib/module/credential/issuance/04-complete-user-authorization.js.map +1 -1
  66. package/lib/module/credential/issuance/05-authorize-access.js +7 -4
  67. package/lib/module/credential/issuance/05-authorize-access.js.map +1 -1
  68. package/lib/module/credential/issuance/06-obtain-credential.js +29 -20
  69. package/lib/module/credential/issuance/06-obtain-credential.js.map +1 -1
  70. package/lib/module/credential/issuance/README.md +8 -14
  71. package/lib/module/credential/issuance/errors.js +44 -0
  72. package/lib/module/credential/issuance/errors.js.map +1 -0
  73. package/lib/module/credential/issuance/index.js +3 -2
  74. package/lib/module/credential/issuance/index.js.map +1 -1
  75. package/lib/module/credential/issuance/types.js +0 -3
  76. package/lib/module/credential/issuance/types.js.map +1 -1
  77. package/lib/module/credential/presentation/01-start-flow.js +1 -1
  78. package/lib/module/credential/presentation/01-start-flow.js.map +1 -1
  79. package/lib/module/credential/presentation/03-get-request-object.js +3 -3
  80. package/lib/module/credential/presentation/03-get-request-object.js.map +1 -1
  81. package/lib/module/credential/presentation/04-send-authorization-response.js +3 -3
  82. package/lib/module/credential/presentation/04-send-authorization-response.js.map +1 -1
  83. package/lib/module/credential/presentation/errors.js +42 -0
  84. package/lib/module/credential/presentation/errors.js.map +1 -0
  85. package/lib/module/credential/presentation/index.js +2 -1
  86. package/lib/module/credential/presentation/index.js.map +1 -1
  87. package/lib/module/credential/status/02-status-attestation.js +11 -9
  88. package/lib/module/credential/status/02-status-attestation.js.map +1 -1
  89. package/lib/module/credential/status/README.md +5 -2
  90. package/lib/module/credential/status/types.js +0 -12
  91. package/lib/module/credential/status/types.js.map +1 -1
  92. package/lib/module/sd-jwt/errors.js +32 -0
  93. package/lib/module/sd-jwt/errors.js.map +1 -0
  94. package/lib/module/sd-jwt/index.js +5 -5
  95. package/lib/module/sd-jwt/index.js.map +1 -1
  96. package/lib/module/sd-jwt/verifier.js +5 -1
  97. package/lib/module/sd-jwt/verifier.js.map +1 -1
  98. package/lib/module/trust/index.js +3 -3
  99. package/lib/module/trust/index.js.map +1 -1
  100. package/lib/module/utils/decoder.js +3 -1
  101. package/lib/module/utils/decoder.js.map +1 -1
  102. package/lib/module/utils/error-codes.js +43 -0
  103. package/lib/module/utils/error-codes.js.map +1 -0
  104. package/lib/module/utils/errors.js +98 -438
  105. package/lib/module/utils/errors.js.map +1 -1
  106. package/lib/module/utils/misc.js +19 -49
  107. package/lib/module/utils/misc.js.map +1 -1
  108. package/lib/module/utils/par.js +3 -2
  109. package/lib/module/utils/par.js.map +1 -1
  110. package/lib/module/wallet-instance/README.md +26 -5
  111. package/lib/module/wallet-instance/index.js +32 -7
  112. package/lib/module/wallet-instance/index.js.map +1 -1
  113. package/lib/module/wallet-instance-attestation/README.md +8 -2
  114. package/lib/module/wallet-instance-attestation/issuing.js +15 -12
  115. package/lib/module/wallet-instance-attestation/issuing.js.map +1 -1
  116. package/lib/typescript/client/generated/wallet-provider.d.ts +138 -27
  117. package/lib/typescript/client/generated/wallet-provider.d.ts.map +1 -1
  118. package/lib/typescript/client/index.d.ts +7 -1
  119. package/lib/typescript/client/index.d.ts.map +1 -1
  120. package/lib/typescript/credential/issuance/03-start-user-authorization.d.ts +2 -2
  121. package/lib/typescript/credential/issuance/04-complete-user-authorization.d.ts +17 -16
  122. package/lib/typescript/credential/issuance/04-complete-user-authorization.d.ts.map +1 -1
  123. package/lib/typescript/credential/issuance/05-authorize-access.d.ts.map +1 -1
  124. package/lib/typescript/credential/issuance/06-obtain-credential.d.ts.map +1 -1
  125. package/lib/typescript/credential/issuance/errors.d.ts +28 -0
  126. package/lib/typescript/credential/issuance/errors.d.ts.map +1 -0
  127. package/lib/typescript/credential/issuance/index.d.ts +4 -3
  128. package/lib/typescript/credential/issuance/index.d.ts.map +1 -1
  129. package/lib/typescript/credential/issuance/types.d.ts +0 -8
  130. package/lib/typescript/credential/issuance/types.d.ts.map +1 -1
  131. package/lib/typescript/credential/presentation/03-get-request-object.d.ts.map +1 -1
  132. package/lib/typescript/credential/presentation/04-send-authorization-response.d.ts.map +1 -1
  133. package/lib/typescript/credential/presentation/errors.d.ts +25 -0
  134. package/lib/typescript/credential/presentation/errors.d.ts.map +1 -0
  135. package/lib/typescript/credential/presentation/index.d.ts +2 -1
  136. package/lib/typescript/credential/presentation/index.d.ts.map +1 -1
  137. package/lib/typescript/credential/status/02-status-attestation.d.ts.map +1 -1
  138. package/lib/typescript/credential/status/types.d.ts +0 -15
  139. package/lib/typescript/credential/status/types.d.ts.map +1 -1
  140. package/lib/typescript/sd-jwt/errors.d.ts +20 -0
  141. package/lib/typescript/sd-jwt/errors.d.ts.map +1 -0
  142. package/lib/typescript/sd-jwt/index.d.ts +3 -2
  143. package/lib/typescript/sd-jwt/index.d.ts.map +1 -1
  144. package/lib/typescript/utils/error-codes.d.ts +45 -0
  145. package/lib/typescript/utils/error-codes.d.ts.map +1 -0
  146. package/lib/typescript/utils/errors.d.ts +88 -225
  147. package/lib/typescript/utils/errors.d.ts.map +1 -1
  148. package/lib/typescript/utils/misc.d.ts +9 -24
  149. package/lib/typescript/utils/misc.d.ts.map +1 -1
  150. package/lib/typescript/utils/par.d.ts.map +1 -1
  151. package/lib/typescript/wallet-instance/index.d.ts +17 -1
  152. package/lib/typescript/wallet-instance/index.d.ts.map +1 -1
  153. package/lib/typescript/wallet-instance-attestation/issuing.d.ts.map +1 -1
  154. package/lib/typescript/wallet-instance-attestation/types.d.ts +4 -4
  155. package/package.json +1 -1
  156. package/src/client/generated/wallet-provider.ts +43 -13
  157. package/src/client/index.ts +28 -15
  158. package/src/credential/issuance/03-start-user-authorization.ts +2 -2
  159. package/src/credential/issuance/04-complete-user-authorization.ts +57 -118
  160. package/src/credential/issuance/05-authorize-access.ts +7 -4
  161. package/src/credential/issuance/06-obtain-credential.ts +39 -39
  162. package/src/credential/issuance/README.md +8 -14
  163. package/src/credential/issuance/errors.ts +44 -0
  164. package/src/credential/issuance/index.ts +8 -2
  165. package/src/credential/issuance/types.ts +0 -8
  166. package/src/credential/presentation/01-start-flow.ts +1 -1
  167. package/src/credential/presentation/03-get-request-object.ts +3 -3
  168. package/src/credential/presentation/04-send-authorization-response.ts +3 -3
  169. package/src/credential/presentation/errors.ts +41 -0
  170. package/src/credential/presentation/index.ts +2 -0
  171. package/src/credential/status/02-status-attestation.ts +17 -25
  172. package/src/credential/status/README.md +5 -2
  173. package/src/credential/status/types.ts +0 -15
  174. package/src/sd-jwt/errors.ts +39 -0
  175. package/src/sd-jwt/index.ts +5 -8
  176. package/src/sd-jwt/verifier.ts +5 -5
  177. package/src/trust/index.ts +3 -3
  178. package/src/utils/decoder.ts +3 -3
  179. package/src/utils/error-codes.ts +50 -0
  180. package/src/utils/errors.ts +152 -476
  181. package/src/utils/misc.ts +21 -65
  182. package/src/utils/par.ts +3 -2
  183. package/src/wallet-instance/README.md +26 -5
  184. package/src/wallet-instance/index.ts +40 -18
  185. package/src/wallet-instance-attestation/README.md +8 -2
  186. package/src/wallet-instance-attestation/issuing.ts +28 -36
@@ -1,12 +1,16 @@
1
+ import { parseRawHttpResponse } from "../utils/misc";
1
2
  import { WalletProviderResponseError } from "../utils/errors";
2
3
  import {
3
4
  ProblemDetail,
4
5
  createApiClient as createWalletProviderApiClient,
6
+ ApiClient as WalletProviderApiClient,
7
+ type EndpointParameters,
5
8
  } from "./generated/wallet-provider";
6
- import { ApiClient as WalletProviderApiClient } from "./generated/wallet-provider";
7
9
 
8
10
  export type WalletProviderClient = WalletProviderApiClient;
9
11
 
12
+ type RawHttpResponse = Awaited<ReturnType<typeof parseRawHttpResponse>>;
13
+
10
14
  const validateResponse = async (response: Response) => {
11
15
  if (!response.ok) {
12
16
  let problemDetail: ProblemDetail = {};
@@ -18,12 +22,11 @@ const validateResponse = async (response: Response) => {
18
22
  };
19
23
  }
20
24
 
21
- throw new WalletProviderResponseError(
22
- problemDetail.title ?? "Invalid response from Wallet Provider",
23
- problemDetail.type,
24
- problemDetail.detail,
25
- response.status
26
- );
25
+ throw new WalletProviderResponseError({
26
+ message: problemDetail.title ?? "Invalid response from Wallet Provider",
27
+ reason: problemDetail,
28
+ statusCode: response.status,
29
+ });
27
30
  }
28
31
  return response;
29
32
  };
@@ -36,7 +39,7 @@ export const getWalletProviderClient = (context: {
36
39
 
37
40
  return createWalletProviderApiClient(
38
41
  (method, url, params) =>
39
- appFetch(url, {
42
+ appFetch(interpolateUrl(url, params), {
40
43
  method,
41
44
  body: params ? JSON.stringify(params.body) : undefined,
42
45
  headers: {
@@ -44,13 +47,23 @@ export const getWalletProviderClient = (context: {
44
47
  },
45
48
  })
46
49
  .then(validateResponse)
47
- .then((res) => {
48
- const contentType = res.headers.get("content-type");
49
- if (contentType?.includes("application/json")) {
50
- return res.json();
51
- }
52
- return res.text();
53
- }),
50
+ .then<RawHttpResponse>(parseRawHttpResponse),
54
51
  walletProviderBaseUrl
55
52
  );
56
53
  };
54
+
55
+ /**
56
+ * Function to interpolate the url when the request includes path params.
57
+ * The client generator expects the literal name of the param in the url
58
+ * and passes the actual values in a separate object.
59
+ */
60
+ export const interpolateUrl = (url: string, params?: EndpointParameters) => {
61
+ if (!params?.path) return url;
62
+
63
+ for (const [key, value] of Object.entries(params.path)) {
64
+ if (typeof value === "string") {
65
+ url = url.replace(`{${key}}`, value);
66
+ }
67
+ }
68
+ return url;
69
+ };
@@ -79,7 +79,7 @@ const selectResponseMode = (
79
79
 
80
80
  /**
81
81
  * WARNING: This function must be called after {@link evaluateIssuerTrust} and {@link startFlow}. The next steam is {@link compeUserAuthorizationWithQueryMode} or {@link compeUserAuthorizationWithFormPostJwtMode}
82
- * Creates and sends a PAR request to the /as/par endpoint of the authroization server.
82
+ * Creates and sends a PAR request to the /as/par endpoint of the authorization server.
83
83
  * This starts the authentication flow to obtain an access token.
84
84
  * This token enables the Wallet Instance to request a digital credential from the Credential Endpoint of the Credential Issuer.
85
85
  * This is an HTTP POST request containing the Wallet Instance identifier (client id), the code challenge and challenge method as specified by PKCE according to RFC 9126
@@ -88,7 +88,7 @@ const selectResponseMode = (
88
88
  * the application session identifier on the Wallet Instance side (state),
89
89
  * the method (query or form_post.jwt) by which the Authorization Server
90
90
  * should transmit the Authorization Response containing the authorization code issued upon the end user's authentication (response_mode)
91
- * to the Wallet Instance's Token Endpoint to obtain the Access Token, and the redirect_uri of the Wallet Instance where the Authorization Response
91
+ * to the Wallet Instance's Token Endpoint to obtain the Access Token, and the redirectUri of the Wallet Instance where the Authorization Response
92
92
  * should be delivered. The redirect is achived by using a custom URL scheme that the Wallet Instance is registered to handle.
93
93
  * @param issuerConf The issuer configuration
94
94
  * @param credentialType The type of the credential to be requested returned by {@link selectCredentialDefinition}
@@ -1,26 +1,13 @@
1
1
  import {
2
2
  AuthorizationErrorShape,
3
3
  AuthorizationResultShape,
4
- type AuthorizationContext,
5
4
  type AuthorizationResult,
6
5
  } from "../../utils/auth";
7
- import {
8
- createAbortPromiseFromSignal,
9
- hasStatus,
10
- isDefined,
11
- until,
12
- type Out,
13
- } from "../../utils/misc";
6
+ import { hasStatusOrThrow, type Out } from "../../utils/misc";
14
7
  import type { StartUserAuthorization } from "./03-start-user-authorization";
15
8
  import parseUrl from "parse-url";
16
- import {
17
- AuthorizationError,
18
- AuthorizationIdpError,
19
- OperationAbortedError,
20
- ValidationFailed,
21
- } from "../../utils/errors";
9
+ import { IssuerResponseError, ValidationFailed } from "../../utils/errors";
22
10
  import type { EvaluateIssuerTrust } from "./02-evaluate-issuer-trust";
23
- import { Linking } from "react-native";
24
11
  import {
25
12
  decode,
26
13
  encodeBase64,
@@ -31,18 +18,13 @@ import { RequestObject } from "../presentation/types";
31
18
  import uuid from "react-native-uuid";
32
19
  import { ResponseUriResultShape } from "./types";
33
20
  import { getJwtFromFormPost } from "../../utils/decoder";
21
+ import { AuthorizationError, AuthorizationIdpError } from "./errors";
34
22
 
35
23
  /**
36
24
  * The interface of the phase to complete User authorization via strong identification when the response mode is "query" and the request credential is a PersonIdentificationData.
37
25
  */
38
26
  export type CompleteUserAuthorizationWithQueryMode = (
39
- issuerRequestUri: Out<StartUserAuthorization>["issuerRequestUri"],
40
- clientId: Out<StartUserAuthorization>["clientId"],
41
- issuerConf: Out<EvaluateIssuerTrust>["issuerConf"],
42
- idpHint: string,
43
- redirectUri: string,
44
- authorizationContext?: AuthorizationContext,
45
- signal?: AbortSignal
27
+ authRedirectUrl: string
46
28
  ) => Promise<AuthorizationResult>;
47
29
 
48
30
  export type CompleteUserAuthorizationWithFormPostJwtMode = (
@@ -63,99 +45,56 @@ export type GetRequestedCredentialToBePresented = (
63
45
  appFetch?: GlobalFetch["fetch"]
64
46
  ) => Promise<RequestObject>;
65
47
 
48
+ export type BuildAuthorizationUrl = (
49
+ issuerRequestUri: Out<StartUserAuthorization>["issuerRequestUri"],
50
+ clientId: Out<StartUserAuthorization>["clientId"],
51
+ issuerConf: Out<EvaluateIssuerTrust>["issuerConf"],
52
+ idpHint: string
53
+ ) => Promise<{
54
+ authUrl: string;
55
+ }>;
56
+
66
57
  /**
67
- * WARNING: This function must be called after {@link startUserAuthorization}. The next function to be called is {@link authorizeAccess}.
68
- * The interface of the phase to complete User authorization via strong identification when the response mode is "query" and the request credential is a PersonIdentificationData.
69
- * It is used to complete the user authorization by catching the redirectSchema from the authorization server which then contains the authorization response.
70
- * This function utilizes the authorization context to open an in-app browser capable of catching the redirectSchema to perform a get request to the authorization endpoint.
71
- * If the 302 redirect happens and the redirectSchema is caught, the function will return the authorization response after parsing it from the query string.
58
+ * WARNING: This function must be called after {@link startUserAuthorization}. The generated authUrl must be used to open a browser or webview capable of catching the redirectSchema to perform a get request to the authorization endpoint.
59
+ * Builds the authorization URL to which the end user should be redirected to continue the authentication flow.
72
60
  * @param issuerRequestUri the URI of the issuer where the request is sent
73
61
  * @param clientId Identifies the current client across all the requests of the issuing flow returned by {@link startUserAuthorization}
74
62
  * @param issuerConf The issuer configuration returned by {@link evaluateIssuerTrust}
75
- * @param authorizationContext The context to identify the user which will be used to start the authorization. It's needed only when requesting a PersonalIdentificationData credential. The implementantion should open an in-app browser capable of catching the redirectSchema.
76
- * If not specified, the default browser is used
77
- * @param idphint Unique identifier of the SPID IDP selected by the user
78
- * @param redirectUri The url to reach to complete the user authorization which is the custom URL scheme that the Wallet Instance is registered to handle, usually a custom URL or deeplink
79
- * @param signal An optional {@link AbortSignal} to abort the operation when using the default browser
80
- * @throws {AuthorizationError} if an error occurs during the authorization process
81
- * @throws {AuthorizationIdpError} if an error occurs during the authorization process and the error is related to the IDP
82
- * @throws {OperationAbortedError} if the caller aborts the operation via the provided signal
83
- * @returns the authorization response which contains code, state and iss
63
+ * @param idpHint Unique identifier of the IDP selected by the user
64
+ * @returns An object containing the authorization URL
84
65
  */
85
- export const completeUserAuthorizationWithQueryMode: CompleteUserAuthorizationWithQueryMode =
86
- async (
87
- issuerRequestUri,
88
- clientId,
89
- issuerConf,
90
- idpHint,
91
- redirectUri,
92
- authorizationContext,
93
- signal
94
- ) => {
95
- const authzRequestEndpoint =
96
- issuerConf.oauth_authorization_server.authorization_endpoint;
97
- const params = new URLSearchParams({
98
- client_id: clientId,
99
- request_uri: issuerRequestUri,
100
- idphint: idpHint,
101
- });
102
- const authUrl = `${authzRequestEndpoint}?${params}`;
103
- var authRedirectUrl: string | undefined;
104
-
105
- if (authorizationContext) {
106
- const redirectSchema = new URL(redirectUri).protocol.replace(":", "");
107
- authRedirectUrl = await authorizationContext
108
- .authorize(authUrl, redirectSchema)
109
- .catch((e) => {
110
- throw new AuthorizationError(e.message);
111
- });
112
- } else {
113
- // handler for redirectUri
114
- const urlEventListener = Linking.addEventListener("url", ({ url }) => {
115
- if (url.includes(redirectUri)) {
116
- authRedirectUrl = url;
117
- }
118
- });
66
+ export const buildAuthorizationUrl: BuildAuthorizationUrl = async (
67
+ issuerRequestUri,
68
+ clientId,
69
+ issuerConf,
70
+ idpHint
71
+ ) => {
72
+ const authzRequestEndpoint =
73
+ issuerConf.oauth_authorization_server.authorization_endpoint;
119
74
 
120
- const operationIsAborted = signal
121
- ? createAbortPromiseFromSignal(signal)
122
- : undefined;
123
- await Linking.openURL(authUrl);
75
+ const params = new URLSearchParams({
76
+ client_id: clientId,
77
+ request_uri: issuerRequestUri,
78
+ idphint: idpHint,
79
+ });
124
80
 
125
- /*
126
- * Waits for 120 seconds for the identificationRedirectUrl variable to be set
127
- * by the custom url handler. If the timeout is exceeded, throw an exception
128
- */
129
- const unitAuthRedirectIsNotUndefined = until(
130
- () => authRedirectUrl !== undefined,
131
- 120
132
- );
81
+ const authUrl = `${authzRequestEndpoint}?${params}`;
133
82
 
134
- /**
135
- * Simultaneously listen for the abort signal (when provided) and the redirect url.
136
- * The first event that occurs will resolve the promise.
137
- * This is useful to properly cleanup when the caller aborts this operation.
138
- */
139
- const winner = await Promise.race(
140
- [operationIsAborted?.listen(), unitAuthRedirectIsNotUndefined].filter(
141
- isDefined
142
- )
143
- ).finally(() => {
144
- urlEventListener.remove();
145
- operationIsAborted?.remove();
146
- });
147
-
148
- if (winner === "OPERATION_ABORTED") {
149
- throw new OperationAbortedError("DefaultQueryModeAuthorization");
150
- }
151
-
152
- if (authRedirectUrl === undefined) {
153
- throw new AuthorizationError("Invalid authentication redirect url");
154
- }
155
- }
83
+ return { authUrl };
84
+ };
156
85
 
86
+ /**
87
+ * WARNING: This function must be called after obtaining the authorization redirect URL from the webviews (SPID and CIE L3) or browser for CIEID.
88
+ * Complete User authorization via strong identification when the response mode is "query" and the request credential is a PersonIdentificationData.
89
+ * This function parses the authorization redirect URL to extract the authorization response.
90
+ * @param authRedirectUrl The URL to which the end user should be redirected to start the authentication flow
91
+ * @returns the authorization response which contains code, state and iss
92
+ */
93
+ export const completeUserAuthorizationWithQueryMode: CompleteUserAuthorizationWithQueryMode =
94
+ async (authRedirectUrl) => {
157
95
  const query = parseUrl(authRedirectUrl).query;
158
- return parseAuthroizationResponse(query);
96
+
97
+ return parseAuthorizationResponse(query);
159
98
  };
160
99
 
161
100
  /**
@@ -183,16 +122,16 @@ export const getRequestedCredentialToBePresented: GetRequestedCredentialToBePres
183
122
  `${authzRequestEndpoint}?${params.toString()}`,
184
123
  { method: "GET" }
185
124
  )
186
- .then(hasStatus(200))
125
+ .then(hasStatusOrThrow(200, IssuerResponseError))
187
126
  .then((res) => res.text())
188
127
  .then((jws) => decode(jws))
189
128
  .then((reqObj) => RequestObject.safeParse(reqObj.payload));
190
129
 
191
130
  if (!requestObject.success) {
192
- throw new ValidationFailed(
193
- "Request Object validation failed",
194
- requestObject.error.message
195
- );
131
+ throw new ValidationFailed({
132
+ message: "Request Object validation failed",
133
+ reason: requestObject.error.message,
134
+ });
196
135
  }
197
136
  return requestObject.data;
198
137
  };
@@ -300,22 +239,22 @@ export const completeUserAuthorizationWithFormPostJwtMode: CompleteUserAuthoriza
300
239
  },
301
240
  body,
302
241
  })
303
- .then(hasStatus(200))
242
+ .then(hasStatusOrThrow(200, IssuerResponseError))
304
243
  .then((reqUri) => reqUri.json());
305
244
 
306
245
  const responseUri = ResponseUriResultShape.safeParse(resUriRes);
307
246
  if (!responseUri.success) {
308
- throw new ValidationFailed(
309
- "Response Uri validation failed",
310
- responseUri.error.message
311
- );
247
+ throw new ValidationFailed({
248
+ message: "Response Uri validation failed",
249
+ reason: responseUri.error.message,
250
+ });
312
251
  }
313
252
 
314
253
  return await appFetch(responseUri.data.redirect_uri)
315
- .then(hasStatus(200))
254
+ .then(hasStatusOrThrow(200, IssuerResponseError))
316
255
  .then((res) => res.text())
317
256
  .then(getJwtFromFormPost)
318
- .then((cbRes) => parseAuthroizationResponse(cbRes.decodedJwt.payload));
257
+ .then((cbRes) => parseAuthorizationResponse(cbRes.decodedJwt.payload));
319
258
  };
320
259
 
321
260
  /**
@@ -325,7 +264,7 @@ export const completeUserAuthorizationWithFormPostJwtMode: CompleteUserAuthoriza
325
264
  * @param authRes the authorization response to be parsed
326
265
  * @returns the authorization result which contains code, state and iss
327
266
  */
328
- export const parseAuthroizationResponse = (
267
+ export const parseAuthorizationResponse = (
329
268
  authRes: unknown
330
269
  ): AuthorizationResult => {
331
270
  const authResParsed = AuthorizationResultShape.safeParse(authRes);
@@ -1,4 +1,4 @@
1
- import { hasStatus, type Out } from "../../utils/misc";
1
+ import { hasStatusOrThrow, type Out } from "../../utils/misc";
2
2
  import type { EvaluateIssuerTrust } from "./02-evaluate-issuer-trust";
3
3
  import type { StartUserAuthorization } from "./03-start-user-authorization";
4
4
  import { createDPopToken } from "../../utils/dpop";
@@ -8,7 +8,7 @@ import * as WalletInstanceAttestation from "../../wallet-instance-attestation";
8
8
  import type { CryptoContext } from "@pagopa/io-react-native-jwt";
9
9
  import { ASSERTION_TYPE } from "./const";
10
10
  import { TokenResponse } from "./types";
11
- import { ValidationFailed } from "../../utils/errors";
11
+ import { IssuerResponseError, ValidationFailed } from "../../utils/errors";
12
12
  import type { CompleteUserAuthorizationWithQueryMode } from "./04-complete-user-authorization";
13
13
 
14
14
  export type AuthorizeAccess = (
@@ -103,12 +103,15 @@ export const authorizeAccess: AuthorizeAccess = async (
103
103
  },
104
104
  body: authorizationRequestFormBody.toString(),
105
105
  })
106
- .then(hasStatus(200))
106
+ .then(hasStatusOrThrow(200, IssuerResponseError))
107
107
  .then((res) => res.json())
108
108
  .then((body) => TokenResponse.safeParse(body));
109
109
 
110
110
  if (!tokenRes.success) {
111
- throw new ValidationFailed(tokenRes.error.message);
111
+ throw new ValidationFailed({
112
+ message: "Token Response validation failed",
113
+ reason: tokenRes.error.message,
114
+ });
112
115
  }
113
116
 
114
117
  return { accessToken: tokenRes.data };
@@ -1,21 +1,20 @@
1
1
  import {
2
+ type CryptoContext,
2
3
  sha256ToBase64,
3
4
  SignJWT,
4
- type CryptoContext,
5
5
  } from "@pagopa/io-react-native-jwt";
6
6
  import type { AuthorizeAccess } from "./05-authorize-access";
7
7
  import type { EvaluateIssuerTrust } from "./02-evaluate-issuer-trust";
8
- import { hasStatus, safeJsonParse, type Out } from "../../utils/misc";
8
+ import { hasStatusOrThrow, type Out } from "../../utils/misc";
9
9
  import type { StartUserAuthorization } from "./03-start-user-authorization";
10
10
  import {
11
- CredentialInvalidStatusError,
12
- CredentialIssuingNotSynchronousError,
13
- CredentialRequestError,
11
+ IssuerResponseError,
12
+ IssuerResponseErrorCodes,
13
+ ResponseErrorBuilder,
14
14
  UnexpectedStatusCodeError,
15
15
  ValidationFailed,
16
16
  } from "../../utils/errors";
17
- import { CredentialIssuanceFailureResponse, CredentialResponse } from "./types";
18
-
17
+ import { CredentialResponse } from "./types";
19
18
  import { createDPopToken } from "../../utils/dpop";
20
19
  import uuid from "react-native-uuid";
21
20
 
@@ -97,7 +96,7 @@ export const obtainCredential: ObtainCredential = async (
97
96
  );
98
97
 
99
98
  // Validation of accessTokenResponse.authorization_details if contain credentialDefinition
100
- const constainsCredentialDefinition = accessToken.authorization_details.some(
99
+ const containsCredentialDefinition = accessToken.authorization_details.some(
101
100
  (c) =>
102
101
  c.credential_configuration_id ===
103
102
  credentialDefinition.credential_configuration_id &&
@@ -105,10 +104,11 @@ export const obtainCredential: ObtainCredential = async (
105
104
  c.type === credentialDefinition.type
106
105
  );
107
106
 
108
- if (!constainsCredentialDefinition) {
109
- throw new ValidationFailed(
110
- "The access token response does not contain the requested credential"
111
- );
107
+ if (!containsCredentialDefinition) {
108
+ throw new ValidationFailed({
109
+ message:
110
+ "The access token response does not contain the requested credential",
111
+ });
112
112
  }
113
113
 
114
114
  /** The credential request body */
@@ -123,7 +123,7 @@ export const obtainCredential: ObtainCredential = async (
123
123
  },
124
124
  };
125
125
 
126
- const tokenRequestSignedDPop = await await createDPopToken(
126
+ const tokenRequestSignedDPop = await createDPopToken(
127
127
  {
128
128
  htm: "POST",
129
129
  htu: credentialUrl,
@@ -141,13 +141,16 @@ export const obtainCredential: ObtainCredential = async (
141
141
  },
142
142
  body: JSON.stringify(credentialRequestFormBody),
143
143
  })
144
- .then(hasStatus(200))
144
+ .then(hasStatusOrThrow(200))
145
145
  .then((res) => res.json())
146
146
  .then((body) => CredentialResponse.safeParse(body))
147
147
  .catch(handleObtainCredentialError);
148
148
 
149
149
  if (!credentialRes.success) {
150
- throw new ValidationFailed(credentialRes.error.message);
150
+ throw new ValidationFailed({
151
+ message: "Credential Response validation failed",
152
+ reason: credentialRes.error.message,
153
+ });
151
154
  }
152
155
 
153
156
  return credentialRes.data;
@@ -165,28 +168,25 @@ const handleObtainCredentialError = (e: unknown) => {
165
168
  throw e;
166
169
  }
167
170
 
168
- // Although it is technically not an error, we handle it as such to avoid
169
- // changing the return type of `obtainCredential` and introduce a breaking change.
170
- if (e.statusCode === 201) {
171
- throw new CredentialIssuingNotSynchronousError(
172
- "This credential cannot be issued synchronously. It will be available at a later time.",
173
- e.message
174
- );
175
- }
176
-
177
- if ([403, 404].includes(e.statusCode)) {
178
- const maybeError = CredentialIssuanceFailureResponse.safeParse(
179
- safeJsonParse(e.responseBody)
180
- );
181
- throw new CredentialInvalidStatusError(
182
- "Invalid status found for the given credential",
183
- maybeError.success ? maybeError.data.error : "unknown",
184
- e.message
185
- );
186
- }
187
-
188
- throw new CredentialRequestError(
189
- `Unable to obtain the requested credential [response status code: ${e.statusCode}]`,
190
- e.message
191
- );
171
+ throw new ResponseErrorBuilder(IssuerResponseError)
172
+ .handle(201, {
173
+ // Although it is technically not an error, we handle it as such to avoid
174
+ // changing the return type of `obtainCredential` and introduce a breaking change.
175
+ code: IssuerResponseErrorCodes.CredentialIssuingNotSynchronous,
176
+ message:
177
+ "This credential cannot be issued synchronously. It will be available at a later time.",
178
+ })
179
+ .handle(403, {
180
+ code: IssuerResponseErrorCodes.CredentialInvalidStatus,
181
+ message: "Invalid status found for the given credential",
182
+ })
183
+ .handle(404, {
184
+ code: IssuerResponseErrorCodes.CredentialInvalidStatus,
185
+ message: "Invalid status found for the given credential",
186
+ })
187
+ .handle("*", {
188
+ code: IssuerResponseErrorCodes.CredentialRequestFailed,
189
+ message: "Unable to obtain the requested credential",
190
+ })
191
+ .buildFrom(e);
192
192
  };
@@ -39,20 +39,14 @@ graph TD;
39
39
 
40
40
  ## Mapped results
41
41
 
42
- ### 201 Created (CredentialIssuingNotSynchronousError)
42
+ The following errors are mapped to a `IssuerResponseError` with specific codes.
43
43
 
44
- A `201 Created` response is returned by the credential issuer when the request has been queued because the credential cannot be issued synchronously. The consumer should try to obtain the credential at a later time.
45
-
46
- Although `201 Created` is not considered an error, it is mapped as an error in this context in order to handle the case where the credential issuance is not synchronous.
47
- This allows keeping the flow consistent and handle the case where the credential is not immediately available.
48
-
49
- ### 403 Forbidden (CredentialInvalidStatusError)
50
-
51
- A `403 Forbidden` response is returned by the credential issuer when the requested credential has an invalid status. It might contain more details in the `errorCode` property.
52
-
53
- ### 404 Not Found (CredentialInvalidStatusError)
54
-
55
- A `404 Not Found` response is returned by the credential issuer when the authenticated user is not entitled to receive the requested credential. It might contain more details in the `errorCode` property.
44
+ |HTTP Status|Error Code|Description|
45
+ |-----------|----------|-----------|
46
+ |`201 Created`|`ERR_CREDENTIAL_ISSUING_NOT_SYNCHRONOUS`| This response is returned by the credential issuer when the request has been queued because the credential cannot be issued synchronously. The consumer should try to obtain the credential at a later time. Although `201 Created` is not considered an error, it is mapped as an error in this context in order to handle the case where the credential issuance is not synchronous. This allows keeping the flow consistent and handle the case where the credential is not immediately available.|
47
+ |`403 Forbidden`|`ERR_CREDENTIAL_INVALID_STATUS`|This response is returned by the credential issuer when the requested credential has an invalid status. It might contain more details in the `reason` property.|
48
+ |`404 Not Found`|`ERR_CREDENTIAL_INVALID_STATUS`| This response is returned by the credential issuer when the authenticated user is not entitled to receive the requested credential. It might contain more details in the `reason` property.|
49
+ |`*`|`ERR_ISSUER_GENERIC_ERROR`|This is a generic error code to map unexpected errors that occurred when interacting with the Issuer.|
56
50
 
57
51
  ## Strong authentication for eID issuance (Query Mode)
58
52
 
@@ -278,7 +272,7 @@ const { issuerRequestUri, clientId, codeVerifier, credentialDefinition } =
278
272
  appFetch,
279
273
  });
280
274
 
281
- // Complete the authroization process with query mode with the authorizationContext which opens the browser
275
+ // Complete the authorization process with query mode with the authorizationContext which opens the browser
282
276
  const { code } =
283
277
  await Credential.Issuance.completeUserAuthorizationWithQueryMode(
284
278
  issuerRequestUri,
@@ -0,0 +1,44 @@
1
+ import { IoWalletError, serializeAttrs } from "../../utils/errors";
2
+
3
+ /**
4
+ * An error subclass thrown when an error occurs during the authorization process.
5
+ */
6
+ export class AuthorizationError extends IoWalletError {
7
+ code = "ERR_IO_WALLET_AUTHORIZATION_ERROR";
8
+
9
+ constructor(message?: string) {
10
+ super(message);
11
+ }
12
+ }
13
+
14
+ /**
15
+ * An error subclass thrown when an error occurs during the authorization process with the IDP.
16
+ * It contains the error and error description returned by the IDP.
17
+ */
18
+ export class AuthorizationIdpError extends IoWalletError {
19
+ code = "ERR_IO_WALLET_IDENTIFICATION_RESPONSE_PARSING_FAILED";
20
+
21
+ error: string;
22
+ errorDescription?: string;
23
+
24
+ constructor(error: string, errorDescription?: string) {
25
+ super(serializeAttrs({ error, errorDescription }));
26
+ this.error = error;
27
+ this.errorDescription = errorDescription;
28
+ }
29
+ }
30
+
31
+ /**
32
+ * Error subclass thrown when an operation has been aborted.
33
+ */
34
+ export class OperationAbortedError extends IoWalletError {
35
+ code = "ERR_IO_WALLET_OPERATION_ABORTED";
36
+
37
+ /** The aborted operation */
38
+ operation: string;
39
+
40
+ constructor(operation: string) {
41
+ super(serializeAttrs({ operation }));
42
+ this.operation = operation;
43
+ }
44
+ }
@@ -10,10 +10,12 @@ import {
10
10
  import {
11
11
  completeUserAuthorizationWithQueryMode,
12
12
  completeUserAuthorizationWithFormPostJwtMode,
13
- parseAuthroizationResponse,
13
+ parseAuthorizationResponse,
14
+ buildAuthorizationUrl,
14
15
  type CompleteUserAuthorizationWithQueryMode,
15
16
  type CompleteUserAuthorizationWithFormPostJwtMode,
16
17
  type GetRequestedCredentialToBePresented,
18
+ type BuildAuthorizationUrl,
17
19
  getRequestedCredentialToBePresented,
18
20
  } from "./04-complete-user-authorization";
19
21
  import { authorizeAccess, type AuthorizeAccess } from "./05-authorize-access";
@@ -25,22 +27,26 @@ import {
25
27
  verifyAndParseCredential,
26
28
  type VerifyAndParseCredential,
27
29
  } from "./07-verify-and-parse-credential";
30
+ import * as Errors from "./errors";
28
31
 
29
32
  export {
30
33
  evaluateIssuerTrust,
31
34
  startUserAuthorization,
35
+ buildAuthorizationUrl,
32
36
  completeUserAuthorizationWithQueryMode,
33
37
  getRequestedCredentialToBePresented,
34
38
  completeUserAuthorizationWithFormPostJwtMode,
35
39
  authorizeAccess,
36
40
  obtainCredential,
37
41
  verifyAndParseCredential,
38
- parseAuthroizationResponse,
42
+ parseAuthorizationResponse,
43
+ Errors,
39
44
  };
40
45
  export type {
41
46
  StartFlow,
42
47
  EvaluateIssuerTrust,
43
48
  StartUserAuthorization,
49
+ BuildAuthorizationUrl,
44
50
  CompleteUserAuthorizationWithQueryMode,
45
51
  GetRequestedCredentialToBePresented,
46
52
  CompleteUserAuthorizationWithFormPostJwtMode,
@@ -30,11 +30,3 @@ export const ResponseUriResultShape = z.object({
30
30
  });
31
31
 
32
32
  export type ResponseMode = "query" | "form_post.jwt";
33
-
34
- export const CredentialIssuanceFailureResponse = z.object({
35
- error: z.string(),
36
- });
37
-
38
- export type CredentialIssuanceFailureResponse = z.infer<
39
- typeof CredentialIssuanceFailureResponse
40
- >;
@@ -1,6 +1,6 @@
1
1
  import * as z from "zod";
2
2
  import { decodeBase64 } from "@pagopa/io-react-native-jwt";
3
- import { AuthRequestDecodeError } from "../../utils/errors";
3
+ import { AuthRequestDecodeError } from "./errors";
4
4
 
5
5
  const QRCodePayload = z.object({
6
6
  protocol: z.string(),