@oobit/react-native-sdk 1.0.4 → 1.0.6
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 +60 -3
- package/dist/WidgetSDK.js.map +1 -1
- package/dist/biometricUtils.d.ts +66 -0
- package/dist/biometricUtils.d.ts.map +1 -0
- package/dist/biometricUtils.js +183 -0
- package/dist/biometricUtils.js.map +1 -0
- package/dist/cryptoUtils.d.ts +64 -0
- package/dist/cryptoUtils.d.ts.map +1 -0
- package/dist/cryptoUtils.js +123 -0
- package/dist/cryptoUtils.js.map +1 -0
- package/dist/index.d.ts +5 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +12 -1
- package/dist/index.js.map +1 -1
- package/dist/types.d.ts +41 -18
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +18 -15
- package/dist/types.js.map +1 -1
- package/package.json +7 -5
- package/src/WidgetSDK.tsx +108 -7
- package/src/biometricUtils.ts +183 -0
- package/src/cryptoUtils.ts +131 -0
- package/src/index.ts +20 -0
- package/src/types.ts +80 -44
package/dist/types.d.ts
CHANGED
|
@@ -12,67 +12,81 @@ export interface DepositToken {
|
|
|
12
12
|
iconUrl: string | null;
|
|
13
13
|
networkName: string;
|
|
14
14
|
}
|
|
15
|
-
export type WidgetMessageType =
|
|
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";
|
|
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 =
|
|
20
|
+
export type NativeMessageType = "native:platform-info" | "native:wallet-opened" | "native:back-pressed" | "native:navigate-back" | "native:card-details-session" | "native:biometric-failed";
|
|
21
21
|
export interface NativeBackPressedMessage {
|
|
22
|
-
type:
|
|
22
|
+
type: "native:back-pressed";
|
|
23
23
|
timestamp: number;
|
|
24
24
|
}
|
|
25
25
|
export interface NativeNavigateBackMessage {
|
|
26
|
-
type:
|
|
26
|
+
type: "native:navigate-back";
|
|
27
27
|
timestamp: number;
|
|
28
28
|
}
|
|
29
29
|
export interface NativePlatformInfoMessage {
|
|
30
|
-
type:
|
|
30
|
+
type: "native:platform-info";
|
|
31
31
|
payload: {
|
|
32
32
|
platform: string;
|
|
33
33
|
walletAvailable: boolean;
|
|
34
34
|
};
|
|
35
35
|
}
|
|
36
36
|
export interface NativeWalletOpenedMessage {
|
|
37
|
-
type:
|
|
37
|
+
type: "native:wallet-opened";
|
|
38
38
|
payload: {
|
|
39
39
|
success: boolean;
|
|
40
40
|
};
|
|
41
41
|
}
|
|
42
|
-
export
|
|
42
|
+
export interface NativeCardDetailsSessionMessage {
|
|
43
|
+
type: "native:card-details-session";
|
|
44
|
+
payload: {
|
|
45
|
+
sessionId: string;
|
|
46
|
+
secretKey: string;
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
export interface NativeBiometricFailedMessage {
|
|
50
|
+
type: "native:biometric-failed";
|
|
51
|
+
payload: {
|
|
52
|
+
reason: "cancelled" | "failed" | "not_available" | "not_enrolled";
|
|
53
|
+
message?: string;
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
export type NativeMessage = NativeBackPressedMessage | NativeNavigateBackMessage | NativePlatformInfoMessage | NativeWalletOpenedMessage | NativeCardDetailsSessionMessage | NativeBiometricFailedMessage;
|
|
43
57
|
export interface BaseWidgetMessage {
|
|
44
58
|
type: WidgetMessageType;
|
|
45
59
|
timestamp: number;
|
|
46
60
|
}
|
|
47
61
|
export interface WidgetReadyMessage extends BaseWidgetMessage {
|
|
48
|
-
type:
|
|
62
|
+
type: "widget:ready";
|
|
49
63
|
}
|
|
50
64
|
export interface OpenWalletMessage extends BaseWidgetMessage {
|
|
51
|
-
type:
|
|
65
|
+
type: "widget:open-wallet";
|
|
52
66
|
payload: {
|
|
53
|
-
platform:
|
|
67
|
+
platform: "ios" | "android";
|
|
54
68
|
};
|
|
55
69
|
}
|
|
56
70
|
export interface CardCreatedMessage extends BaseWidgetMessage {
|
|
57
|
-
type:
|
|
71
|
+
type: "widget:card-created";
|
|
58
72
|
payload: {
|
|
59
73
|
cardId: string;
|
|
60
|
-
cardType:
|
|
74
|
+
cardType: "virtual" | "physical";
|
|
61
75
|
last4: string;
|
|
62
76
|
};
|
|
63
77
|
}
|
|
64
78
|
export interface WidgetErrorMessage extends BaseWidgetMessage {
|
|
65
|
-
type:
|
|
79
|
+
type: "widget:error";
|
|
66
80
|
payload: {
|
|
67
81
|
code: string;
|
|
68
82
|
message: string;
|
|
69
83
|
};
|
|
70
84
|
}
|
|
71
85
|
export interface WidgetCloseMessage extends BaseWidgetMessage {
|
|
72
|
-
type:
|
|
86
|
+
type: "widget:close";
|
|
73
87
|
}
|
|
74
88
|
export interface TransactionRequestedMessage extends BaseWidgetMessage {
|
|
75
|
-
type:
|
|
89
|
+
type: "widget:transaction-requested";
|
|
76
90
|
payload: {
|
|
77
91
|
token: DepositToken;
|
|
78
92
|
cryptoAmount: string;
|
|
@@ -81,18 +95,24 @@ export interface TransactionRequestedMessage extends BaseWidgetMessage {
|
|
|
81
95
|
};
|
|
82
96
|
}
|
|
83
97
|
export interface TokenExpiredMessage extends BaseWidgetMessage {
|
|
84
|
-
type:
|
|
98
|
+
type: "widget:token-expired";
|
|
85
99
|
payload?: {
|
|
86
100
|
reason?: string;
|
|
87
101
|
};
|
|
88
102
|
}
|
|
89
|
-
export
|
|
103
|
+
export interface RequestCardDetailsSessionMessage extends BaseWidgetMessage {
|
|
104
|
+
type: "widget:request-card-details-session";
|
|
105
|
+
payload: {
|
|
106
|
+
publicKey: string;
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
export type WidgetMessage = WidgetReadyMessage | OpenWalletMessage | CardCreatedMessage | WidgetErrorMessage | WidgetCloseMessage | TransactionRequestedMessage | TokenExpiredMessage | RequestCardDetailsSessionMessage;
|
|
90
110
|
/**
|
|
91
111
|
* Widget environment configuration
|
|
92
112
|
* - 'development': Uses development/staging widget URL
|
|
93
113
|
* - 'production': Uses production widget URL
|
|
94
114
|
*/
|
|
95
|
-
export type WidgetEnvironment =
|
|
115
|
+
export type WidgetEnvironment = "development" | "production";
|
|
96
116
|
/**
|
|
97
117
|
* SDK Configuration
|
|
98
118
|
*/
|
|
@@ -140,9 +160,12 @@ export declare const MessageTypes: {
|
|
|
140
160
|
readonly CLOSE: "widget:close";
|
|
141
161
|
readonly TRANSACTION_REQUESTED: "widget:transaction-requested";
|
|
142
162
|
readonly TOKEN_EXPIRED: "widget:token-expired";
|
|
163
|
+
readonly REQUEST_CARD_DETAILS_SESSION: "widget:request-card-details-session";
|
|
143
164
|
readonly PLATFORM_INFO: "native:platform-info";
|
|
144
165
|
readonly WALLET_OPENED: "native:wallet-opened";
|
|
145
166
|
readonly BACK_PRESSED: "native:back-pressed";
|
|
146
167
|
readonly NAVIGATE_BACK: "native:navigate-back";
|
|
168
|
+
readonly CARD_DETAILS_SESSION: "native:card-details-session";
|
|
169
|
+
readonly BIOMETRIC_FAILED: "native:biometric-failed";
|
|
147
170
|
};
|
|
148
171
|
//# 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,CAAC;
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|
package/dist/types.js
CHANGED
|
@@ -10,12 +10,12 @@ exports.MessageTypes = exports.WALLET_URLS = void 0;
|
|
|
10
10
|
*/
|
|
11
11
|
exports.WALLET_URLS = {
|
|
12
12
|
ios: {
|
|
13
|
-
passkit:
|
|
14
|
-
fallback:
|
|
13
|
+
passkit: "shoebox://", // Apple Wallet deep link
|
|
14
|
+
fallback: "https://wallet.apple.com",
|
|
15
15
|
},
|
|
16
16
|
android: {
|
|
17
|
-
googlePay:
|
|
18
|
-
fallback:
|
|
17
|
+
googlePay: "https://pay.google.com/gp/w/home/wallet",
|
|
18
|
+
fallback: "https://wallet.google.com",
|
|
19
19
|
},
|
|
20
20
|
};
|
|
21
21
|
/**
|
|
@@ -27,17 +27,20 @@ exports.WALLET_URLS = {
|
|
|
27
27
|
*/
|
|
28
28
|
exports.MessageTypes = {
|
|
29
29
|
// Widget → Native
|
|
30
|
-
READY:
|
|
31
|
-
OPEN_WALLET:
|
|
32
|
-
CARD_CREATED:
|
|
33
|
-
ERROR:
|
|
34
|
-
CLOSE:
|
|
35
|
-
TRANSACTION_REQUESTED:
|
|
36
|
-
TOKEN_EXPIRED:
|
|
30
|
+
READY: "widget:ready",
|
|
31
|
+
OPEN_WALLET: "widget:open-wallet",
|
|
32
|
+
CARD_CREATED: "widget:card-created",
|
|
33
|
+
ERROR: "widget:error",
|
|
34
|
+
CLOSE: "widget:close",
|
|
35
|
+
TRANSACTION_REQUESTED: "widget:transaction-requested",
|
|
36
|
+
TOKEN_EXPIRED: "widget:token-expired",
|
|
37
|
+
REQUEST_CARD_DETAILS_SESSION: "widget:request-card-details-session",
|
|
37
38
|
// Native → Widget
|
|
38
|
-
PLATFORM_INFO:
|
|
39
|
-
WALLET_OPENED:
|
|
40
|
-
BACK_PRESSED:
|
|
41
|
-
NAVIGATE_BACK:
|
|
39
|
+
PLATFORM_INFO: "native:platform-info",
|
|
40
|
+
WALLET_OPENED: "native:wallet-opened",
|
|
41
|
+
BACK_PRESSED: "native:back-pressed",
|
|
42
|
+
NAVIGATE_BACK: "native:navigate-back",
|
|
43
|
+
CARD_DETAILS_SESSION: "native:card-details-session",
|
|
44
|
+
BIOMETRIC_FAILED: "native:biometric-failed",
|
|
42
45
|
};
|
|
43
46
|
//# 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;;;AA2LH;;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,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;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.6",
|
|
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,23 +39,25 @@
|
|
|
39
39
|
"url": "git+https://github.com/oobit-tech/react-native-SDK.git"
|
|
40
40
|
},
|
|
41
41
|
"peerDependencies": {
|
|
42
|
+
"expo-intent-launcher": ">=6.0.0",
|
|
42
43
|
"expo-linking": ">=6.0.0",
|
|
44
|
+
"expo-local-authentication": ">=14.0.0",
|
|
43
45
|
"react": ">=18.0.0",
|
|
44
46
|
"react-native": ">=0.70.0",
|
|
45
47
|
"react-native-webview": ">=13.0.0"
|
|
46
48
|
},
|
|
47
|
-
"
|
|
48
|
-
"
|
|
49
|
-
"optional": true
|
|
50
|
-
}
|
|
49
|
+
"dependencies": {
|
|
50
|
+
"node-forge": "^1.3.1"
|
|
51
51
|
},
|
|
52
52
|
"devDependencies": {
|
|
53
|
+
"@types/node-forge": "^1.3.11",
|
|
53
54
|
"@types/react": "~19.1.0",
|
|
54
55
|
"@typescript-eslint/eslint-plugin": "^8.47.0",
|
|
55
56
|
"@typescript-eslint/parser": "^8.47.0",
|
|
56
57
|
"eslint": "^9.39.1",
|
|
57
58
|
"expo-intent-launcher": "~13.0.7",
|
|
58
59
|
"expo-linking": "~8.0.8",
|
|
60
|
+
"expo-local-authentication": "~15.0.2",
|
|
59
61
|
"react": "19.1.0",
|
|
60
62
|
"react-native": "0.81.5",
|
|
61
63
|
"react-native-webview": "13.15.0",
|
package/src/WidgetSDK.tsx
CHANGED
|
@@ -11,10 +11,24 @@ import React, {
|
|
|
11
11
|
useRef,
|
|
12
12
|
useState,
|
|
13
13
|
} from "react";
|
|
14
|
-
import {
|
|
14
|
+
import {
|
|
15
|
+
ActivityIndicator,
|
|
16
|
+
BackHandler,
|
|
17
|
+
Linking,
|
|
18
|
+
Platform,
|
|
19
|
+
StyleSheet,
|
|
20
|
+
View,
|
|
21
|
+
} from "react-native";
|
|
15
22
|
import { WebView, WebViewMessageEvent } from "react-native-webview";
|
|
23
|
+
import { authenticateWithBiometrics } from "./biometricUtils";
|
|
16
24
|
import { getWidgetUrl } from "./config";
|
|
17
|
-
import {
|
|
25
|
+
import { generateSessionCredentials } from "./cryptoUtils";
|
|
26
|
+
import {
|
|
27
|
+
NativeBiometricFailedMessage,
|
|
28
|
+
NativeCardDetailsSessionMessage,
|
|
29
|
+
WidgetMessage,
|
|
30
|
+
WidgetSDKConfig,
|
|
31
|
+
} from "./types";
|
|
18
32
|
import { isWalletAvailable, openNativeWallet } from "./walletUtils";
|
|
19
33
|
|
|
20
34
|
export interface WidgetSDKRef {
|
|
@@ -117,6 +131,10 @@ export const WidgetSDK = forwardRef<WidgetSDKRef, WidgetSDKConfig>(
|
|
|
117
131
|
onError?.("TOKEN_EXPIRED", "Access token has expired");
|
|
118
132
|
break;
|
|
119
133
|
|
|
134
|
+
case "widget:request-card-details-session":
|
|
135
|
+
handleCardDetailsRequest(message);
|
|
136
|
+
break;
|
|
137
|
+
|
|
120
138
|
default:
|
|
121
139
|
console.warn("[WidgetSDK] Unknown message type:", message);
|
|
122
140
|
}
|
|
@@ -178,7 +196,8 @@ export const WidgetSDK = forwardRef<WidgetSDKRef, WidgetSDKConfig>(
|
|
|
178
196
|
const handleTransactionRequested = (message: WidgetMessage) => {
|
|
179
197
|
if (message.type !== "widget:transaction-requested") return;
|
|
180
198
|
|
|
181
|
-
const { token, cryptoAmount, depositAddress, depositAddressTag } =
|
|
199
|
+
const { token, cryptoAmount, depositAddress, depositAddressTag } =
|
|
200
|
+
message.payload;
|
|
182
201
|
console.log("[WidgetSDK] Transaction requested:", {
|
|
183
202
|
token,
|
|
184
203
|
cryptoAmount,
|
|
@@ -186,7 +205,89 @@ export const WidgetSDK = forwardRef<WidgetSDKRef, WidgetSDKConfig>(
|
|
|
186
205
|
depositAddressTag,
|
|
187
206
|
});
|
|
188
207
|
|
|
189
|
-
onTransactionRequested?.(
|
|
208
|
+
onTransactionRequested?.(
|
|
209
|
+
token,
|
|
210
|
+
cryptoAmount,
|
|
211
|
+
depositAddress,
|
|
212
|
+
depositAddressTag
|
|
213
|
+
);
|
|
214
|
+
};
|
|
215
|
+
|
|
216
|
+
/**
|
|
217
|
+
* Handle card details session request from widget
|
|
218
|
+
* 1. Show biometric prompt for user authentication
|
|
219
|
+
* 2. Generate cryptographically linked sessionId + secretKey pair
|
|
220
|
+
* 3. Send credentials back to widget for API call and decryption
|
|
221
|
+
*/
|
|
222
|
+
const handleCardDetailsRequest = async (message: WidgetMessage) => {
|
|
223
|
+
if (message.type !== "widget:request-card-details-session") return;
|
|
224
|
+
|
|
225
|
+
const { publicKey } = message.payload;
|
|
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
|
+
);
|
|
240
|
+
|
|
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
|
+
try {
|
|
255
|
+
console.log(
|
|
256
|
+
"[WidgetSDK] Biometric auth successful, generating session credentials..."
|
|
257
|
+
);
|
|
258
|
+
|
|
259
|
+
const { secretKeyHex, sessionId } = await generateSessionCredentials(
|
|
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);
|
|
275
|
+
} catch (error) {
|
|
276
|
+
console.error(
|
|
277
|
+
"[WidgetSDK] Failed to generate session credentials:",
|
|
278
|
+
error
|
|
279
|
+
);
|
|
280
|
+
|
|
281
|
+
const failedMessage: NativeBiometricFailedMessage = {
|
|
282
|
+
type: "native:biometric-failed",
|
|
283
|
+
payload: {
|
|
284
|
+
reason: "failed",
|
|
285
|
+
message: "Failed to generate secure session",
|
|
286
|
+
},
|
|
287
|
+
};
|
|
288
|
+
|
|
289
|
+
sendMessageToWidget(failedMessage);
|
|
290
|
+
}
|
|
190
291
|
};
|
|
191
292
|
|
|
192
293
|
/**
|
|
@@ -286,8 +387,8 @@ const styles = StyleSheet.create({
|
|
|
286
387
|
},
|
|
287
388
|
loadingOverlay: {
|
|
288
389
|
...StyleSheet.absoluteFillObject,
|
|
289
|
-
backgroundColor:
|
|
290
|
-
justifyContent:
|
|
291
|
-
alignItems:
|
|
390
|
+
backgroundColor: "rgba(255, 255, 255, 0.9)",
|
|
391
|
+
justifyContent: "center",
|
|
392
|
+
alignItems: "center",
|
|
292
393
|
},
|
|
293
394
|
});
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Biometric Authentication Utilities
|
|
3
|
+
*
|
|
4
|
+
* Provides cross-platform biometric authentication for card details access.
|
|
5
|
+
* Uses expo-local-authentication which works in Expo Go without native modules.
|
|
6
|
+
*
|
|
7
|
+
* @requires expo-local-authentication - included in Expo SDK
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import * as LocalAuthentication from 'expo-local-authentication';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Result of a biometric authentication attempt
|
|
14
|
+
*/
|
|
15
|
+
export interface BiometricResult {
|
|
16
|
+
/** Whether authentication was successful */
|
|
17
|
+
success: boolean;
|
|
18
|
+
/** Error details if authentication failed */
|
|
19
|
+
error?: {
|
|
20
|
+
reason: 'cancelled' | 'failed' | 'not_available' | 'not_enrolled';
|
|
21
|
+
message?: string;
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Information about biometric capabilities on the device
|
|
27
|
+
*/
|
|
28
|
+
export interface BiometricAvailability {
|
|
29
|
+
/** Whether any biometric authentication is available */
|
|
30
|
+
available: boolean;
|
|
31
|
+
/** The types of biometry available */
|
|
32
|
+
biometryTypes: LocalAuthentication.AuthenticationType[];
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Checks if biometric authentication is available on the device
|
|
37
|
+
*
|
|
38
|
+
* @returns Object containing availability status and biometry types
|
|
39
|
+
*/
|
|
40
|
+
export async function isBiometricAvailable(): Promise<BiometricAvailability> {
|
|
41
|
+
try {
|
|
42
|
+
const hasHardware = await LocalAuthentication.hasHardwareAsync();
|
|
43
|
+
const isEnrolled = await LocalAuthentication.isEnrolledAsync();
|
|
44
|
+
const supportedTypes = await LocalAuthentication.supportedAuthenticationTypesAsync();
|
|
45
|
+
|
|
46
|
+
return {
|
|
47
|
+
available: hasHardware && isEnrolled,
|
|
48
|
+
biometryTypes: supportedTypes,
|
|
49
|
+
};
|
|
50
|
+
} catch (error) {
|
|
51
|
+
console.error('[BiometricUtils] Error checking biometric availability:', error);
|
|
52
|
+
return {
|
|
53
|
+
available: false,
|
|
54
|
+
biometryTypes: [],
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Prompts the user for biometric authentication
|
|
61
|
+
*
|
|
62
|
+
* This function should be called before generating session credentials
|
|
63
|
+
* for viewing card details. It ensures proper user verification before
|
|
64
|
+
* accessing sensitive payment card information.
|
|
65
|
+
*
|
|
66
|
+
* @param promptMessage - Message to display in the biometric prompt
|
|
67
|
+
* @returns Result indicating success or failure with reason
|
|
68
|
+
*
|
|
69
|
+
* @example
|
|
70
|
+
* ```typescript
|
|
71
|
+
* const result = await authenticateWithBiometrics('Authenticate to view card details');
|
|
72
|
+
* if (result.success) {
|
|
73
|
+
* // Proceed with generating session credentials
|
|
74
|
+
* } else {
|
|
75
|
+
* // Handle failure based on result.error.reason
|
|
76
|
+
* }
|
|
77
|
+
* ```
|
|
78
|
+
*/
|
|
79
|
+
export async function authenticateWithBiometrics(
|
|
80
|
+
promptMessage: string = 'Authenticate to view card details'
|
|
81
|
+
): Promise<BiometricResult> {
|
|
82
|
+
try {
|
|
83
|
+
// First check if biometrics are available
|
|
84
|
+
const hasHardware = await LocalAuthentication.hasHardwareAsync();
|
|
85
|
+
|
|
86
|
+
if (!hasHardware) {
|
|
87
|
+
console.log('[BiometricUtils] Biometrics not available on device');
|
|
88
|
+
return {
|
|
89
|
+
success: false,
|
|
90
|
+
error: {
|
|
91
|
+
reason: 'not_available',
|
|
92
|
+
message: 'Biometric authentication is not available on this device',
|
|
93
|
+
},
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// Check if biometrics are enrolled
|
|
98
|
+
const isEnrolled = await LocalAuthentication.isEnrolledAsync();
|
|
99
|
+
|
|
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
|
+
}
|
|
110
|
+
|
|
111
|
+
console.log('[BiometricUtils] Attempting biometric authentication...');
|
|
112
|
+
|
|
113
|
+
// Perform the biometric authentication
|
|
114
|
+
const result = await LocalAuthentication.authenticateAsync({
|
|
115
|
+
promptMessage,
|
|
116
|
+
cancelLabel: 'Cancel',
|
|
117
|
+
disableDeviceFallback: false, // Allow PIN/password fallback
|
|
118
|
+
fallbackLabel: 'Use Passcode',
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
if (result.success) {
|
|
122
|
+
console.log('[BiometricUtils] Biometric authentication successful');
|
|
123
|
+
return { success: true };
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// Handle various failure reasons
|
|
127
|
+
console.log('[BiometricUtils] Biometric authentication failed:', result.error);
|
|
128
|
+
|
|
129
|
+
let reason: 'cancelled' | 'failed' | 'not_available' | 'not_enrolled' = 'failed';
|
|
130
|
+
|
|
131
|
+
if (result.error === 'user_cancel' || result.error === 'system_cancel') {
|
|
132
|
+
reason = 'cancelled';
|
|
133
|
+
} else if (result.error === 'not_enrolled') {
|
|
134
|
+
reason = 'not_enrolled';
|
|
135
|
+
} else if (result.error === 'not_available') {
|
|
136
|
+
reason = 'not_available';
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
return {
|
|
140
|
+
success: false,
|
|
141
|
+
error: {
|
|
142
|
+
reason,
|
|
143
|
+
message: result.warning || 'Authentication failed',
|
|
144
|
+
},
|
|
145
|
+
};
|
|
146
|
+
} catch (error) {
|
|
147
|
+
console.error('[BiometricUtils] Biometric authentication error:', error);
|
|
148
|
+
|
|
149
|
+
const errorMessage = error instanceof Error ? error.message : 'Unknown error occurred';
|
|
150
|
+
|
|
151
|
+
return {
|
|
152
|
+
success: false,
|
|
153
|
+
error: {
|
|
154
|
+
reason: 'failed',
|
|
155
|
+
message: errorMessage,
|
|
156
|
+
},
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Gets a user-friendly description of the biometric type
|
|
163
|
+
*
|
|
164
|
+
* @param biometryType - The biometry type from the device
|
|
165
|
+
* @returns Human-readable string for the biometry type
|
|
166
|
+
*/
|
|
167
|
+
export function getBiometryTypeLabel(
|
|
168
|
+
biometryType: LocalAuthentication.AuthenticationType
|
|
169
|
+
): string {
|
|
170
|
+
switch (biometryType) {
|
|
171
|
+
case LocalAuthentication.AuthenticationType.FACIAL_RECOGNITION:
|
|
172
|
+
return 'Face ID';
|
|
173
|
+
case LocalAuthentication.AuthenticationType.FINGERPRINT:
|
|
174
|
+
return 'Touch ID';
|
|
175
|
+
case LocalAuthentication.AuthenticationType.IRIS:
|
|
176
|
+
return 'Iris';
|
|
177
|
+
default:
|
|
178
|
+
return 'Biometric Authentication';
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// Re-export AuthenticationType for convenience
|
|
183
|
+
export { AuthenticationType } from 'expo-local-authentication';
|