@pagopa/io-react-native-wallet 0.13.1 → 0.14.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (63) hide show
  1. package/lib/commonjs/cie/component.js +180 -0
  2. package/lib/commonjs/cie/component.js.map +1 -0
  3. package/lib/commonjs/cie/error.js +44 -0
  4. package/lib/commonjs/cie/error.js.map +1 -0
  5. package/lib/commonjs/cie/index.js +32 -0
  6. package/lib/commonjs/cie/index.js.map +1 -0
  7. package/lib/commonjs/cie/manager.js +142 -0
  8. package/lib/commonjs/cie/manager.js.map +1 -0
  9. package/lib/commonjs/client/index.js +5 -2
  10. package/lib/commonjs/client/index.js.map +1 -1
  11. package/lib/commonjs/credential/issuance/04-complete-user-authorization.js +6 -2
  12. package/lib/commonjs/credential/issuance/04-complete-user-authorization.js.map +1 -1
  13. package/lib/commonjs/credential/issuance/index.js +6 -0
  14. package/lib/commonjs/credential/issuance/index.js.map +1 -1
  15. package/lib/commonjs/index.js +3 -1
  16. package/lib/commonjs/index.js.map +1 -1
  17. package/lib/module/cie/component.js +171 -0
  18. package/lib/module/cie/component.js.map +1 -0
  19. package/lib/module/cie/error.js +36 -0
  20. package/lib/module/cie/error.js.map +1 -0
  21. package/lib/module/cie/index.js +4 -0
  22. package/lib/module/cie/index.js.map +1 -0
  23. package/lib/module/cie/manager.js +133 -0
  24. package/lib/module/cie/manager.js.map +1 -0
  25. package/lib/module/client/index.js +5 -2
  26. package/lib/module/client/index.js.map +1 -1
  27. package/lib/module/credential/issuance/04-complete-user-authorization.js +3 -0
  28. package/lib/module/credential/issuance/04-complete-user-authorization.js.map +1 -1
  29. package/lib/module/credential/issuance/index.js +2 -2
  30. package/lib/module/credential/issuance/index.js.map +1 -1
  31. package/lib/module/index.js +2 -1
  32. package/lib/module/index.js.map +1 -1
  33. package/lib/typescript/cie/component.d.ts +46 -0
  34. package/lib/typescript/cie/component.d.ts.map +1 -0
  35. package/lib/typescript/cie/error.d.ts +31 -0
  36. package/lib/typescript/cie/error.d.ts.map +1 -0
  37. package/lib/typescript/cie/index.d.ts +4 -0
  38. package/lib/typescript/cie/index.d.ts.map +1 -0
  39. package/lib/typescript/cie/manager.d.ts +5 -0
  40. package/lib/typescript/cie/manager.d.ts.map +1 -0
  41. package/lib/typescript/client/index.d.ts.map +1 -1
  42. package/lib/typescript/credential/issuance/04-complete-user-authorization.d.ts +5 -0
  43. package/lib/typescript/credential/issuance/04-complete-user-authorization.d.ts.map +1 -1
  44. package/lib/typescript/credential/issuance/index.d.ts +2 -2
  45. package/lib/typescript/credential/issuance/index.d.ts.map +1 -1
  46. package/lib/typescript/index.d.ts +2 -1
  47. package/lib/typescript/index.d.ts.map +1 -1
  48. package/package.json +6 -2
  49. package/src/cie/component.tsx +216 -0
  50. package/src/cie/error.ts +58 -0
  51. package/src/cie/index.ts +4 -0
  52. package/src/cie/manager.ts +183 -0
  53. package/src/client/index.ts +4 -1
  54. package/src/credential/issuance/04-complete-user-authorization.ts +16 -13
  55. package/src/credential/issuance/index.ts +2 -0
  56. package/src/index.ts +2 -0
  57. package/lib/commonjs/credential/issuance/03-start-credential-issuance.js +0 -287
  58. package/lib/commonjs/credential/issuance/03-start-credential-issuance.js.map +0 -1
  59. package/lib/module/credential/issuance/03-start-credential-issuance.js +0 -276
  60. package/lib/module/credential/issuance/03-start-credential-issuance.js.map +0 -1
  61. package/lib/typescript/credential/issuance/03-start-credential-issuance.d.ts +0 -41
  62. package/lib/typescript/credential/issuance/03-start-credential-issuance.d.ts.map +0 -1
  63. package/src/credential/issuance/03-start-credential-issuance.ts +0 -407
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/credential/issuance/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,SAAS,EAAE,MAAM,iBAAiB,CAAC;AACjD,OAAO,EACL,mBAAmB,EACnB,KAAK,mBAAmB,EACzB,MAAM,4BAA4B,CAAC;AACpC,OAAO,EACL,sBAAsB,EACtB,KAAK,sBAAsB,EAC5B,MAAM,+BAA+B,CAAC;AACvC,OAAO,EACL,sCAAsC,EACtC,KAAK,sCAAsC,EAC5C,MAAM,kCAAkC,CAAC;AAC1C,OAAO,EAAE,eAAe,EAAE,KAAK,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAC9E,OAAO,EACL,gBAAgB,EAChB,KAAK,gBAAgB,EACtB,MAAM,wBAAwB,CAAC;AAChC,OAAO,EACL,wBAAwB,EACxB,KAAK,wBAAwB,EAC9B,MAAM,kCAAkC,CAAC;AAE1C,OAAO,EACL,mBAAmB,EACnB,sBAAsB,EACtB,sCAAsC,EACtC,eAAe,EACf,gBAAgB,EAChB,wBAAwB,GACzB,CAAC;AACF,YAAY,EACV,SAAS,EACT,mBAAmB,EACnB,sBAAsB,EACtB,sCAAsC,EACtC,eAAe,EACf,gBAAgB,EAChB,wBAAwB,GACzB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/credential/issuance/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,SAAS,EAAE,MAAM,iBAAiB,CAAC;AACjD,OAAO,EACL,mBAAmB,EACnB,KAAK,mBAAmB,EACzB,MAAM,4BAA4B,CAAC;AACpC,OAAO,EACL,sBAAsB,EACtB,KAAK,sBAAsB,EAC5B,MAAM,+BAA+B,CAAC;AACvC,OAAO,EACL,sCAAsC,EACtC,oBAAoB,EACpB,KAAK,sCAAsC,EAC5C,MAAM,kCAAkC,CAAC;AAC1C,OAAO,EAAE,eAAe,EAAE,KAAK,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAC9E,OAAO,EACL,gBAAgB,EAChB,KAAK,gBAAgB,EACtB,MAAM,wBAAwB,CAAC;AAChC,OAAO,EACL,wBAAwB,EACxB,KAAK,wBAAwB,EAC9B,MAAM,kCAAkC,CAAC;AAE1C,OAAO,EACL,mBAAmB,EACnB,sBAAsB,EACtB,sCAAsC,EACtC,eAAe,EACf,gBAAgB,EAChB,wBAAwB,EACxB,oBAAoB,GACrB,CAAC;AACF,YAAY,EACV,SAAS,EACT,mBAAmB,EACnB,sBAAsB,EACtB,sCAAsC,EACtC,eAAe,EACf,gBAAgB,EAChB,wBAAwB,GACzB,CAAC"}
@@ -8,9 +8,10 @@ import * as Errors from "./utils/errors";
8
8
  import * as WalletInstanceAttestation from "./wallet-instance-attestation";
