@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 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)` | Back button, or the camera was refused |
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.
@@ -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 `url`, or a verification link's `url` (from your server, via the UVerify API). */
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 (or a verification link) inside your app. The
19
- * check itself runs on UVerify's page, the same one customers get in a
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
- * Needs the camera permission: add NSCameraUsageDescription to Info.plist and
24
- * <uses-permission android:name="android.permission.CAMERA" /> to AndroidManifest.xml.
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;
@@ -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 (or a verification link) inside your app. The
43
- * check itself runs on UVerify's page, the same one customers get in a
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
- * Needs the camera permission: add NSCameraUsageDescription to Info.plist and
48
- * <uses-permission android:name="android.permission.CAMERA" /> to AndroidManifest.xml.
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 (the web view can only use it if the app has it).
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 }, "Camera access is needed for the face check. Allow it in Settings, then try again.")));
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
- setSupportMultipleWindows: false, startInLoadingState: true, renderLoading: () => (react_1.default.createElement(react_native_1.View, { style: styles.center },
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@uverifyng/react-native-liveness",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "UVerify's face check (liveness) and verification links inside a React Native app.",
5
5
  "license": "MIT",
6
6
  "author": "Elasto Web Services Limited",