@oobit/react-native-sdk 1.0.5 → 1.0.7
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.
- package/dist/WidgetSDK.d.ts.map +1 -1
- package/dist/WidgetSDK.js +21 -47
- package/dist/WidgetSDK.js.map +1 -1
- package/dist/biometricUtils.d.ts +8 -8
- package/dist/biometricUtils.d.ts.map +1 -1
- package/dist/biometricUtils.js +42 -38
- package/dist/biometricUtils.js.map +1 -1
- package/dist/cryptoUtils.d.ts +3 -6
- package/dist/cryptoUtils.d.ts.map +1 -1
- package/dist/cryptoUtils.js +16 -36
- package/dist/cryptoUtils.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/dist/types.d.ts +21 -4
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -1
- package/package.json +10 -6
- package/src/WidgetSDK.tsx +22 -77
- package/src/biometricUtils.ts +47 -53
- package/src/cryptoUtils.ts +15 -44
- package/src/index.ts +1 -1
- package/src/types.ts +27 -4
package/dist/WidgetSDK.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"WidgetSDK.d.ts","sourceRoot":"","sources":["../src/WidgetSDK.tsx"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAON,MAAM,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"WidgetSDK.d.ts","sourceRoot":"","sources":["../src/WidgetSDK.tsx"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAON,MAAM,OAAO,CAAC;AAWf,OAAO,EAAyC,eAAe,EAAE,MAAM,SAAS,CAAC;AAIjF,MAAM,WAAW,YAAY;IAC3B,YAAY,EAAE,MAAM,IAAI,CAAC;CAC1B;AAED,eAAO,MAAM,SAAS,sFAoSrB,CAAC"}
|
package/dist/WidgetSDK.js
CHANGED
|
@@ -41,10 +41,9 @@ exports.WidgetSDK = void 0;
|
|
|
41
41
|
const react_1 = __importStar(require("react"));
|
|
42
42
|
const react_native_1 = require("react-native");
|
|
43
43
|
const react_native_webview_1 = require("react-native-webview");
|
|
44
|
-
const biometricUtils_1 = require("./biometricUtils");
|
|
45
44
|
const config_1 = require("./config");
|
|
46
|
-
const cryptoUtils_1 = require("./cryptoUtils");
|
|
47
45
|
const walletUtils_1 = require("./walletUtils");
|
|
46
|
+
const Clipboard = __importStar(require("expo-clipboard"));
|
|
48
47
|
exports.WidgetSDK = (0, react_1.forwardRef)(({ accessToken, userWalletAddress, environment, onReady, onCardCreated, onError, onClose, onTransactionRequested, }, ref) => {
|
|
49
48
|
const webViewRef = (0, react_1.useRef)(null);
|
|
50
49
|
const [walletAvailable, setWalletAvailable] = (0, react_1.useState)(false);
|
|
@@ -110,8 +109,8 @@ exports.WidgetSDK = (0, react_1.forwardRef)(({ accessToken, userWalletAddress, e
|
|
|
110
109
|
console.error("[WidgetSDK] Access token expired");
|
|
111
110
|
onError?.("TOKEN_EXPIRED", "Access token has expired");
|
|
112
111
|
break;
|
|
113
|
-
case "widget:
|
|
114
|
-
|
|
112
|
+
case "widget:copy-to-clipboard":
|
|
113
|
+
handleCopyToClipboard(message);
|
|
115
114
|
break;
|
|
116
115
|
default:
|
|
117
116
|
console.warn("[WidgetSDK] Unknown message type:", message);
|
|
@@ -174,55 +173,30 @@ exports.WidgetSDK = (0, react_1.forwardRef)(({ accessToken, userWalletAddress, e
|
|
|
174
173
|
onTransactionRequested?.(token, cryptoAmount, depositAddress, depositAddressTag);
|
|
175
174
|
};
|
|
176
175
|
/**
|
|
177
|
-
* Handle
|
|
178
|
-
*
|
|
179
|
-
* 2. Generate cryptographically linked sessionId + secretKey pair
|
|
180
|
-
* 3. Send credentials back to widget for API call and decryption
|
|
176
|
+
* Handle clipboard copy request from widget
|
|
177
|
+
* Copies text to device clipboard and sends result back to widget
|
|
181
178
|
*/
|
|
182
|
-
const
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
console.log("[WidgetSDK] Card details requested, initiating biometric auth...");
|
|
187
|
-
// Step 1: Authenticate with biometrics
|
|
188
|
-
const biometricResult = await (0, biometricUtils_1.authenticateWithBiometrics)("Authenticate to view card details");
|
|
189
|
-
if (!biometricResult.success) {
|
|
190
|
-
console.log("[WidgetSDK] Biometric auth failed:", biometricResult.error);
|
|
191
|
-
const failedMessage = {
|
|
192
|
-
type: "native:biometric-failed",
|
|
193
|
-
payload: {
|
|
194
|
-
reason: biometricResult.error?.reason || "failed",
|
|
195
|
-
message: biometricResult.error?.message,
|
|
196
|
-
},
|
|
197
|
-
};
|
|
198
|
-
sendMessageToWidget(failedMessage);
|
|
199
|
-
return;
|
|
200
|
-
}
|
|
201
|
-
// Step 2: Generate session credentials
|
|
179
|
+
const handleCopyToClipboard = async (message) => {
|
|
180
|
+
const { text, label } = message.payload;
|
|
181
|
+
// Log only the label, never the actual text content (security)
|
|
182
|
+
console.log("[WidgetSDK] Copy to clipboard requested:", label || "unlabeled");
|
|
202
183
|
try {
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
type: "native:card-details-session",
|
|
209
|
-
payload: {
|
|
210
|
-
sessionId,
|
|
211
|
-
secretKey: secretKeyHex,
|
|
212
|
-
},
|
|
213
|
-
};
|
|
214
|
-
sendMessageToWidget(sessionMessage);
|
|
184
|
+
await Clipboard.setStringAsync(text);
|
|
185
|
+
sendMessageToWidget({
|
|
186
|
+
type: "native:clipboard-result",
|
|
187
|
+
payload: { success: true, label },
|
|
188
|
+
});
|
|
215
189
|
}
|
|
216
190
|
catch (error) {
|
|
217
|
-
console.error("[WidgetSDK]
|
|
218
|
-
|
|
219
|
-
type: "native:
|
|
191
|
+
console.error("[WidgetSDK] Clipboard copy failed:", error);
|
|
192
|
+
sendMessageToWidget({
|
|
193
|
+
type: "native:clipboard-result",
|
|
220
194
|
payload: {
|
|
221
|
-
|
|
222
|
-
|
|
195
|
+
success: false,
|
|
196
|
+
label,
|
|
197
|
+
error: "Clipboard operation failed",
|
|
223
198
|
},
|
|
224
|
-
};
|
|
225
|
-
sendMessageToWidget(failedMessage);
|
|
199
|
+
});
|
|
226
200
|
}
|
|
227
201
|
};
|
|
228
202
|
/**
|
package/dist/WidgetSDK.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"WidgetSDK.js","sourceRoot":"","sources":["../src/WidgetSDK.tsx"],"names":[],"mappings":";AAAA;;;GAGG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAEH,+CAOe;AACf,+CAOsB;AACtB,+DAAoE;AACpE,
|
|
1
|
+
{"version":3,"file":"WidgetSDK.js","sourceRoot":"","sources":["../src/WidgetSDK.tsx"],"names":[],"mappings":"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"}
|
package/dist/biometricUtils.d.ts
CHANGED
|
@@ -2,11 +2,11 @@
|
|
|
2
2
|
* Biometric Authentication Utilities
|
|
3
3
|
*
|
|
4
4
|
* Provides cross-platform biometric authentication for card details access.
|
|
5
|
-
*
|
|
5
|
+
* Uses expo-local-authentication which works in Expo Go without native modules.
|
|
6
6
|
*
|
|
7
|
-
* @requires
|
|
7
|
+
* @requires expo-local-authentication - included in Expo SDK
|
|
8
8
|
*/
|
|
9
|
-
import
|
|
9
|
+
import * as LocalAuthentication from 'expo-local-authentication';
|
|
10
10
|
/**
|
|
11
11
|
* Result of a biometric authentication attempt
|
|
12
12
|
*/
|
|
@@ -25,13 +25,13 @@ export interface BiometricResult {
|
|
|
25
25
|
export interface BiometricAvailability {
|
|
26
26
|
/** Whether any biometric authentication is available */
|
|
27
27
|
available: boolean;
|
|
28
|
-
/** The
|
|
29
|
-
|
|
28
|
+
/** The types of biometry available */
|
|
29
|
+
biometryTypes: LocalAuthentication.AuthenticationType[];
|
|
30
30
|
}
|
|
31
31
|
/**
|
|
32
32
|
* Checks if biometric authentication is available on the device
|
|
33
33
|
*
|
|
34
|
-
* @returns Object containing availability status and biometry
|
|
34
|
+
* @returns Object containing availability status and biometry types
|
|
35
35
|
*/
|
|
36
36
|
export declare function isBiometricAvailable(): Promise<BiometricAvailability>;
|
|
37
37
|
/**
|
|
@@ -61,6 +61,6 @@ export declare function authenticateWithBiometrics(promptMessage?: string): Prom
|
|
|
61
61
|
* @param biometryType - The biometry type from the device
|
|
62
62
|
* @returns Human-readable string for the biometry type
|
|
63
63
|
*/
|
|
64
|
-
export declare function getBiometryTypeLabel(biometryType:
|
|
65
|
-
export {
|
|
64
|
+
export declare function getBiometryTypeLabel(biometryType: LocalAuthentication.AuthenticationType): string;
|
|
65
|
+
export { AuthenticationType } from 'expo-local-authentication';
|
|
66
66
|
//# sourceMappingURL=biometricUtils.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"biometricUtils.d.ts","sourceRoot":"","sources":["../src/biometricUtils.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,
|
|
1
|
+
{"version":3,"file":"biometricUtils.d.ts","sourceRoot":"","sources":["../src/biometricUtils.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,mBAAmB,MAAM,2BAA2B,CAAC;AAEjE;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,4CAA4C;IAC5C,OAAO,EAAE,OAAO,CAAC;IACjB,6CAA6C;IAC7C,KAAK,CAAC,EAAE;QACN,MAAM,EAAE,WAAW,GAAG,QAAQ,GAAG,eAAe,GAAG,cAAc,CAAC;QAClE,OAAO,CAAC,EAAE,MAAM,CAAC;KAClB,CAAC;CACH;AAED;;GAEG;AACH,MAAM,WAAW,qBAAqB;IACpC,wDAAwD;IACxD,SAAS,EAAE,OAAO,CAAC;IACnB,sCAAsC;IACtC,aAAa,EAAE,mBAAmB,CAAC,kBAAkB,EAAE,CAAC;CACzD;AAED;;;;GAIG;AACH,wBAAsB,oBAAoB,IAAI,OAAO,CAAC,qBAAqB,CAAC,CAiB3E;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAsB,0BAA0B,CAC9C,aAAa,GAAE,MAA4C,GAC1D,OAAO,CAAC,eAAe,CAAC,CA8E1B;AAED;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAClC,YAAY,EAAE,mBAAmB,CAAC,kBAAkB,GACnD,MAAM,CAWR;AAGD,OAAO,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC"}
|
package/dist/biometricUtils.js
CHANGED
|
@@ -3,9 +3,9 @@
|
|
|
3
3
|
* Biometric Authentication Utilities
|
|
4
4
|
*
|
|
5
5
|
* Provides cross-platform biometric authentication for card details access.
|
|
6
|
-
*
|
|
6
|
+
* Uses expo-local-authentication which works in Expo Go without native modules.
|
|
7
7
|
*
|
|
8
|
-
* @requires
|
|
8
|
+
* @requires expo-local-authentication - included in Expo SDK
|
|
9
9
|
*/
|
|
10
10
|
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
11
11
|
if (k2 === undefined) k2 = k;
|
|
@@ -41,34 +41,31 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
41
41
|
};
|
|
42
42
|
})();
|
|
43
43
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
44
|
-
exports.
|
|
44
|
+
exports.AuthenticationType = void 0;
|
|
45
45
|
exports.isBiometricAvailable = isBiometricAvailable;
|
|
46
46
|
exports.authenticateWithBiometrics = authenticateWithBiometrics;
|
|
47
47
|
exports.getBiometryTypeLabel = getBiometryTypeLabel;
|
|
48
|
-
const
|
|
49
|
-
Object.defineProperty(exports, "BiometryTypes", { enumerable: true, get: function () { return react_native_biometrics_1.BiometryTypes; } });
|
|
50
|
-
// Singleton instance of ReactNativeBiometrics
|
|
51
|
-
const rnBiometrics = new react_native_biometrics_1.default({
|
|
52
|
-
allowDeviceCredentials: false, // Only allow biometric, not PIN/password fallback
|
|
53
|
-
});
|
|
48
|
+
const LocalAuthentication = __importStar(require("expo-local-authentication"));
|
|
54
49
|
/**
|
|
55
50
|
* Checks if biometric authentication is available on the device
|
|
56
51
|
*
|
|
57
|
-
* @returns Object containing availability status and biometry
|
|
52
|
+
* @returns Object containing availability status and biometry types
|
|
58
53
|
*/
|
|
59
54
|
async function isBiometricAvailable() {
|
|
60
55
|
try {
|
|
61
|
-
const
|
|
56
|
+
const hasHardware = await LocalAuthentication.hasHardwareAsync();
|
|
57
|
+
const isEnrolled = await LocalAuthentication.isEnrolledAsync();
|
|
58
|
+
const supportedTypes = await LocalAuthentication.supportedAuthenticationTypesAsync();
|
|
62
59
|
return {
|
|
63
|
-
available,
|
|
64
|
-
|
|
60
|
+
available: hasHardware && isEnrolled,
|
|
61
|
+
biometryTypes: supportedTypes,
|
|
65
62
|
};
|
|
66
63
|
}
|
|
67
64
|
catch (error) {
|
|
68
65
|
console.error('[BiometricUtils] Error checking biometric availability:', error);
|
|
69
66
|
return {
|
|
70
67
|
available: false,
|
|
71
|
-
|
|
68
|
+
biometryTypes: [],
|
|
72
69
|
};
|
|
73
70
|
}
|
|
74
71
|
}
|
|
@@ -95,8 +92,8 @@ async function isBiometricAvailable() {
|
|
|
95
92
|
async function authenticateWithBiometrics(promptMessage = 'Authenticate to view card details') {
|
|
96
93
|
try {
|
|
97
94
|
// First check if biometrics are available
|
|
98
|
-
const
|
|
99
|
-
if (!
|
|
95
|
+
const hasHardware = await LocalAuthentication.hasHardwareAsync();
|
|
96
|
+
if (!hasHardware) {
|
|
100
97
|
console.log('[BiometricUtils] Biometrics not available on device');
|
|
101
98
|
return {
|
|
102
99
|
success: false,
|
|
@@ -107,42 +104,46 @@ async function authenticateWithBiometrics(promptMessage = 'Authenticate to view
|
|
|
107
104
|
};
|
|
108
105
|
}
|
|
109
106
|
// Check if biometrics are enrolled
|
|
110
|
-
const
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
107
|
+
const isEnrolled = await LocalAuthentication.isEnrolledAsync();
|
|
108
|
+
if (!isEnrolled) {
|
|
109
|
+
console.log('[BiometricUtils] No biometrics enrolled');
|
|
110
|
+
return {
|
|
111
|
+
success: false,
|
|
112
|
+
error: {
|
|
113
|
+
reason: 'not_enrolled',
|
|
114
|
+
message: 'No biometric authentication is set up on this device',
|
|
115
|
+
},
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
console.log('[BiometricUtils] Attempting biometric authentication...');
|
|
114
119
|
// Perform the biometric authentication
|
|
115
|
-
const
|
|
120
|
+
const result = await LocalAuthentication.authenticateAsync({
|
|
116
121
|
promptMessage,
|
|
117
|
-
|
|
122
|
+
cancelLabel: 'Cancel',
|
|
123
|
+
disableDeviceFallback: false, // Allow PIN/password fallback
|
|
124
|
+
fallbackLabel: 'Use Passcode',
|
|
118
125
|
});
|
|
119
|
-
if (success) {
|
|
126
|
+
if (result.success) {
|
|
120
127
|
console.log('[BiometricUtils] Biometric authentication successful');
|
|
121
128
|
return { success: true };
|
|
122
129
|
}
|
|
123
130
|
// Handle various failure reasons
|
|
124
|
-
console.log('[BiometricUtils] Biometric authentication failed:', error);
|
|
125
|
-
// Parse the error message to determine the reason
|
|
126
|
-
const errorMessage = error || 'Authentication failed';
|
|
131
|
+
console.log('[BiometricUtils] Biometric authentication failed:', result.error);
|
|
127
132
|
let reason = 'failed';
|
|
128
|
-
if (
|
|
129
|
-
errorMessage.toLowerCase().includes('user cancel')) {
|
|
133
|
+
if (result.error === 'user_cancel' || result.error === 'system_cancel') {
|
|
130
134
|
reason = 'cancelled';
|
|
131
135
|
}
|
|
132
|
-
else if (
|
|
133
|
-
errorMessage.toLowerCase().includes('no fingerprint') ||
|
|
134
|
-
errorMessage.toLowerCase().includes('no face')) {
|
|
136
|
+
else if (result.error === 'not_enrolled') {
|
|
135
137
|
reason = 'not_enrolled';
|
|
136
138
|
}
|
|
137
|
-
else if (
|
|
138
|
-
errorMessage.toLowerCase().includes('not supported')) {
|
|
139
|
+
else if (result.error === 'not_available') {
|
|
139
140
|
reason = 'not_available';
|
|
140
141
|
}
|
|
141
142
|
return {
|
|
142
143
|
success: false,
|
|
143
144
|
error: {
|
|
144
145
|
reason,
|
|
145
|
-
message:
|
|
146
|
+
message: result.warning || 'Authentication failed',
|
|
146
147
|
},
|
|
147
148
|
};
|
|
148
149
|
}
|
|
@@ -166,14 +167,17 @@ async function authenticateWithBiometrics(promptMessage = 'Authenticate to view
|
|
|
166
167
|
*/
|
|
167
168
|
function getBiometryTypeLabel(biometryType) {
|
|
168
169
|
switch (biometryType) {
|
|
169
|
-
case
|
|
170
|
+
case LocalAuthentication.AuthenticationType.FACIAL_RECOGNITION:
|
|
170
171
|
return 'Face ID';
|
|
171
|
-
case
|
|
172
|
+
case LocalAuthentication.AuthenticationType.FINGERPRINT:
|
|
172
173
|
return 'Touch ID';
|
|
173
|
-
case
|
|
174
|
-
return '
|
|
174
|
+
case LocalAuthentication.AuthenticationType.IRIS:
|
|
175
|
+
return 'Iris';
|
|
175
176
|
default:
|
|
176
177
|
return 'Biometric Authentication';
|
|
177
178
|
}
|
|
178
179
|
}
|
|
180
|
+
// Re-export AuthenticationType for convenience
|
|
181
|
+
var expo_local_authentication_1 = require("expo-local-authentication");
|
|
182
|
+
Object.defineProperty(exports, "AuthenticationType", { enumerable: true, get: function () { return expo_local_authentication_1.AuthenticationType; } });
|
|
179
183
|
//# sourceMappingURL=biometricUtils.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"biometricUtils.js","sourceRoot":"","sources":["../src/biometricUtils.ts"],"names":[],"mappings":";AAAA;;;;;;;GAOG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"biometricUtils.js","sourceRoot":"","sources":["../src/biometricUtils.ts"],"names":[],"mappings":"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"}
|
package/dist/cryptoUtils.d.ts
CHANGED
|
@@ -7,10 +7,8 @@
|
|
|
7
7
|
* - Input to RSA: secretKey as Base64 string
|
|
8
8
|
* - Output: encrypted sessionId as Base64 string
|
|
9
9
|
*
|
|
10
|
-
* Uses
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
* @requires react-native-quick-crypto - npm install react-native-quick-crypto
|
|
10
|
+
* Uses node-forge for pure JavaScript RSA-OAEP encryption.
|
|
11
|
+
* This works in Expo Go without native modules.
|
|
14
12
|
*/
|
|
15
13
|
/**
|
|
16
14
|
* Generates a random hex key matching Android's generateRandomHexKey()
|
|
@@ -30,10 +28,9 @@ export declare function generateRandomHexKey(): string;
|
|
|
30
28
|
*/
|
|
31
29
|
export declare function hexToBase64(hexString: string): string;
|
|
32
30
|
/**
|
|
33
|
-
* Encrypts data using RSA with OAEP-SHA1 padding
|
|
31
|
+
* Encrypts data using RSA with OAEP-SHA1 padding
|
|
34
32
|
*
|
|
35
33
|
* MUST match Android's RSA/ECB/OAEPWithSHA-1AndMGF1Padding algorithm
|
|
36
|
-
* Uses native crypto (OpenSSL on Android, CommonCrypto on iOS)
|
|
37
34
|
*
|
|
38
35
|
* @param data - Plain text data to encrypt (Base64 string of the secret key)
|
|
39
36
|
* @param publicKeyPem - RSA public key in PEM format
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cryptoUtils.d.ts","sourceRoot":"","sources":["../src/cryptoUtils.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"cryptoUtils.d.ts","sourceRoot":"","sources":["../src/cryptoUtils.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAIH;;;;;;;GAOG;AACH,wBAAgB,oBAAoB,IAAI,MAAM,CAU7C;AAED;;;;;;GAMG;AACH,wBAAgB,WAAW,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAUrD;AAED;;;;;;;;;GASG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,MAAM,CAmBzE;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,0BAA0B,CAAC,YAAY,EAAE,MAAM,GAAG,kBAAkB,CAqBnF;AAGD,MAAM,WAAW,kBAAkB;IACjC,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;CACnB"}
|
package/dist/cryptoUtils.js
CHANGED
|
@@ -8,10 +8,8 @@
|
|
|
8
8
|
* - Input to RSA: secretKey as Base64 string
|
|
9
9
|
* - Output: encrypted sessionId as Base64 string
|
|
10
10
|
*
|
|
11
|
-
* Uses
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
* @requires react-native-quick-crypto - npm install react-native-quick-crypto
|
|
11
|
+
* Uses node-forge for pure JavaScript RSA-OAEP encryption.
|
|
12
|
+
* This works in Expo Go without native modules.
|
|
15
13
|
*/
|
|
16
14
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
17
15
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
@@ -21,7 +19,7 @@ exports.generateRandomHexKey = generateRandomHexKey;
|
|
|
21
19
|
exports.hexToBase64 = hexToBase64;
|
|
22
20
|
exports.encryptWithRSA = encryptWithRSA;
|
|
23
21
|
exports.generateSessionCredentials = generateSessionCredentials;
|
|
24
|
-
const
|
|
22
|
+
const node_forge_1 = __importDefault(require("node-forge"));
|
|
25
23
|
/**
|
|
26
24
|
* Generates a random hex key matching Android's generateRandomHexKey()
|
|
27
25
|
*
|
|
@@ -55,32 +53,13 @@ function hexToBase64(hexString) {
|
|
|
55
53
|
const byte = parseInt(hexString.substring(i, i + 2), 16);
|
|
56
54
|
binaryString += String.fromCharCode(byte);
|
|
57
55
|
}
|
|
58
|
-
// Use
|
|
59
|
-
|
|
60
|
-
return btoa(binaryString);
|
|
61
|
-
}
|
|
62
|
-
// Fallback: manual Base64 encoding
|
|
63
|
-
const base64Chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
|
|
64
|
-
let result = '';
|
|
65
|
-
let i = 0;
|
|
66
|
-
while (i < binaryString.length) {
|
|
67
|
-
const a = binaryString.charCodeAt(i++);
|
|
68
|
-
const b = binaryString.charCodeAt(i++);
|
|
69
|
-
const c = binaryString.charCodeAt(i++);
|
|
70
|
-
const triplet = (a << 16) | ((b || 0) << 8) | (c || 0);
|
|
71
|
-
result +=
|
|
72
|
-
base64Chars[(triplet >> 18) & 0x3f] +
|
|
73
|
-
base64Chars[(triplet >> 12) & 0x3f] +
|
|
74
|
-
(isNaN(b) ? '=' : base64Chars[(triplet >> 6) & 0x3f]) +
|
|
75
|
-
(isNaN(c) ? '=' : base64Chars[triplet & 0x3f]);
|
|
76
|
-
}
|
|
77
|
-
return result;
|
|
56
|
+
// Use forge's utility for Base64 encoding
|
|
57
|
+
return node_forge_1.default.util.encode64(binaryString);
|
|
78
58
|
}
|
|
79
59
|
/**
|
|
80
|
-
* Encrypts data using RSA with OAEP-SHA1 padding
|
|
60
|
+
* Encrypts data using RSA with OAEP-SHA1 padding
|
|
81
61
|
*
|
|
82
62
|
* MUST match Android's RSA/ECB/OAEPWithSHA-1AndMGF1Padding algorithm
|
|
83
|
-
* Uses native crypto (OpenSSL on Android, CommonCrypto on iOS)
|
|
84
63
|
*
|
|
85
64
|
* @param data - Plain text data to encrypt (Base64 string of the secret key)
|
|
86
65
|
* @param publicKeyPem - RSA public key in PEM format
|
|
@@ -89,16 +68,17 @@ function hexToBase64(hexString) {
|
|
|
89
68
|
*/
|
|
90
69
|
function encryptWithRSA(data, publicKeyPem) {
|
|
91
70
|
try {
|
|
92
|
-
//
|
|
93
|
-
const
|
|
71
|
+
// Parse the PEM-formatted public key
|
|
72
|
+
const publicKey = node_forge_1.default.pki.publicKeyFromPem(publicKeyPem);
|
|
94
73
|
// Encrypt using RSA-OAEP with SHA-1 (matches Android's OAEPWithSHA-1AndMGF1Padding)
|
|
95
|
-
const encrypted =
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
74
|
+
const encrypted = publicKey.encrypt(data, 'RSA-OAEP', {
|
|
75
|
+
md: node_forge_1.default.md.sha1.create(),
|
|
76
|
+
mgf1: {
|
|
77
|
+
md: node_forge_1.default.md.sha1.create(),
|
|
78
|
+
},
|
|
79
|
+
});
|
|
80
|
+
// Encode to Base64
|
|
81
|
+
return node_forge_1.default.util.encode64(encrypted);
|
|
102
82
|
}
|
|
103
83
|
catch (error) {
|
|
104
84
|
console.error('[CryptoUtils] RSA encryption failed:', error);
|
package/dist/cryptoUtils.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cryptoUtils.js","sourceRoot":"","sources":["../src/cryptoUtils.ts"],"names":[],"mappings":";AAAA
|
|
1
|
+
{"version":3,"file":"cryptoUtils.js","sourceRoot":"","sources":["../src/cryptoUtils.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;GAWG;;;;;AAYH,oDAUC;AASD,kCAUC;AAYD,wCAmBC;AAoBD,gEAqBC;AA/GD,4DAA+B;AAE/B;;;;;;;GAOG;AACH,SAAgB,oBAAoB;IAClC,6CAA6C;IAC7C,iFAAiF;IACjF,OAAO,sCAAsC;SAC1C,OAAO,CAAC,OAAO,EAAE,CAAC,CAAC,EAAE,EAAE;QACtB,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,MAAM,EAAE,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC;QACnC,MAAM,CAAC,GAAG,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,GAAG,CAAC,GAAG,GAAG,CAAC;QAC1C,OAAO,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;IACxB,CAAC,CAAC;SACD,OAAO,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;AACvB,CAAC;AAED;;;;;;GAMG;AACH,SAAgB,WAAW,CAAC,SAAiB;IAC3C,sCAAsC;IACtC,IAAI,YAAY,GAAG,EAAE,CAAC;IACtB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,SAAS,CAAC,MAAM,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC;QAC7C,MAAM,IAAI,GAAG,QAAQ,CAAC,SAAS,CAAC,SAAS,CAAC,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;QACzD,YAAY,IAAI,MAAM,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC;IAC5C,CAAC;IAED,0CAA0C;IAC1C,OAAO,oBAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,YAAY,CAAC,CAAC;AAC3C,CAAC;AAED;;;;;;;;;GASG;AACH,SAAgB,cAAc,CAAC,IAAY,EAAE,YAAoB;IAC/D,IAAI,CAAC;QACH,qCAAqC;QACrC,MAAM,SAAS,GAAG,oBAAK,CAAC,GAAG,CAAC,gBAAgB,CAAC,YAAY,CAAC,CAAC;QAE3D,oFAAoF;QACpF,MAAM,SAAS,GAAG,SAAS,CAAC,OAAO,CAAC,IAAI,EAAE,UAAU,EAAE;YACpD,EAAE,EAAE,oBAAK,CAAC,EAAE,CAAC,IAAI,CAAC,MAAM,EAAE;YAC1B,IAAI,EAAE;gBACJ,EAAE,EAAE,oBAAK,CAAC,EAAE,CAAC,IAAI,CAAC,MAAM,EAAE;aAC3B;SACF,CAAC,CAAC;QAEH,mBAAmB;QACnB,OAAO,oBAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC;IACxC,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,CAAC,KAAK,CAAC,sCAAsC,EAAE,KAAK,CAAC,CAAC;QAC7D,MAAM,IAAI,KAAK,CAAC,gCAAgC,CAAC,CAAC;IACpD,CAAC;AACH,CAAC;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,SAAgB,0BAA0B,CAAC,YAAoB;IAC7D,sBAAsB;IACtB,IAAI,CAAC,YAAY,IAAI,CAAC,YAAY,CAAC,QAAQ,CAAC,kBAAkB,CAAC,EAAE,CAAC;QAChE,MAAM,IAAI,KAAK,CAAC,+BAA+B,CAAC,CAAC;IACnD,CAAC;IAED,8EAA8E;IAC9E,MAAM,YAAY,GAAG,oBAAoB,EAAE,CAAC;IAE5C,4DAA4D;IAC5D,MAAM,eAAe,GAAG,WAAW,CAAC,YAAY,CAAC,CAAC;IAElD,+EAA+E;IAC/E,MAAM,SAAS,GAAG,cAAc,CAAC,eAAe,EAAE,YAAY,CAAC,CAAC;IAEhE,OAAO,CAAC,GAAG,CAAC,0DAA0D,CAAC,CAAC;IAExE,OAAO;QACL,YAAY;QACZ,SAAS;KACV,CAAC;AACJ,CAAC"}
|
package/dist/index.d.ts
CHANGED
|
@@ -8,7 +8,7 @@ export type { WidgetSDKConfig, WidgetEnvironment, DepositToken, WidgetMessageTyp
|
|
|
8
8
|
export { WALLET_URLS, MessageTypes } from './types';
|
|
9
9
|
export { WIDGET_URLS, getWidgetUrl } from './config';
|
|
10
10
|
export { openNativeWallet, isWalletAvailable } from './walletUtils';
|
|
11
|
-
export { authenticateWithBiometrics, isBiometricAvailable, getBiometryTypeLabel,
|
|
11
|
+
export { authenticateWithBiometrics, isBiometricAvailable, getBiometryTypeLabel, AuthenticationType, } from './biometricUtils';
|
|
12
12
|
export type { BiometricResult, BiometricAvailability } from './biometricUtils';
|
|
13
13
|
export { generateSessionCredentials, generateRandomHexKey, hexToBase64, } from './cryptoUtils';
|
|
14
14
|
export type { SessionCredentials } from './cryptoUtils';
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxC,YAAY,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAGhD,YAAY,EAEV,eAAe,EACf,iBAAiB,EACjB,YAAY,EAGZ,iBAAiB,EACjB,iBAAiB,EACjB,aAAa,EACb,kBAAkB,EAClB,iBAAiB,EACjB,kBAAkB,EAClB,kBAAkB,EAClB,kBAAkB,EAClB,2BAA2B,EAC3B,gCAAgC,EAGhC,iBAAiB,EACjB,aAAa,EACb,wBAAwB,EACxB,yBAAyB,EACzB,yBAAyB,EACzB,yBAAyB,EACzB,+BAA+B,EAC/B,4BAA4B,GAC7B,MAAM,SAAS,CAAC;AAGjB,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACpD,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AAGrD,OAAO,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAGpE,OAAO,EACL,0BAA0B,EAC1B,oBAAoB,EACpB,oBAAoB,EACpB,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxC,YAAY,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAGhD,YAAY,EAEV,eAAe,EACf,iBAAiB,EACjB,YAAY,EAGZ,iBAAiB,EACjB,iBAAiB,EACjB,aAAa,EACb,kBAAkB,EAClB,iBAAiB,EACjB,kBAAkB,EAClB,kBAAkB,EAClB,kBAAkB,EAClB,2BAA2B,EAC3B,gCAAgC,EAGhC,iBAAiB,EACjB,aAAa,EACb,wBAAwB,EACxB,yBAAyB,EACzB,yBAAyB,EACzB,yBAAyB,EACzB,+BAA+B,EAC/B,4BAA4B,GAC7B,MAAM,SAAS,CAAC;AAGjB,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACpD,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AAGrD,OAAO,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAGpE,OAAO,EACL,0BAA0B,EAC1B,oBAAoB,EACpB,oBAAoB,EACpB,kBAAkB,GACnB,MAAM,kBAAkB,CAAC;AAC1B,YAAY,EAAE,eAAe,EAAE,qBAAqB,EAAE,MAAM,kBAAkB,CAAC;AAG/E,OAAO,EACL,0BAA0B,EAC1B,oBAAoB,EACpB,WAAW,GACZ,MAAM,eAAe,CAAC;AACvB,YAAY,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* Export all public components, types, and utilities
|
|
5
5
|
*/
|
|
6
6
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
-
exports.hexToBase64 = exports.generateRandomHexKey = exports.generateSessionCredentials = exports.
|
|
7
|
+
exports.hexToBase64 = exports.generateRandomHexKey = exports.generateSessionCredentials = exports.AuthenticationType = exports.getBiometryTypeLabel = exports.isBiometricAvailable = exports.authenticateWithBiometrics = exports.isWalletAvailable = exports.openNativeWallet = exports.getWidgetUrl = exports.WIDGET_URLS = exports.MessageTypes = exports.WALLET_URLS = exports.WidgetSDK = void 0;
|
|
8
8
|
var WidgetSDK_1 = require("./WidgetSDK");
|
|
9
9
|
Object.defineProperty(exports, "WidgetSDK", { enumerable: true, get: function () { return WidgetSDK_1.WidgetSDK; } });
|
|
10
10
|
// Export constants
|
|
@@ -23,7 +23,7 @@ var biometricUtils_1 = require("./biometricUtils");
|
|
|
23
23
|
Object.defineProperty(exports, "authenticateWithBiometrics", { enumerable: true, get: function () { return biometricUtils_1.authenticateWithBiometrics; } });
|
|
24
24
|
Object.defineProperty(exports, "isBiometricAvailable", { enumerable: true, get: function () { return biometricUtils_1.isBiometricAvailable; } });
|
|
25
25
|
Object.defineProperty(exports, "getBiometryTypeLabel", { enumerable: true, get: function () { return biometricUtils_1.getBiometryTypeLabel; } });
|
|
26
|
-
Object.defineProperty(exports, "
|
|
26
|
+
Object.defineProperty(exports, "AuthenticationType", { enumerable: true, get: function () { return biometricUtils_1.AuthenticationType; } });
|
|
27
27
|
// Export crypto utilities (for advanced usage)
|
|
28
28
|
var cryptoUtils_1 = require("./cryptoUtils");
|
|
29
29
|
Object.defineProperty(exports, "generateSessionCredentials", { enumerable: true, get: function () { return cryptoUtils_1.generateSessionCredentials; } });
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAAA;;;GAGG;;;AAEH,yCAAwC;AAA/B,sGAAA,SAAS,OAAA;AAiClB,mBAAmB;AACnB,iCAAoD;AAA3C,oGAAA,WAAW,OAAA;AAAE,qGAAA,YAAY,OAAA;AAClC,mCAAqD;AAA5C,qGAAA,WAAW,OAAA;AAAE,sGAAA,YAAY,OAAA;AAElC,0BAA0B;AAC1B,6CAAoE;AAA3D,+GAAA,gBAAgB,OAAA;AAAE,gHAAA,iBAAiB,OAAA;AAE5C,6BAA6B;AAC7B,mDAK0B;AAJxB,4HAAA,0BAA0B,OAAA;AAC1B,sHAAA,oBAAoB,OAAA;AACpB,sHAAA,oBAAoB,OAAA;AACpB
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAAA;;;GAGG;;;AAEH,yCAAwC;AAA/B,sGAAA,SAAS,OAAA;AAiClB,mBAAmB;AACnB,iCAAoD;AAA3C,oGAAA,WAAW,OAAA;AAAE,qGAAA,YAAY,OAAA;AAClC,mCAAqD;AAA5C,qGAAA,WAAW,OAAA;AAAE,sGAAA,YAAY,OAAA;AAElC,0BAA0B;AAC1B,6CAAoE;AAA3D,+GAAA,gBAAgB,OAAA;AAAE,gHAAA,iBAAiB,OAAA;AAE5C,6BAA6B;AAC7B,mDAK0B;AAJxB,4HAAA,0BAA0B,OAAA;AAC1B,sHAAA,oBAAoB,OAAA;AACpB,sHAAA,oBAAoB,OAAA;AACpB,oHAAA,kBAAkB,OAAA;AAIpB,+CAA+C;AAC/C,6CAIuB;AAHrB,yHAAA,0BAA0B,OAAA;AAC1B,mHAAA,oBAAoB,OAAA;AACpB,0GAAA,WAAW,OAAA"}
|
package/dist/types.d.ts
CHANGED
|
@@ -12,12 +12,12 @@ export interface DepositToken {
|
|
|
12
12
|
iconUrl: string | null;
|
|
13
13
|
networkName: string;
|
|
14
14
|
}
|
|
15
|
-
export type WidgetMessageType = "widget:ready" | "widget:open-wallet" | "widget:card-created" | "widget:error" | "widget:close" | "widget:transaction-requested" | "widget:token-expired" | "widget:request-card-details-session";
|
|
15
|
+
export type WidgetMessageType = "widget:ready" | "widget:open-wallet" | "widget:card-created" | "widget:error" | "widget:close" | "widget:transaction-requested" | "widget:token-expired" | "widget:request-card-details-session" | "widget:copy-to-clipboard";
|
|
16
16
|
/**
|
|
17
17
|
* Native Message Types
|
|
18
18
|
* Messages sent from the native app to the web widget
|
|
19
19
|
*/
|
|
20
|
-
export type NativeMessageType = "native:platform-info" | "native:wallet-opened" | "native:back-pressed" | "native:navigate-back" | "native:card-details-session" | "native:biometric-failed";
|
|
20
|
+
export type NativeMessageType = "native:platform-info" | "native:wallet-opened" | "native:back-pressed" | "native:navigate-back" | "native:card-details-session" | "native:biometric-failed" | "native:clipboard-result";
|
|
21
21
|
export interface NativeBackPressedMessage {
|
|
22
22
|
type: "native:back-pressed";
|
|
23
23
|
timestamp: number;
|
|
@@ -53,7 +53,15 @@ export interface NativeBiometricFailedMessage {
|
|
|
53
53
|
message?: string;
|
|
54
54
|
};
|
|
55
55
|
}
|
|
56
|
-
export
|
|
56
|
+
export interface NativeClipboardResultMessage {
|
|
57
|
+
type: "native:clipboard-result";
|
|
58
|
+
payload: {
|
|
59
|
+
success: boolean;
|
|
60
|
+
label?: string;
|
|
61
|
+
error?: string;
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
export type NativeMessage = NativeBackPressedMessage | NativeNavigateBackMessage | NativePlatformInfoMessage | NativeWalletOpenedMessage | NativeCardDetailsSessionMessage | NativeBiometricFailedMessage | NativeClipboardResultMessage;
|
|
57
65
|
export interface BaseWidgetMessage {
|
|
58
66
|
type: WidgetMessageType;
|
|
59
67
|
timestamp: number;
|
|
@@ -106,7 +114,14 @@ export interface RequestCardDetailsSessionMessage extends BaseWidgetMessage {
|
|
|
106
114
|
publicKey: string;
|
|
107
115
|
};
|
|
108
116
|
}
|
|
109
|
-
export
|
|
117
|
+
export interface CopyToClipboardMessage extends BaseWidgetMessage {
|
|
118
|
+
type: "widget:copy-to-clipboard";
|
|
119
|
+
payload: {
|
|
120
|
+
text: string;
|
|
121
|
+
label?: string;
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
export type WidgetMessage = WidgetReadyMessage | OpenWalletMessage | CardCreatedMessage | WidgetErrorMessage | WidgetCloseMessage | TransactionRequestedMessage | TokenExpiredMessage | RequestCardDetailsSessionMessage | CopyToClipboardMessage;
|
|
110
125
|
/**
|
|
111
126
|
* Widget environment configuration
|
|
112
127
|
* - 'development': Uses development/staging widget URL
|
|
@@ -161,11 +176,13 @@ export declare const MessageTypes: {
|
|
|
161
176
|
readonly TRANSACTION_REQUESTED: "widget:transaction-requested";
|
|
162
177
|
readonly TOKEN_EXPIRED: "widget:token-expired";
|
|
163
178
|
readonly REQUEST_CARD_DETAILS_SESSION: "widget:request-card-details-session";
|
|
179
|
+
readonly COPY_TO_CLIPBOARD: "widget:copy-to-clipboard";
|
|
164
180
|
readonly PLATFORM_INFO: "native:platform-info";
|
|
165
181
|
readonly WALLET_OPENED: "native:wallet-opened";
|
|
166
182
|
readonly BACK_PRESSED: "native:back-pressed";
|
|
167
183
|
readonly NAVIGATE_BACK: "native:navigate-back";
|
|
168
184
|
readonly CARD_DETAILS_SESSION: "native:card-details-session";
|
|
169
185
|
readonly BIOMETRIC_FAILED: "native:biometric-failed";
|
|
186
|
+
readonly CLIPBOARD_RESULT: "native:clipboard-result";
|
|
170
187
|
};
|
|
171
188
|
//# sourceMappingURL=types.d.ts.map
|
package/dist/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH;;;GAGG;AACH,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,MAAM,iBAAiB,GACzB,cAAc,GACd,oBAAoB,GACpB,qBAAqB,GACrB,cAAc,GACd,cAAc,GACd,8BAA8B,GAC9B,sBAAsB,GACtB,qCAAqC,CAAC;
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|
package/dist/types.js
CHANGED
|
@@ -35,6 +35,7 @@ exports.MessageTypes = {
|
|
|
35
35
|
TRANSACTION_REQUESTED: "widget:transaction-requested",
|
|
36
36
|
TOKEN_EXPIRED: "widget:token-expired",
|
|
37
37
|
REQUEST_CARD_DETAILS_SESSION: "widget:request-card-details-session",
|
|
38
|
+
COPY_TO_CLIPBOARD: "widget:copy-to-clipboard",
|
|
38
39
|
// Native → Widget
|
|
39
40
|
PLATFORM_INFO: "native:platform-info",
|
|
40
41
|
WALLET_OPENED: "native:wallet-opened",
|
|
@@ -42,5 +43,6 @@ exports.MessageTypes = {
|
|
|
42
43
|
NAVIGATE_BACK: "native:navigate-back",
|
|
43
44
|
CARD_DETAILS_SESSION: "native:card-details-session",
|
|
44
45
|
BIOMETRIC_FAILED: "native:biometric-failed",
|
|
46
|
+
CLIPBOARD_RESULT: "native:clipboard-result",
|
|
45
47
|
};
|
|
46
48
|
//# sourceMappingURL=types.js.map
|
package/dist/types.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":";AAAA;;;GAGG;;;
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":";AAAA;;;GAGG;;;AAgNH;;GAEG;AACU,QAAA,WAAW,GAAG;IACzB,GAAG,EAAE;QACH,OAAO,EAAE,YAAY,EAAE,yBAAyB;QAChD,QAAQ,EAAE,0BAA0B;KACrC;IACD,OAAO,EAAE;QACP,SAAS,EAAE,yCAAyC;QACpD,QAAQ,EAAE,2BAA2B;KACtC;CACO,CAAC;AAEX;;;;;;GAMG;AACU,QAAA,YAAY,GAAG;IAC1B,kBAAkB;IAClB,KAAK,EAAE,cAAc;IACrB,WAAW,EAAE,oBAAoB;IACjC,YAAY,EAAE,qBAAqB;IACnC,KAAK,EAAE,cAAc;IACrB,KAAK,EAAE,cAAc;IACrB,qBAAqB,EAAE,8BAA8B;IACrD,aAAa,EAAE,sBAAsB;IACrC,4BAA4B,EAAE,qCAAqC;IACnE,iBAAiB,EAAE,0BAA0B;IAC7C,kBAAkB;IAClB,aAAa,EAAE,sBAAsB;IACrC,aAAa,EAAE,sBAAsB;IACrC,YAAY,EAAE,qBAAqB;IACnC,aAAa,EAAE,sBAAsB;IACrC,oBAAoB,EAAE,6BAA6B;IACnD,gBAAgB,EAAE,yBAAyB;IAC3C,gBAAgB,EAAE,yBAAyB;CACnC,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@oobit/react-native-sdk",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.7",
|
|
4
4
|
"description": "React Native SDK for integrating Oobit crypto payments into wallet apps",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"module": "dist/index.js",
|
|
@@ -39,25 +39,29 @@
|
|
|
39
39
|
"url": "git+https://github.com/oobit-tech/react-native-SDK.git"
|
|
40
40
|
},
|
|
41
41
|
"peerDependencies": {
|
|
42
|
+
"expo-clipboard": ">=5.0.0",
|
|
42
43
|
"expo-intent-launcher": ">=6.0.0",
|
|
43
44
|
"expo-linking": ">=6.0.0",
|
|
45
|
+
"expo-local-authentication": ">=14.0.0",
|
|
44
46
|
"react": ">=18.0.0",
|
|
45
47
|
"react-native": ">=0.70.0",
|
|
46
|
-
"react-native-webview": ">=13.0.0"
|
|
47
|
-
|
|
48
|
-
|
|
48
|
+
"react-native-webview": ">=13.0.0"
|
|
49
|
+
},
|
|
50
|
+
"dependencies": {
|
|
51
|
+
"node-forge": "^1.3.1"
|
|
49
52
|
},
|
|
50
53
|
"devDependencies": {
|
|
54
|
+
"@types/node-forge": "^1.3.11",
|
|
51
55
|
"@types/react": "~19.1.0",
|
|
52
56
|
"@typescript-eslint/eslint-plugin": "^8.47.0",
|
|
53
57
|
"@typescript-eslint/parser": "^8.47.0",
|
|
54
58
|
"eslint": "^9.39.1",
|
|
59
|
+
"expo-clipboard": "~8.0.8",
|
|
55
60
|
"expo-intent-launcher": "~13.0.7",
|
|
56
61
|
"expo-linking": "~8.0.8",
|
|
62
|
+
"expo-local-authentication": "~15.0.2",
|
|
57
63
|
"react": "19.1.0",
|
|
58
64
|
"react-native": "0.81.5",
|
|
59
|
-
"react-native-biometrics": "^3.0.1",
|
|
60
|
-
"react-native-quick-crypto": "^0.7.9",
|
|
61
65
|
"react-native-webview": "13.15.0",
|
|
62
66
|
"typescript": "~5.9.2"
|
|
63
67
|
},
|
package/src/WidgetSDK.tsx
CHANGED
|
@@ -20,16 +20,10 @@ import {
|
|
|
20
20
|
View,
|
|
21
21
|
} from "react-native";
|
|
22
22
|
import { WebView, WebViewMessageEvent } from "react-native-webview";
|
|
23
|
-
import { authenticateWithBiometrics } from "./biometricUtils";
|
|
24
23
|
import { getWidgetUrl } from "./config";
|
|
25
|
-
import {
|
|
26
|
-
import {
|
|
27
|
-
NativeBiometricFailedMessage,
|
|
28
|
-
NativeCardDetailsSessionMessage,
|
|
29
|
-
WidgetMessage,
|
|
30
|
-
WidgetSDKConfig,
|
|
31
|
-
} from "./types";
|
|
24
|
+
import { CopyToClipboardMessage, WidgetMessage, WidgetSDKConfig } from "./types";
|
|
32
25
|
import { isWalletAvailable, openNativeWallet } from "./walletUtils";
|
|
26
|
+
import * as Clipboard from "expo-clipboard";
|
|
33
27
|
|
|
34
28
|
export interface WidgetSDKRef {
|
|
35
29
|
navigateBack: () => void;
|
|
@@ -131,8 +125,8 @@ export const WidgetSDK = forwardRef<WidgetSDKRef, WidgetSDKConfig>(
|
|
|
131
125
|
onError?.("TOKEN_EXPIRED", "Access token has expired");
|
|
132
126
|
break;
|
|
133
127
|
|
|
134
|
-
case "widget:
|
|
135
|
-
|
|
128
|
+
case "widget:copy-to-clipboard":
|
|
129
|
+
handleCopyToClipboard(message as CopyToClipboardMessage);
|
|
136
130
|
break;
|
|
137
131
|
|
|
138
132
|
default:
|
|
@@ -214,79 +208,30 @@ export const WidgetSDK = forwardRef<WidgetSDKRef, WidgetSDKConfig>(
|
|
|
214
208
|
};
|
|
215
209
|
|
|
216
210
|
/**
|
|
217
|
-
* Handle
|
|
218
|
-
*
|
|
219
|
-
* 2. Generate cryptographically linked sessionId + secretKey pair
|
|
220
|
-
* 3. Send credentials back to widget for API call and decryption
|
|
211
|
+
* Handle clipboard copy request from widget
|
|
212
|
+
* Copies text to device clipboard and sends result back to widget
|
|
221
213
|
*/
|
|
222
|
-
const
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
console.log(
|
|
227
|
-
"[WidgetSDK] Card details requested, initiating biometric auth..."
|
|
228
|
-
);
|
|
229
|
-
|
|
230
|
-
// Step 1: Authenticate with biometrics
|
|
231
|
-
const biometricResult = await authenticateWithBiometrics(
|
|
232
|
-
"Authenticate to view card details"
|
|
233
|
-
);
|
|
234
|
-
|
|
235
|
-
if (!biometricResult.success) {
|
|
236
|
-
console.log(
|
|
237
|
-
"[WidgetSDK] Biometric auth failed:",
|
|
238
|
-
biometricResult.error
|
|
239
|
-
);
|
|
214
|
+
const handleCopyToClipboard = async (message: CopyToClipboardMessage) => {
|
|
215
|
+
const { text, label } = message.payload;
|
|
216
|
+
// Log only the label, never the actual text content (security)
|
|
217
|
+
console.log("[WidgetSDK] Copy to clipboard requested:", label || "unlabeled");
|
|
240
218
|
|
|
241
|
-
const failedMessage: NativeBiometricFailedMessage = {
|
|
242
|
-
type: "native:biometric-failed",
|
|
243
|
-
payload: {
|
|
244
|
-
reason: biometricResult.error?.reason || "failed",
|
|
245
|
-
message: biometricResult.error?.message,
|
|
246
|
-
},
|
|
247
|
-
};
|
|
248
|
-
|
|
249
|
-
sendMessageToWidget(failedMessage);
|
|
250
|
-
return;
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
// Step 2: Generate session credentials
|
|
254
219
|
try {
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
publicKey
|
|
261
|
-
);
|
|
262
|
-
|
|
263
|
-
console.log("[WidgetSDK] Session credentials generated successfully");
|
|
264
|
-
|
|
265
|
-
// Step 3: Send credentials to widget
|
|
266
|
-
const sessionMessage: NativeCardDetailsSessionMessage = {
|
|
267
|
-
type: "native:card-details-session",
|
|
268
|
-
payload: {
|
|
269
|
-
sessionId,
|
|
270
|
-
secretKey: secretKeyHex,
|
|
271
|
-
},
|
|
272
|
-
};
|
|
273
|
-
|
|
274
|
-
sendMessageToWidget(sessionMessage);
|
|
220
|
+
await Clipboard.setStringAsync(text);
|
|
221
|
+
sendMessageToWidget({
|
|
222
|
+
type: "native:clipboard-result",
|
|
223
|
+
payload: { success: true, label },
|
|
224
|
+
});
|
|
275
225
|
} catch (error) {
|
|
276
|
-
console.error(
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
);
|
|
280
|
-
|
|
281
|
-
const failedMessage: NativeBiometricFailedMessage = {
|
|
282
|
-
type: "native:biometric-failed",
|
|
226
|
+
console.error("[WidgetSDK] Clipboard copy failed:", error);
|
|
227
|
+
sendMessageToWidget({
|
|
228
|
+
type: "native:clipboard-result",
|
|
283
229
|
payload: {
|
|
284
|
-
|
|
285
|
-
|
|
230
|
+
success: false,
|
|
231
|
+
label,
|
|
232
|
+
error: "Clipboard operation failed",
|
|
286
233
|
},
|
|
287
|
-
};
|
|
288
|
-
|
|
289
|
-
sendMessageToWidget(failedMessage);
|
|
234
|
+
});
|
|
290
235
|
}
|
|
291
236
|
};
|
|
292
237
|
|
package/src/biometricUtils.ts
CHANGED
|
@@ -2,12 +2,12 @@
|
|
|
2
2
|
* Biometric Authentication Utilities
|
|
3
3
|
*
|
|
4
4
|
* Provides cross-platform biometric authentication for card details access.
|
|
5
|
-
*
|
|
5
|
+
* Uses expo-local-authentication which works in Expo Go without native modules.
|
|
6
6
|
*
|
|
7
|
-
* @requires
|
|
7
|
+
* @requires expo-local-authentication - included in Expo SDK
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
|
-
import
|
|
10
|
+
import * as LocalAuthentication from 'expo-local-authentication';
|
|
11
11
|
|
|
12
12
|
/**
|
|
13
13
|
* Result of a biometric authentication attempt
|
|
@@ -28,33 +28,30 @@ export interface BiometricResult {
|
|
|
28
28
|
export interface BiometricAvailability {
|
|
29
29
|
/** Whether any biometric authentication is available */
|
|
30
30
|
available: boolean;
|
|
31
|
-
/** The
|
|
32
|
-
|
|
31
|
+
/** The types of biometry available */
|
|
32
|
+
biometryTypes: LocalAuthentication.AuthenticationType[];
|
|
33
33
|
}
|
|
34
34
|
|
|
35
|
-
// Singleton instance of ReactNativeBiometrics
|
|
36
|
-
const rnBiometrics = new ReactNativeBiometrics({
|
|
37
|
-
allowDeviceCredentials: false, // Only allow biometric, not PIN/password fallback
|
|
38
|
-
});
|
|
39
|
-
|
|
40
35
|
/**
|
|
41
36
|
* Checks if biometric authentication is available on the device
|
|
42
37
|
*
|
|
43
|
-
* @returns Object containing availability status and biometry
|
|
38
|
+
* @returns Object containing availability status and biometry types
|
|
44
39
|
*/
|
|
45
40
|
export async function isBiometricAvailable(): Promise<BiometricAvailability> {
|
|
46
41
|
try {
|
|
47
|
-
const
|
|
42
|
+
const hasHardware = await LocalAuthentication.hasHardwareAsync();
|
|
43
|
+
const isEnrolled = await LocalAuthentication.isEnrolledAsync();
|
|
44
|
+
const supportedTypes = await LocalAuthentication.supportedAuthenticationTypesAsync();
|
|
48
45
|
|
|
49
46
|
return {
|
|
50
|
-
available,
|
|
51
|
-
|
|
47
|
+
available: hasHardware && isEnrolled,
|
|
48
|
+
biometryTypes: supportedTypes,
|
|
52
49
|
};
|
|
53
50
|
} catch (error) {
|
|
54
51
|
console.error('[BiometricUtils] Error checking biometric availability:', error);
|
|
55
52
|
return {
|
|
56
53
|
available: false,
|
|
57
|
-
|
|
54
|
+
biometryTypes: [],
|
|
58
55
|
};
|
|
59
56
|
}
|
|
60
57
|
}
|
|
@@ -84,9 +81,9 @@ export async function authenticateWithBiometrics(
|
|
|
84
81
|
): Promise<BiometricResult> {
|
|
85
82
|
try {
|
|
86
83
|
// First check if biometrics are available
|
|
87
|
-
const
|
|
84
|
+
const hasHardware = await LocalAuthentication.hasHardwareAsync();
|
|
88
85
|
|
|
89
|
-
if (!
|
|
86
|
+
if (!hasHardware) {
|
|
90
87
|
console.log('[BiometricUtils] Biometrics not available on device');
|
|
91
88
|
return {
|
|
92
89
|
success: false,
|
|
@@ -98,48 +95,44 @@ export async function authenticateWithBiometrics(
|
|
|
98
95
|
}
|
|
99
96
|
|
|
100
97
|
// Check if biometrics are enrolled
|
|
101
|
-
const
|
|
98
|
+
const isEnrolled = await LocalAuthentication.isEnrolledAsync();
|
|
102
99
|
|
|
103
|
-
|
|
104
|
-
|
|
100
|
+
if (!isEnrolled) {
|
|
101
|
+
console.log('[BiometricUtils] No biometrics enrolled');
|
|
102
|
+
return {
|
|
103
|
+
success: false,
|
|
104
|
+
error: {
|
|
105
|
+
reason: 'not_enrolled',
|
|
106
|
+
message: 'No biometric authentication is set up on this device',
|
|
107
|
+
},
|
|
108
|
+
};
|
|
109
|
+
}
|
|
105
110
|
|
|
106
|
-
console.log(
|
|
107
|
-
`[BiometricUtils] Attempting biometric auth. Type: ${biometryType}, Keys exist: ${keysExist}`
|
|
108
|
-
);
|
|
111
|
+
console.log('[BiometricUtils] Attempting biometric authentication...');
|
|
109
112
|
|
|
110
113
|
// Perform the biometric authentication
|
|
111
|
-
const
|
|
114
|
+
const result = await LocalAuthentication.authenticateAsync({
|
|
112
115
|
promptMessage,
|
|
113
|
-
|
|
116
|
+
cancelLabel: 'Cancel',
|
|
117
|
+
disableDeviceFallback: false, // Allow PIN/password fallback
|
|
118
|
+
fallbackLabel: 'Use Passcode',
|
|
114
119
|
});
|
|
115
120
|
|
|
116
|
-
if (success) {
|
|
121
|
+
if (result.success) {
|
|
117
122
|
console.log('[BiometricUtils] Biometric authentication successful');
|
|
118
123
|
return { success: true };
|
|
119
124
|
}
|
|
120
125
|
|
|
121
126
|
// Handle various failure reasons
|
|
122
|
-
console.log('[BiometricUtils] Biometric authentication failed:', error);
|
|
127
|
+
console.log('[BiometricUtils] Biometric authentication failed:', result.error);
|
|
123
128
|
|
|
124
|
-
|
|
125
|
-
const errorMessage = error || 'Authentication failed';
|
|
126
|
-
let reason: NonNullable<BiometricResult['error']>['reason'] = 'failed';
|
|
129
|
+
let reason: 'cancelled' | 'failed' | 'not_available' | 'not_enrolled' = 'failed';
|
|
127
130
|
|
|
128
|
-
if (
|
|
129
|
-
errorMessage.toLowerCase().includes('cancel') ||
|
|
130
|
-
errorMessage.toLowerCase().includes('user cancel')
|
|
131
|
-
) {
|
|
131
|
+
if (result.error === 'user_cancel' || result.error === 'system_cancel') {
|
|
132
132
|
reason = 'cancelled';
|
|
133
|
-
} else if (
|
|
134
|
-
errorMessage.toLowerCase().includes('not enrolled') ||
|
|
135
|
-
errorMessage.toLowerCase().includes('no fingerprint') ||
|
|
136
|
-
errorMessage.toLowerCase().includes('no face')
|
|
137
|
-
) {
|
|
133
|
+
} else if (result.error === 'not_enrolled') {
|
|
138
134
|
reason = 'not_enrolled';
|
|
139
|
-
} else if (
|
|
140
|
-
errorMessage.toLowerCase().includes('not available') ||
|
|
141
|
-
errorMessage.toLowerCase().includes('not supported')
|
|
142
|
-
) {
|
|
135
|
+
} else if (result.error === 'not_available') {
|
|
143
136
|
reason = 'not_available';
|
|
144
137
|
}
|
|
145
138
|
|
|
@@ -147,14 +140,13 @@ export async function authenticateWithBiometrics(
|
|
|
147
140
|
success: false,
|
|
148
141
|
error: {
|
|
149
142
|
reason,
|
|
150
|
-
message:
|
|
143
|
+
message: result.warning || 'Authentication failed',
|
|
151
144
|
},
|
|
152
145
|
};
|
|
153
146
|
} catch (error) {
|
|
154
147
|
console.error('[BiometricUtils] Biometric authentication error:', error);
|
|
155
148
|
|
|
156
|
-
const errorMessage =
|
|
157
|
-
error instanceof Error ? error.message : 'Unknown error occurred';
|
|
149
|
+
const errorMessage = error instanceof Error ? error.message : 'Unknown error occurred';
|
|
158
150
|
|
|
159
151
|
return {
|
|
160
152
|
success: false,
|
|
@@ -172,18 +164,20 @@ export async function authenticateWithBiometrics(
|
|
|
172
164
|
* @param biometryType - The biometry type from the device
|
|
173
165
|
* @returns Human-readable string for the biometry type
|
|
174
166
|
*/
|
|
175
|
-
export function getBiometryTypeLabel(
|
|
167
|
+
export function getBiometryTypeLabel(
|
|
168
|
+
biometryType: LocalAuthentication.AuthenticationType
|
|
169
|
+
): string {
|
|
176
170
|
switch (biometryType) {
|
|
177
|
-
case
|
|
171
|
+
case LocalAuthentication.AuthenticationType.FACIAL_RECOGNITION:
|
|
178
172
|
return 'Face ID';
|
|
179
|
-
case
|
|
173
|
+
case LocalAuthentication.AuthenticationType.FINGERPRINT:
|
|
180
174
|
return 'Touch ID';
|
|
181
|
-
case
|
|
182
|
-
return '
|
|
175
|
+
case LocalAuthentication.AuthenticationType.IRIS:
|
|
176
|
+
return 'Iris';
|
|
183
177
|
default:
|
|
184
178
|
return 'Biometric Authentication';
|
|
185
179
|
}
|
|
186
180
|
}
|
|
187
181
|
|
|
188
|
-
// Re-export
|
|
189
|
-
export {
|
|
182
|
+
// Re-export AuthenticationType for convenience
|
|
183
|
+
export { AuthenticationType } from 'expo-local-authentication';
|
package/src/cryptoUtils.ts
CHANGED
|
@@ -7,13 +7,11 @@
|
|
|
7
7
|
* - Input to RSA: secretKey as Base64 string
|
|
8
8
|
* - Output: encrypted sessionId as Base64 string
|
|
9
9
|
*
|
|
10
|
-
* Uses
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
* @requires react-native-quick-crypto - npm install react-native-quick-crypto
|
|
10
|
+
* Uses node-forge for pure JavaScript RSA-OAEP encryption.
|
|
11
|
+
* This works in Expo Go without native modules.
|
|
14
12
|
*/
|
|
15
13
|
|
|
16
|
-
import
|
|
14
|
+
import forge from 'node-forge';
|
|
17
15
|
|
|
18
16
|
/**
|
|
19
17
|
* Generates a random hex key matching Android's generateRandomHexKey()
|
|
@@ -50,39 +48,14 @@ export function hexToBase64(hexString: string): string {
|
|
|
50
48
|
binaryString += String.fromCharCode(byte);
|
|
51
49
|
}
|
|
52
50
|
|
|
53
|
-
// Use
|
|
54
|
-
|
|
55
|
-
return btoa(binaryString);
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
// Fallback: manual Base64 encoding
|
|
59
|
-
const base64Chars =
|
|
60
|
-
'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
|
|
61
|
-
let result = '';
|
|
62
|
-
let i = 0;
|
|
63
|
-
|
|
64
|
-
while (i < binaryString.length) {
|
|
65
|
-
const a = binaryString.charCodeAt(i++);
|
|
66
|
-
const b = binaryString.charCodeAt(i++);
|
|
67
|
-
const c = binaryString.charCodeAt(i++);
|
|
68
|
-
|
|
69
|
-
const triplet = (a << 16) | ((b || 0) << 8) | (c || 0);
|
|
70
|
-
|
|
71
|
-
result +=
|
|
72
|
-
base64Chars[(triplet >> 18) & 0x3f] +
|
|
73
|
-
base64Chars[(triplet >> 12) & 0x3f] +
|
|
74
|
-
(isNaN(b) ? '=' : base64Chars[(triplet >> 6) & 0x3f]) +
|
|
75
|
-
(isNaN(c) ? '=' : base64Chars[triplet & 0x3f]);
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
return result;
|
|
51
|
+
// Use forge's utility for Base64 encoding
|
|
52
|
+
return forge.util.encode64(binaryString);
|
|
79
53
|
}
|
|
80
54
|
|
|
81
55
|
/**
|
|
82
|
-
* Encrypts data using RSA with OAEP-SHA1 padding
|
|
56
|
+
* Encrypts data using RSA with OAEP-SHA1 padding
|
|
83
57
|
*
|
|
84
58
|
* MUST match Android's RSA/ECB/OAEPWithSHA-1AndMGF1Padding algorithm
|
|
85
|
-
* Uses native crypto (OpenSSL on Android, CommonCrypto on iOS)
|
|
86
59
|
*
|
|
87
60
|
* @param data - Plain text data to encrypt (Base64 string of the secret key)
|
|
88
61
|
* @param publicKeyPem - RSA public key in PEM format
|
|
@@ -91,21 +64,19 @@ export function hexToBase64(hexString: string): string {
|
|
|
91
64
|
*/
|
|
92
65
|
export function encryptWithRSA(data: string, publicKeyPem: string): string {
|
|
93
66
|
try {
|
|
94
|
-
//
|
|
95
|
-
const
|
|
67
|
+
// Parse the PEM-formatted public key
|
|
68
|
+
const publicKey = forge.pki.publicKeyFromPem(publicKeyPem);
|
|
96
69
|
|
|
97
70
|
// Encrypt using RSA-OAEP with SHA-1 (matches Android's OAEPWithSHA-1AndMGF1Padding)
|
|
98
|
-
const encrypted =
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
oaepHash: 'sha1',
|
|
71
|
+
const encrypted = publicKey.encrypt(data, 'RSA-OAEP', {
|
|
72
|
+
md: forge.md.sha1.create(),
|
|
73
|
+
mgf1: {
|
|
74
|
+
md: forge.md.sha1.create(),
|
|
103
75
|
},
|
|
104
|
-
|
|
105
|
-
);
|
|
76
|
+
});
|
|
106
77
|
|
|
107
|
-
//
|
|
108
|
-
return
|
|
78
|
+
// Encode to Base64
|
|
79
|
+
return forge.util.encode64(encrypted);
|
|
109
80
|
} catch (error) {
|
|
110
81
|
console.error('[CryptoUtils] RSA encryption failed:', error);
|
|
111
82
|
throw new Error('Failed to encrypt session data');
|
package/src/index.ts
CHANGED
package/src/types.ts
CHANGED
|
@@ -22,7 +22,8 @@ export type WidgetMessageType =
|
|
|
22
22
|
| "widget:close"
|
|
23
23
|
| "widget:transaction-requested"
|
|
24
24
|
| "widget:token-expired"
|
|
25
|
-
| "widget:request-card-details-session"
|
|
25
|
+
| "widget:request-card-details-session"
|
|
26
|
+
| "widget:copy-to-clipboard";
|
|
26
27
|
|
|
27
28
|
/**
|
|
28
29
|
* Native Message Types
|
|
@@ -34,7 +35,8 @@ export type NativeMessageType =
|
|
|
34
35
|
| "native:back-pressed"
|
|
35
36
|
| "native:navigate-back"
|
|
36
37
|
| "native:card-details-session"
|
|
37
|
-
| "native:biometric-failed"
|
|
38
|
+
| "native:biometric-failed"
|
|
39
|
+
| "native:clipboard-result";
|
|
38
40
|
|
|
39
41
|
export interface NativeBackPressedMessage {
|
|
40
42
|
type: "native:back-pressed";
|
|
@@ -77,13 +79,23 @@ export interface NativeBiometricFailedMessage {
|
|
|
77
79
|
};
|
|
78
80
|
}
|
|
79
81
|
|
|
82
|
+
export interface NativeClipboardResultMessage {
|
|
83
|
+
type: "native:clipboard-result";
|
|
84
|
+
payload: {
|
|
85
|
+
success: boolean;
|
|
86
|
+
label?: string; // Echo back the label for correlation
|
|
87
|
+
error?: string;
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
|
|
80
91
|
export type NativeMessage =
|
|
81
92
|
| NativeBackPressedMessage
|
|
82
93
|
| NativeNavigateBackMessage
|
|
83
94
|
| NativePlatformInfoMessage
|
|
84
95
|
| NativeWalletOpenedMessage
|
|
85
96
|
| NativeCardDetailsSessionMessage
|
|
86
|
-
| NativeBiometricFailedMessage
|
|
97
|
+
| NativeBiometricFailedMessage
|
|
98
|
+
| NativeClipboardResultMessage;
|
|
87
99
|
|
|
88
100
|
export interface BaseWidgetMessage {
|
|
89
101
|
type: WidgetMessageType;
|
|
@@ -146,6 +158,14 @@ export interface RequestCardDetailsSessionMessage extends BaseWidgetMessage {
|
|
|
146
158
|
};
|
|
147
159
|
}
|
|
148
160
|
|
|
161
|
+
export interface CopyToClipboardMessage extends BaseWidgetMessage {
|
|
162
|
+
type: "widget:copy-to-clipboard";
|
|
163
|
+
payload: {
|
|
164
|
+
text: string;
|
|
165
|
+
label?: string; // Optional: human-readable label for analytics/logging (e.g., "Card Number", "Deposit Address")
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
|
|
149
169
|
export type WidgetMessage =
|
|
150
170
|
| WidgetReadyMessage
|
|
151
171
|
| OpenWalletMessage
|
|
@@ -154,7 +174,8 @@ export type WidgetMessage =
|
|
|
154
174
|
| WidgetCloseMessage
|
|
155
175
|
| TransactionRequestedMessage
|
|
156
176
|
| TokenExpiredMessage
|
|
157
|
-
| RequestCardDetailsSessionMessage
|
|
177
|
+
| RequestCardDetailsSessionMessage
|
|
178
|
+
| CopyToClipboardMessage;
|
|
158
179
|
|
|
159
180
|
/**
|
|
160
181
|
* Widget environment configuration
|
|
@@ -219,6 +240,7 @@ export const MessageTypes = {
|
|
|
219
240
|
TRANSACTION_REQUESTED: "widget:transaction-requested",
|
|
220
241
|
TOKEN_EXPIRED: "widget:token-expired",
|
|
221
242
|
REQUEST_CARD_DETAILS_SESSION: "widget:request-card-details-session",
|
|
243
|
+
COPY_TO_CLIPBOARD: "widget:copy-to-clipboard",
|
|
222
244
|
// Native → Widget
|
|
223
245
|
PLATFORM_INFO: "native:platform-info",
|
|
224
246
|
WALLET_OPENED: "native:wallet-opened",
|
|
@@ -226,4 +248,5 @@ export const MessageTypes = {
|
|
|
226
248
|
NAVIGATE_BACK: "native:navigate-back",
|
|
227
249
|
CARD_DETAILS_SESSION: "native:card-details-session",
|
|
228
250
|
BIOMETRIC_FAILED: "native:biometric-failed",
|
|
251
|
+
CLIPBOARD_RESULT: "native:clipboard-result",
|
|
229
252
|
} as const;
|