9
9
  import * as Trust from "./trust";
10
10
  import * as WalletInstance from "./wallet-instance";
11
+ import * as Cie from "./cie";
11
12
  import { AuthorizationDetail, AuthorizationDetails } from "./utils/par";
12
13
  import { createCryptoContextFor } from "./utils/crypto";
13
14
  import type { IntegrityContext } from "./utils/integrity";
14
- export { SdJwt, PID, Credential, WalletInstanceAttestation, WalletInstance, Errors, Trust, createCryptoContextFor, AuthorizationDetail, AuthorizationDetails, fixBase64EncodingOnKey, };
15
+ export { SdJwt, PID, Credential, WalletInstanceAttestation, WalletInstance, Errors, Trust, createCryptoContextFor, AuthorizationDetail, AuthorizationDetails, fixBase64EncodingOnKey, Cie, };
15
16
  export type { IntegrityContext, AuthorizationContext };
16
17
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,cAAc,CAAC;AACzD,OAAO,EAAE,sBAAsB,EAAE,MAAM,aAAa,CAAC;AAGrD,OAAO,gCAAgC,CAAC;AAExC,OAAO,KAAK,UAAU,MAAM,cAAc,CAAC;AAC3C,OAAO,KAAK,GAAG,MAAM,OAAO,CAAC;AAC7B,OAAO,KAAK,KAAK,MAAM,UAAU,CAAC;AAClC,OAAO,KAAK,MAAM,MAAM,gBAAgB,CAAC;AACzC,OAAO,KAAK,yBAAyB,MAAM,+BAA+B,CAAC;AAC3E,OAAO,KAAK,KAAK,MAAM,SAAS,CAAC;AACjC,OAAO,KAAK,cAAc,MAAM,mBAAmB,CAAC;AACpD,OAAO,EAAE,mBAAmB,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AACxE,OAAO,EAAE,sBAAsB,EAAE,MAAM,gBAAgB,CAAC;AACxD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAE1D,OAAO,EACL,KAAK,EACL,GAAG,EACH,UAAU,EACV,yBAAyB,EACzB,cAAc,EACd,MAAM,EACN,KAAK,EACL,sBAAsB,EACtB,mBAAmB,EACnB,oBAAoB,EACpB,sBAAsB,GACvB,CAAC;AAEF,YAAY,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,cAAc,CAAC;AACzD,OAAO,EAAE,sBAAsB,EAAE,MAAM,aAAa,CAAC;AAGrD,OAAO,gCAAgC,CAAC;AAExC,OAAO,KAAK,UAAU,MAAM,cAAc,CAAC;AAC3C,OAAO,KAAK,GAAG,MAAM,OAAO,CAAC;AAC7B,OAAO,KAAK,KAAK,MAAM,UAAU,CAAC;AAClC,OAAO,KAAK,MAAM,MAAM,gBAAgB,CAAC;AACzC,OAAO,KAAK,yBAAyB,MAAM,+BAA+B,CAAC;AAC3E,OAAO,KAAK,KAAK,MAAM,SAAS,CAAC;AACjC,OAAO,KAAK,cAAc,MAAM,mBAAmB,CAAC;AACpD,OAAO,KAAK,GAAG,MAAM,OAAO,CAAC;AAC7B,OAAO,EAAE,mBAAmB,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AACxE,OAAO,EAAE,sBAAsB,EAAE,MAAM,gBAAgB,CAAC;AACxD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAE1D,OAAO,EACL,KAAK,EACL,GAAG,EACH,UAAU,EACV,yBAAyB,EACzB,cAAc,EACd,MAAM,EACN,KAAK,EACL,sBAAsB,EACtB,mBAAmB,EACnB,oBAAoB,EACpB,sBAAsB,EACtB,GAAG,GACJ,CAAC;AAEF,YAAY,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pagopa/io-react-native-wallet",
3
- "version": "0.13.1",
3
+ "version": "0.14.0",
4
4
  "description": "Provide data structures, helpers and API for IO Wallet",
