@uverifyng/react-native-liveness 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +7 -1
- package/dist/UVerifyWebView.d.ts +10 -8
- package/dist/UVerifyWebView.js +15 -11
- package/dist/events.d.ts +9 -0
- package/dist/events.js +7 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -25,6 +25,8 @@ React Native 0.72+, `react-native-webview` 13+. iOS 14.3+ (camera inside web vie
|
|
|
25
25
|
<uses-permission android:name="android.permission.CAMERA" />
|
|
26
26
|
```
|
|
27
27
|
|
|
28
|
+
Using address links? Also add location: `NSLocationWhenInUseUsageDescription` (iOS) and `<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />` (Android). If the link asks for a proof of address, the customer photographs it in the page: add `NSPhotoLibraryUsageDescription` (iOS) for the gallery; react-native-webview handles the file picker.
|
|
29
|
+
|
|
28
30
|
## Use
|
|
29
31
|
|
|
30
32
|
Your **server** creates the session with a UVerify server SDK and returns its `url` and `id`. Never put your API key in the app.
|
|
@@ -57,6 +59,10 @@ The result in the app is only a hint to move on; **trust what your server gets f
|
|
|
57
59
|
|
|
58
60
|
The same component opens a verification link (`link.url` from `kyc.createLink`): `onFinish` receives `{ type: 'kyc', id, status, outcome }`.
|
|
59
61
|
|
|
62
|
+
### Address links
|
|
63
|
+
|
|
64
|
+
It also opens an address link (`link.url` from `address.createLink`), where the customer shares their phone's location at home. The component asks for location instead of the camera, and `onFinish` receives `{ type: 'address', id, status, result }`. Read the distance and details on your server with `address.getLink(id)`.
|
|
65
|
+
|
|
60
66
|
### Options
|
|
61
67
|
|
|
62
68
|
| Prop | |
|
|
@@ -64,7 +70,7 @@ The same component opens a verification link (`link.url` from `kyc.createLink`):
|
|
|
64
70
|
| `url` | The session or link `url` from your server |
|
|
65
71
|
| `redirectUrl` | The `redirect_url` you created it with, if any; reaching it also finishes (`{ type: 'redirect', params }`) |
|
|
66
72
|
| `onFinish(result)` | The flow finished |
|
|
67
|
-
| `onCancel(reason)` |
|
|
73
|
+
| `onCancel(reason)` | `back`, or `camera_denied` / `location_denied` |
|
|
68
74
|
| `allowedHosts` | Extra hosts allowed in the view (UVerify’s are always allowed; everything else is blocked) |
|
|
69
75
|
|
|
70
76
|
MIT licence.
|
package/dist/UVerifyWebView.d.ts
CHANGED
|
@@ -2,25 +2,27 @@ import React from 'react';
|
|
|
2
2
|
import { type StyleProp, type ViewStyle } from 'react-native';
|
|
3
3
|
import { type UVerifyResult } from './events';
|
|
4
4
|
export interface UVerifyWebViewProps {
|
|
5
|
-
/** A liveness session's
|
|
5
|
+
/** A liveness session's, verification link's or address link's `url` (from your server, via the UVerify API). */
|
|
6
6
|
url: string;
|
|
7
7
|
/** The redirect_url you created the session/link with, if any: reaching it also finishes. */
|
|
8
8
|
redirectUrl?: string;
|
|
9
9
|
/** The flow finished. For liveness, pass `result.sessionId` to your server for the face match. */
|
|
10
10
|
onFinish: (result: UVerifyResult) => void;
|
|
11
|
-
/** The person backed out (Android back button) or refused the camera. */
|
|
12
|
-
onCancel?: (reason: 'back' | 'camera_denied') => void;
|
|
11
|
+
/** The person backed out (Android back button), or refused the camera (location, for an address link). */
|
|
12
|
+
onCancel?: (reason: 'back' | 'camera_denied' | 'location_denied') => void;
|
|
13
13
|
/** Extra hosts to allow (default: uverify.com.ng and its subdomains). */
|
|
14
14
|
allowedHosts?: string[];
|
|
15
15
|
style?: StyleProp<ViewStyle>;
|
|
16
16
|
}
|
|
17
17
|
/**
|
|
18
|
-
* UVerify's hosted face check
|
|
19
|
-
* check itself runs on UVerify's page, the same one customers get in
|
|
20
|
-
* browser, so every anti-spoofing layer applies and improvements reach your
|
|
18
|
+
* UVerify's hosted face check, verification link or address link inside your
|
|
19
|
+
* app. The check itself runs on UVerify's page, the same one customers get in
|
|
20
|
+
* a browser, so every anti-spoofing layer applies and improvements reach your
|
|
21
21
|
* app without an update.
|
|
22
22
|
*
|
|
23
|
-
*
|
|
24
|
-
* <uses-permission android:name="android.permission.CAMERA" />
|
|
23
|
+
* Face checks need the camera: NSCameraUsageDescription in Info.plist and
|
|
24
|
+
* <uses-permission android:name="android.permission.CAMERA" /> in AndroidManifest.xml.
|
|
25
|
+
* Address links need the location: NSLocationWhenInUseUsageDescription and
|
|
26
|
+
* <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />.
|
|
25
27
|
*/
|
|
26
28
|
export declare function UVerifyWebView({ url, redirectUrl, onFinish, onCancel, allowedHosts, style }: UVerifyWebViewProps): React.JSX.Element;
|
package/dist/UVerifyWebView.js
CHANGED
|
@@ -39,15 +39,18 @@ const react_native_1 = require("react-native");
|
|
|
39
39
|
const react_native_webview_1 = require("react-native-webview");
|
|
40
40
|
const events_1 = require("./events");
|
|
41
41
|
/**
|
|
42
|
-
* UVerify's hosted face check
|
|
43
|
-
* check itself runs on UVerify's page, the same one customers get in
|
|
44
|
-
* browser, so every anti-spoofing layer applies and improvements reach your
|
|
42
|
+
* UVerify's hosted face check, verification link or address link inside your
|
|
43
|
+
* app. The check itself runs on UVerify's page, the same one customers get in
|
|
44
|
+
* a browser, so every anti-spoofing layer applies and improvements reach your
|
|
45
45
|
* app without an update.
|
|
46
46
|
*
|
|
47
|
-
*
|
|
48
|
-
* <uses-permission android:name="android.permission.CAMERA" />
|
|
47
|
+
* Face checks need the camera: NSCameraUsageDescription in Info.plist and
|
|
48
|
+
* <uses-permission android:name="android.permission.CAMERA" /> in AndroidManifest.xml.
|
|
49
|
+
* Address links need the location: NSLocationWhenInUseUsageDescription and
|
|
50
|
+
* <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />.
|
|
49
51
|
*/
|
|
50
52
|
function UVerifyWebView({ url, redirectUrl, onFinish, onCancel, allowedHosts = [], style }) {
|
|
53
|
+
const location = (0, events_1.needsLocation)(url);
|
|
51
54
|
const [cameraOk, setCameraOk] = (0, react_1.useState)(react_native_1.Platform.OS !== 'android');
|
|
52
55
|
const [denied, setDenied] = (0, react_1.useState)(false);
|
|
53
56
|
const done = (0, react_1.useRef)(false);
|
|
@@ -58,19 +61,19 @@ function UVerifyWebView({ url, redirectUrl, onFinish, onCancel, allowedHosts = [
|
|
|
58
61
|
done.current = true;
|
|
59
62
|
onFinish(r);
|
|
60
63
|
}, [onFinish]);
|
|
61
|
-
// Android: ask for the camera before the page does
|
|
64
|
+
// Android: ask for the camera (or location) before the page does: the web view can only use it if the app has it.
|
|
62
65
|
(0, react_1.useEffect)(() => {
|
|
63
66
|
if (react_native_1.Platform.OS !== 'android')
|
|
64
67
|
return;
|
|
65
|
-
react_native_1.PermissionsAndroid.request(react_native_1.PermissionsAndroid.PERMISSIONS.CAMERA).then((r) => {
|
|
68
|
+
react_native_1.PermissionsAndroid.request(location ? react_native_1.PermissionsAndroid.PERMISSIONS.ACCESS_FINE_LOCATION : react_native_1.PermissionsAndroid.PERMISSIONS.CAMERA).then((r) => {
|
|
66
69
|
if (r === react_native_1.PermissionsAndroid.RESULTS.GRANTED)
|
|
67
70
|
setCameraOk(true);
|
|
68
71
|
else {
|
|
69
72
|
setDenied(true);
|
|
70
|
-
onCancel?.('camera_denied');
|
|
73
|
+
onCancel?.(location ? 'location_denied' : 'camera_denied');
|
|
71
74
|
}
|
|
72
75
|
});
|
|
73
|
-
}, [onCancel]);
|
|
76
|
+
}, [onCancel, location]);
|
|
74
77
|
(0, react_1.useEffect)(() => {
|
|
75
78
|
const sub = react_native_1.BackHandler.addEventListener('hardwareBackPress', () => {
|
|
76
79
|
if (done.current)
|
|
@@ -96,7 +99,7 @@ function UVerifyWebView({ url, redirectUrl, onFinish, onCancel, allowedHosts = [
|
|
|
96
99
|
};
|
|
97
100
|
if (denied) {
|
|
98
101
|
return (react_1.default.createElement(react_native_1.View, { style: [styles.center, style] },
|
|
99
|
-
react_1.default.createElement(react_native_1.Text, { style: styles.text },
|
|
102
|
+
react_1.default.createElement(react_native_1.Text, { style: styles.text }, location ? 'Location access is needed to confirm your address. Allow it in Settings, then try again.' : 'Camera access is needed for the face check. Allow it in Settings, then try again.')));
|
|
100
103
|
}
|
|
101
104
|
if (!cameraOk) {
|
|
102
105
|
return (react_1.default.createElement(react_native_1.View, { style: [styles.center, style] },
|
|
@@ -106,7 +109,8 @@ function UVerifyWebView({ url, redirectUrl, onFinish, onCancel, allowedHosts = [
|
|
|
106
109
|
// iOS: camera inside the page, inline (not full-screen), no second prompt from WebKit.
|
|
107
110
|
allowsInlineMediaPlayback: true, mediaPlaybackRequiresUserAction: false, mediaCapturePermissionGrantType: "grant",
|
|
108
111
|
// Android: react-native-webview passes the page's camera request through once the app holds CAMERA (asked above).
|
|
109
|
-
|
|
112
|
+
// Location (address links): Android web views only share it when enabled; iOS asks with the app's own permission.
|
|
113
|
+
geolocationEnabled: location, setSupportMultipleWindows: false, startInLoadingState: true, renderLoading: () => (react_1.default.createElement(react_native_1.View, { style: styles.center },
|
|
110
114
|
react_1.default.createElement(react_native_1.ActivityIndicator, null))) }));
|
|
111
115
|
}
|
|
112
116
|
const styles = react_native_1.StyleSheet.create({
|
package/dist/events.d.ts
CHANGED
|
@@ -11,6 +11,13 @@ export type UVerifyResult = {
|
|
|
11
11
|
id: string;
|
|
12
12
|
status: string;
|
|
13
13
|
outcome: string | null;
|
|
14
|
+
}
|
|
15
|
+
/** An address link: fetch it on your server for the distance and details. */
|
|
16
|
+
| {
|
|
17
|
+
type: 'address';
|
|
18
|
+
id: string;
|
|
19
|
+
status: 'completed' | 'expired';
|
|
20
|
+
result: 'verified' | 'partial' | 'failed' | null;
|
|
14
21
|
} | {
|
|
15
22
|
type: 'redirect';
|
|
16
23
|
url: string;
|
|
@@ -20,5 +27,7 @@ export type UVerifyResult = {
|
|
|
20
27
|
export declare function parseMessage(data: string): UVerifyResult | null;
|
|
21
28
|
/** The page is sending the person back to your redirect_url: finish with its query parameters. */
|
|
22
29
|
export declare function parseRedirect(url: string, redirectUrl: string | undefined): UVerifyResult | null;
|
|
30
|
+
/** An address link (/address/<id>) needs the phone's location; everything else needs the camera. */
|
|
31
|
+
export declare function needsLocation(url: string): boolean;
|
|
23
32
|
/** Only UVerify's pages (and about:blank) load in the view; anything else is refused. */
|
|
24
33
|
export declare function isAllowed(url: string, hosts: string[]): boolean;
|
package/dist/events.js
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.parseMessage = parseMessage;
|
|
4
4
|
exports.parseRedirect = parseRedirect;
|
|
5
|
+
exports.needsLocation = needsLocation;
|
|
5
6
|
exports.isAllowed = isAllowed;
|
|
6
7
|
/** The hosted page's "finished" message (posted by uverify.com.ng), or null if it's something else. */
|
|
7
8
|
function parseMessage(data) {
|
|
@@ -18,6 +19,8 @@ function parseMessage(data) {
|
|
|
18
19
|
return { type: 'liveness', sessionId: m.session_id, status: m.status };
|
|
19
20
|
if (m.type === 'kyc.finished' && typeof m.id === 'string')
|
|
20
21
|
return { type: 'kyc', id: m.id, status: m.status, outcome: m.outcome ?? null };
|
|
22
|
+
if (m.type === 'address.finished' && typeof m.id === 'string')
|
|
23
|
+
return { type: 'address', id: m.id, status: m.status, result: m.result ?? null };
|
|
21
24
|
return null;
|
|
22
25
|
}
|
|
23
26
|
/** The page is sending the person back to your redirect_url: finish with its query parameters. */
|
|
@@ -35,6 +38,10 @@ function parseRedirect(url, redirectUrl) {
|
|
|
35
38
|
}
|
|
36
39
|
return { type: 'redirect', url, params };
|
|
37
40
|
}
|
|
41
|
+
/** An address link (/address/<id>) needs the phone's location; everything else needs the camera. */
|
|
42
|
+
function needsLocation(url) {
|
|
43
|
+
return /^https:\/\/[^/]+\/address\//i.test(url);
|
|
44
|
+
}
|
|
38
45
|
/** Only UVerify's pages (and about:blank) load in the view; anything else is refused. */
|
|
39
46
|
function isAllowed(url, hosts) {
|
|
40
47
|
if (url === 'about:blank')
|
package/package.json
CHANGED