5
5
  "main": "lib/commonjs/index",
6
6
  "module": "lib/module/index",
@@ -56,6 +56,7 @@
56
56
  "@pagopa/eslint-config": "^3.0.0",
57
57
  "@pagopa/io-react-native-crypto": "^0.2.3",
58
58
  "@pagopa/io-react-native-jwt": "^1.2.0",
59
+ "@pagopa/react-native-cie": "^1.3.0",
59
60
  "@react-native/eslint-config": "^0.72.2",
60
61
  "@rushstack/eslint-patch": "^1.3.2",
61
62
  "@types/jest": "^28.1.2",
@@ -70,6 +71,7 @@
70
71
  "react": "18.2.0",
71
72
  "react-native": "0.72.14",
72
73
  "react-native-builder-bob": "^0.20.0",
74
+ "react-native-webview": "^13.10.5",
73
75
  "typed-openapi": "^0.4.1",
74
76
  "typescript": "^5.0.2"
75
77
  },
@@ -79,8 +81,10 @@
79
81
  "peerDependencies": {
80
82
  "@pagopa/io-react-native-crypto": "*",
81
83
  "@pagopa/io-react-native-jwt": "*",
84
+ "@pagopa/react-native-cie": "*",
82
85
  "react": "*",
83
- "react-native": "*"
86
+ "react-native": "*",
87
+ "react-native-webview": "*"
84
88
  },
85
89
  "engines": {
86
90
  "node": ">= 16.0.0"
@@ -0,0 +1,216 @@
1
+ import React, { createRef } from "react";
2
+ import { Platform } from "react-native";
3
+ import { WebView } from "react-native-webview";
4
+
5
+ import type {
6
+ WebViewErrorEvent,
7
+ WebViewNavigationEvent,
8
+ WebViewMessageEvent,
9
+ WebViewNavigation,
10
+ WebViewHttpErrorEvent,
11
+ } from "react-native-webview/lib/WebViewTypes";
12
+ import { startCieAndroid, startCieiOS, type ContinueWithUrl } from "./manager";
13
+ import { CieError, CieErrorType } from "./error";
14
+
15
+ /* To obtain the authentication URL on CIE L3 it is necessary to take the
16
+ * link contained in the "Entra con lettura carta CIE" button.
17
+ * This link can then be used on CieManager.
18
+ * This javascript code takes the link in question and sends it to the react native function via postMessage
19
+ */
20
+ const injectedJavaScript = `
21
+ (function() {
22
+ function sendDocumentContent() {
23
+ const idpAuthUrl = [...document.querySelectorAll("a")]
24
+ .filter(a => a.textContent.includes("lettura carta CIE"))
25
+ .map(a=>a.href)[0];
26
+
27
+ if(idpAuthUrl) {
28
+ window.ReactNativeWebView.postMessage(idpAuthUrl);
29
+ }
30
+ }
31
+ if (document.readyState === 'complete') {
32
+ sendDocumentContent();
33
+ } else {
34
+ window.addEventListener('load', sendDocumentContent);
35
+ }
36
+ })();
37
+ true;
38
+ `;
39
+ export type OnSuccess = (url: string) => void;
40
+ export type OnError = (e: CieError) => void;
41
+ export type OnCieEvent = (e: CieEvent) => void;
42
+ export enum CieEvent {
43
+ "reading" = "reading",
44
+ "completed" = "completed",
45
+ "waiting_card" = "waiting_card",
46
+ }
47
+
48
+ type CIEParams = {
49
+ authUrl: string;
50
+ onSuccess: OnSuccess;
51
+ onError: OnError;
52
+ pin: string;
53
+ useUat: boolean;
54
+ redirectUrl: string;
55
+ onEvent: OnCieEvent;
56
+ };
57
+
58
+ /*
59
+ * To make sure the server recognizes the client as valid iPhone device (iOS only) we use a custom header
60
+ * on Android it is not required.
61
+ */
62
+ const iOSUserAgent =
63
+ "Mozilla/5.0 (iPhone; CPU iPhone OS 14_0_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.0 Mobile/15E148 Safari/604.1";
64
+ const defaultUserAgent = Platform.select({
65
+ ios: iOSUserAgent,
66
+ default: undefined,
67
+ });
68
+
69
+ const webView = createRef<WebView>();
70
+
71
+ /**
72
+ * WebViewComponent
73
+ *
74
+ * Component that manages authentication via CIE L3 (NFC+PIN) based on WebView (react-native-webview).
75
+ * In particular, once rendered, it makes a series of calls to the authUrl in the WebView,
76
+ * extrapolates the authentication URL necessary for CieManager to sign via certificate
77
+ * and calls the CIE SDK which is responsible for starting card reading via NFC.
78
+ * At the end of the reading, a redirect is made in the WebView towards the page that asks
79
+ * the user for consent to send the data to the Service Provider. This moment can be captured
80
+ * via the onUserInteraction parameter. When the user allows or denies their consent,
81
+ * a redirect is made to the URL set by the Service Provider.
82
+ * This url can be configured using the redirectUrl parameter which allows you to close the WebView.
83
+ * The event can then be captured via the onSuccess parameter.
84
+ *
85
+ * @param {CIEParams} params - Parameters required by the component.
86
+ * @param {string} params.authUrl -The authentication URL of the Service Provider to which to authenticate.
87
+ * @param {boolean} params.useUat - If set to true it uses the CIE testing environment.
88
+ * @param {string} params.pin - CIE pin for use with NFC reading.
89
+ * @param {Function} params.onError - Callback function in case of error. The function is passed the Error parameter.
90
+ * @param {Function} params.onSuccess - Callback at the end of authentication to which the redirect URL including parameters is passed.
91
+ * @param {string} params.redirectUrl - Redirect URL set by the Service Provider. It is used to stop the flow and return to the calling function via onSuccess.
92
+ * @param {Function} params.onEvent - Callback function that is called whenever there is a new CieEvent from the CIE reader.
93
+ * @returns {JSX.Element} - The configured component with WebView.
94
+ */
95
+ export const WebViewComponent = (params: CIEParams) => {
96
+ const [webViewUrl, setWebViewUrl] = React.useState(params.authUrl);
97
+ const [isCardReadingFinished, setCardReadingFinished] = React.useState(false);
98
+
99
+ /*
100
+ * Once the reading of the card with NFC is finished, it is necessary
101
+ * to change the URL of the WebView by redirecting to the URL returned by
102
+ * CieManager to allow the user to continue with the consent authorization
103
+ * */
104
+ const continueWithUrl: ContinueWithUrl = (callbackUrl: string) => {
105
+ setCardReadingFinished(true);
106
+ setWebViewUrl(callbackUrl);
107
+ };
108
+
109
+ // This function is called from the injected javascript code (postMessage). Which receives the authentication URL
110
+ const handleMessage = async (event: WebViewMessageEvent) => {
111
+ const cieAuthorizationUri = event.nativeEvent.data;
112
+ const startCie = Platform.select({
113
+ ios: startCieiOS,
114
+ default: startCieAndroid,
115
+ });
116
+ await startCie(
117
+ params.useUat,
118
+ params.pin,
119
+ params.onError,
120
+ params.onEvent,
121
+ cieAuthorizationUri,
122
+ continueWithUrl
123
+ );
124
+ };
125
+
126
+ //This function is called when authentication with CIE ends and the SP URL containing code and state is returned
127
+ const handleShouldStartLoading =
128
+ (onSuccess: OnSuccess, redirectUrl: string) =>
129
+ (event: WebViewNavigation): boolean => {
130
+ if (isCardReadingFinished && event.url.includes(redirectUrl)) {
131
+ onSuccess(event.url);
132
+ return false;
133
+ } else {
134
+ return true;
135
+ }
136
+ };
137
+
138
+ const handleOnLoadEnd =
139
+ (onError: OnError, onCieEvent: OnCieEvent) =>
140
+ (e: WebViewNavigationEvent | WebViewErrorEvent) => {
141
+ const eventTitle = e.nativeEvent.title.toLowerCase();
142
+ if (
143
+ eventTitle === "pagina web non disponibile" ||
144
+ // On Android, if we attempt to access the idp URL twice,
145
+ // we are presented with an error page titled "ERROR".
146
+ eventTitle === "errore"
147
+ ) {
148
+ handleOnError(onError)(new Error(eventTitle));
149
+ }
150
+
151
+ /* At the end of loading the page, if the card has already been read
152
+ * then the WebView has loaded the page to ask the user for consent,
153
+ * so send the completed event
154
+ * */
155
+ if (isCardReadingFinished) {
156
+ onCieEvent(CieEvent.completed);
157
+ }
158
+ };
159
+
160
+ const handleOnError =
161
+ (onError: OnError) =>
162
+ (e: WebViewErrorEvent | WebViewHttpErrorEvent | Error): void => {
163
+ const error = e as Error;
164
+ const webViewError = e as WebViewErrorEvent;
165
+ const webViewHttpError = e as WebViewHttpErrorEvent;
166
+ if (webViewHttpError.nativeEvent.statusCode) {
167
+ const { description, statusCode } = webViewHttpError.nativeEvent;
168
+ onError(
169
+ new CieError({
170
+ message: `WebView http error: ${description} with status code: ${statusCode}`,
171
+ type: CieErrorType.WEB_VIEW_ERROR,
172
+ })
173
+ );
174
+ } else if (webViewError.nativeEvent) {
175
+ const { code, description } = webViewError.nativeEvent;
176
+ onError(
177
+ new CieError({
178
+ message: `WebView error: ${description} with code: ${code}`,
179
+ type: CieErrorType.WEB_VIEW_ERROR,
180
+ })
181
+ );
182
+ } else if (error.message !== undefined) {
183
+ onError(
184
+ new CieError({
185
+ message: `${error.message}`,
186
+ type: CieErrorType.WEB_VIEW_ERROR,
187
+ })
188
+ );
189
+ } else {
190
+ onError(
191
+ new CieError({
192
+ message: "An error occurred in the WebView",
193
+ type: CieErrorType.WEB_VIEW_ERROR,
194
+ })
195
+ );
196
+ }
197
+ };
198
+
199
+ return (
200
+ <WebView
201
+ ref={webView}
202
+ userAgent={defaultUserAgent}
203
+ javaScriptEnabled={true}
204
+ source={{ uri: webViewUrl }}
205
+ onLoadEnd={handleOnLoadEnd(params.onError, params.onEvent)}
206
+ onError={handleOnError(params.onError)}
207
+ onHttpError={handleOnError(params.onError)}
208
+ injectedJavaScript={injectedJavaScript}
209
+ onShouldStartLoadWithRequest={handleShouldStartLoading(
210
+ params.onSuccess,
211
+ params.redirectUrl
212
+ )}
213
+ onMessage={handleMessage}
214
+ />
215
+ );
216
+ };
@@ -0,0 +1,58 @@
1
+ export enum CieErrorType {
2
+ GENERIC,
3
+ TAG_NOT_VALID,
4
+ WEB_VIEW_ERROR,
5
+ NFC_ERROR,
6
+ AUTHENTICATION_ERROR,
7
+ PIN_ERROR,
8
+ PIN_LOCKED,
9
+ CERTIFICATE_ERROR,
10
+ }
11
+
12
+ interface BaseCieError {
13
+ message: string;
14
+ type?: CieErrorType;
15
+ }
16
+
17
+ interface PinErrorOptions extends BaseCieError {
18
+ type: CieErrorType.PIN_ERROR;
19
+ attemptsLeft: number;
20
+ }
21
+
22
+ interface NonPinErrorOptions extends BaseCieError {
23
+ type?: Exclude<CieErrorType, CieErrorType.PIN_ERROR>;
24
+ attemptsLeft?: number;
25
+ }
26
+
27
+ type ErrorOptions = PinErrorOptions | NonPinErrorOptions;
28
+
29
+ export class CieError extends Error {
30
+ public type: CieErrorType;
31
+ public attemptsLeft?: number;
32
+ constructor(options: ErrorOptions) {
33
+ super(options.message);
34
+
35
+ if (options.type) {
36
+ this.type = options.type;
37
+ } else {
38
+ this.type = CieErrorType.GENERIC;
39
+ }
40
+
41
+ if (this.type === CieErrorType.PIN_ERROR) {
42
+ this.attemptsLeft = options.attemptsLeft;
43
+ } else if (this.type === CieErrorType.PIN_LOCKED) {
44
+ this.attemptsLeft = 0;
45
+ }
46
+
47
+ this.name = this.constructor.name;
48
+ }
49
+
50
+ toString(): string {
51
+ return JSON.stringify({
52
+ name: this.name,
53
+ type: CieErrorType[this.type],
54
+ message: this.message,
55
+ attemptsLeft: this.attemptsLeft,
56
+ });
57
+ }
58
+ }
@@ -0,0 +1,4 @@
1
+ import { CieEvent, WebViewComponent } from "./component";
2
+ import { CieError, CieErrorType } from "./error";
3
+
4
+ export { WebViewComponent, CieError, CieErrorType, CieEvent };
@@ -0,0 +1,183 @@
1
+ import cieManager, { type Event as CEvent } from "@pagopa/react-native-cie";
2
+ import { Platform } from "react-native";
3
+ import { CieEvent, type OnCieEvent, type OnError } from "./component";
4
+ import { CieError, CieErrorType } from "./error";
5
+
6
+ const BASE_UAT_URL = "https://collaudo.idserver.servizicie.interno.gov.it/idp/";
7
+
8
+ export type ContinueWithUrl = (callbackUrl: string) => void;
9
+
10
+ export const startCieAndroid = (
11
+ useCieUat: boolean,
12
+ ciePin: string,
13
+ onError: OnError,
14
+ onEvent: OnCieEvent,
15
+ cieAuthorizationUri: string,
16
+ continueWithUrl: ContinueWithUrl
17
+ ) => {
18
+ try {
19
+ cieManager.removeAllListeners();
20
+ cieManager
21
+ .start()
22
+ .then(async () => {
23
+ cieManager.onEvent(handleCieEvent(onError, onEvent));
24
+ cieManager.onError((e: Error) => {
25
+ console.error(e);
26
+ return onError(new CieError({ message: e.message }));
27
+ });
28
+ cieManager.onSuccess(handleCieSuccess(continueWithUrl));
29
+ await cieManager.setPin(ciePin);
30
+ cieManager.setAuthenticationUrl(cieAuthorizationUri);
31
+ cieManager.enableLog(useCieUat);
32
+ cieManager.setCustomIdpUrl(useCieUat ? getCieUatEndpoint() : null);
33
+ await cieManager.startListeningNFC();
34
+ onEvent(CieEvent.waiting_card);
35
+ })
36
+ .catch(onError);
37
+ } catch {
38
+ onError(
39
+ new CieError({
40
+ message: "Unable to start CIE NFC manager on iOS",
41
+ type: CieErrorType.NFC_ERROR,
42
+ })
43
+ );
44
+ }
45
+ };
46
+
47
+ export const startCieiOS = async (
48
+ useCieUat: boolean,
49
+ ciePin: string,
50
+ onError: OnError,
51
+ onEvent: OnCieEvent,
52
+ cieAuthorizationUri: string,
53
+ continueWithUrl: ContinueWithUrl
54
+ ) => {
55
+ try {
56
+ cieManager.removeAllListeners();
57
+ cieManager.onEvent(handleCieEvent(onError, onEvent));
58
+ cieManager.onError((e: Error) =>
59
+ onError(new CieError({ message: e.message }))
60
+ );
61
+ cieManager.onSuccess(handleCieSuccess(continueWithUrl));
62
+ cieManager.enableLog(useCieUat);
63
+ cieManager.setCustomIdpUrl(useCieUat ? getCieUatEndpoint() : null);
64
+ await cieManager.setPin(ciePin);
65
+ cieManager.setAuthenticationUrl(cieAuthorizationUri);
66
+ cieManager
67
+ .start()
68
+ .then(async () => {
69
+ await cieManager.startListeningNFC();
70
+ onEvent(CieEvent.waiting_card);
71
+ })
72
+ .catch(onError);
73
+ } catch {
74
+ onError(
75
+ new CieError({
76
+ message: "Unable to start CIE NFC manager on Android",
77
+ type: CieErrorType.NFC_ERROR,
78
+ })
79
+ );
80
+ }
81
+ };
82
+
83
+ const handleCieEvent =
84
+ (onError: OnError, onEvent: OnCieEvent) => (event: CEvent) => {
85
+ switch (event.event) {
86
+ // Reading starts
87
+ case "ON_TAG_DISCOVERED":
88
+ onEvent(CieEvent.reading);
89
+ break;
90
+ // "Function not supported" seems to be TAG_ERROR_NFC_NOT_SUPPORTED
91
+ // for the iOS SDK
92
+ case "Function not supported" as unknown:
93
+ case "TAG_ERROR_NFC_NOT_SUPPORTED":
94
+ case "ON_TAG_DISCOVERED_NOT_CIE":
95
+ onError(
96
+ new CieError({
97
+ message: `Invalid CIE card: ${event.event}`,
98
+ type: CieErrorType.TAG_NOT_VALID,
99
+ })
100
+ );
101
+ break;
102
+ case "AUTHENTICATION_ERROR":
103
+ case "ON_NO_INTERNET_CONNECTION":
104
+ onError(
105
+ new CieError({
106
+ message: `Authentication error or no internet connection`,
107
+ type: CieErrorType.AUTHENTICATION_ERROR,
108
+ })
109
+ );
110
+ break;
111
+ case "EXTENDED_APDU_NOT_SUPPORTED":
112
+ onError(
113
+ new CieError({
114
+ message: `APDU not supported`,
115
+ type: CieErrorType.NFC_ERROR,
116
+ })
117
+ );
118
+ break;
119
+ case "Transmission Error":
120
+ case "ON_TAG_LOST":
121
+ onError(
122
+ new CieError({
123
+ message: `Trasmission error`,
124
+ type: CieErrorType.NFC_ERROR,
125
+ })
126
+ );
127
+ break;
128
+
129
+ // The card is temporarily locked. Unlock is available by CieID app
130
+ case "PIN Locked":
131
+ case "ON_CARD_PIN_LOCKED":
132
+ onError(
133
+ new CieError({
134
+ message: `PIN locked`,
135
+ type: CieErrorType.PIN_LOCKED,
136
+ })
137
+ );
138
+ break;
139
+ case "ON_PIN_ERROR":
140
+ onError(
141
+ new CieError({
142
+ message: `PIN locked`,
143
+ type: CieErrorType.PIN_ERROR,
144
+ attemptsLeft: event.attemptsLeft,
145
+ })
146
+ );
147
+ break;
148
+
149
+ // CIE is Expired or Revoked
150
+ case "CERTIFICATE_EXPIRED":
151
+ onError(
152
+ new CieError({
153
+ message: `Certificate expired`,
154
+ type: CieErrorType.CERTIFICATE_ERROR,
155
+ })
156
+ );
157
+ break;
158
+ case "CERTIFICATE_REVOKED":
159
+ onError(
160
+ new CieError({
161
+ message: `Certificate revoked`,
162
+ type: CieErrorType.CERTIFICATE_ERROR,
163
+ })
164
+ );
165
+
166
+ break;
167
+
168
+ default:
169
+ break;
170
+ }
171
+ };
172
+
173
+ const handleCieSuccess =
174
+ (continueWithUrl: ContinueWithUrl) => (url: string) => {
175
+ continueWithUrl(decodeURIComponent(url));
176
+ };
177
+
178
+ const getCieUatEndpoint = () =>
179
+ Platform.select({
180
+ ios: `${BASE_UAT_URL}Authn/SSL/Login2`,
181
+ android: BASE_UAT_URL,
182
+ default: null,
183
+ });
@@ -39,11 +39,14 @@ export const getWalletProviderClient = (context: {
39
39
  appFetch(url, {
40
40
  method,
41
41
  body: params ? JSON.stringify(params.body) : undefined,
42
+ headers: {
43
+ "Content-Type": "application/json",
44
+ },
42
45
  })
43
46
  .then(validateResponse)
44
47
  .then((res) => {
45
48
  const contentType = res.headers.get("content-type");
46
- if (contentType === "application/json") {
49
+ if (contentType?.includes("application/json")) {
47
50
  return res.json();
48
51
  }
49
52
  return res.text();
@@ -96,21 +96,24 @@ export const completeUserAuthorizationWithQueryMode: CompleteUserAuthorizationWi
96
96
  throw new AuthorizationError("Invalid authentication redirect url");
97
97
  }
98
98
  }
99
+ return parseAuthRedirectUrl(authRedirectUrl);
100
+ };
99
101
 
100
- const urlParse = parseUrl(authRedirectUrl);
101
- const authRes = AuthorizationResultShape.safeParse(urlParse.query);
102
- if (!authRes.success) {
103
- const authErr = AuthorizationErrorShape.safeParse(urlParse.query);
104
- if (!authErr.success) {
105
- throw new AuthorizationError(authRes.error.message); // an error occured while parsing the result and the error
106
- }
107
- throw new AuthorizationIdpError(
108
- authErr.data.error,
109
- authErr.data.error_description
110
- );
102
+ export const parseAuthRedirectUrl = (authRedirectUrl: string) => {
103
+ const urlParse = parseUrl(authRedirectUrl);
104
+ const authRes = AuthorizationResultShape.safeParse(urlParse.query);
105
+ if (!authRes.success) {
106
+ const authErr = AuthorizationErrorShape.safeParse(urlParse.query);
107
+ if (!authErr.success) {
108
+ throw new AuthorizationError(authRes.error.message); // an error occured while parsing the result and the error
111
109
  }
112
- return authRes.data;
113
- };
110
+ throw new AuthorizationIdpError(
111
+ authErr.data.error,
112
+ authErr.data.error_description
113
+ );
114
+ }
115
+ return authRes.data;
116
+ };
114
117
 
115
118
  // TODO: SIW-1120 implement generic credential issuance flow
116
119
  export const completeUserAuthorizationWithFormPostJwtMode = () => {
@@ -9,6 +9,7 @@ import {
9
9
  } from "./03-start-user-authorization";
10
10
  import {
11
11
  completeUserAuthorizationWithQueryMode,
12
+ parseAuthRedirectUrl,
12
13
  type CompleteUserAuthorizationWithQueryMode,
13
14
  } from "./04-complete-user-authorization";
14
15
  import { authorizeAccess, type AuthorizeAccess } from "./05-authorize-access";
@@ -28,6 +29,7 @@ export {
28
29
  authorizeAccess,
29
30
  obtainCredential,
30
31
  verifyAndParseCredential,
32
+ parseAuthRedirectUrl,
31
33
  };
32
34
  export type {
33
35
  StartFlow,
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 Cie from "./cie";
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
+ Cie,
30
32
  };
31
33
 
32
34
  export type { IntegrityContext, AuthorizationContext };