Skip to content

Commit

Permalink
version 1.0.0
Browse files Browse the repository at this point in the history
  • Loading branch information
JayLVouched committed Aug 11, 2022
1 parent d0b04fe commit be47d99
Show file tree
Hide file tree
Showing 17 changed files with 262 additions and 57 deletions.
25 changes: 19 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,12 @@ Ensure all [prerequisites](#prerequisites) have been met.

## Create Verification Flow

1. Determine the steps needed (ID, ID + Selfie, Authentication)
Note: Be sure to review the IDScreen, BackIDScreen and FaceScreen in
the example app to get a sense of possible behaviors you can use in
your app's verification flow. Some IDs require processing both front
and back sides.

1. Determine the steps needed (ID, ID + Selfie, Reverification)
2. Create the Component/Screen(s) for each step
3. Use the appropriate Camera ([IdCamera](#idcamera) or [FaceCamera](#facecamera)) for the step.
4. Ensure [session.confirm](#post-confirm-verification) is called once verification is complete to recieve finalized job data.
Expand All @@ -74,6 +79,15 @@ const job = await session.postFrontId(cardDetectionResult, params);
`Parameters` - [CardDetectResult](#carddetectresult-object), [Params](#params-object)
`Returns` - [Job](#job-object)

##### POST Back Id image

```javascript
const job = await session.postBackId(cardDetectionResult, params);
```

`Parameters` - [CardDetectResult](#carddetectresult-object), [Params](#params-object)
`Returns` - [Job](#job-object)

##### POST Selfie image

```javascript
Expand All @@ -83,18 +97,17 @@ const job = await session.postFace(faceDetectionResult);
`Parameters` - [FaceDetectResult](#facedetectresult-object)
`Returns` - [Job](#job-object)

##### POST Authentication
##### POST Re-verification

```javascript
const authResult = await session.postAuthenticate(
const authResult = await session.postReverify(
faceDetectionResult,
jobId,
matchId
jobId
);
```

`Parameters` - [FaceDetectResult](#facedetectresult-object), String, Boolean
`Returns` - [AuthenticateResult](#authenticateresult-object)
`Returns` - [Job](#job-object)

##### POST confirm verification

Expand Down
2 changes: 1 addition & 1 deletion android/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ dependencies {
//noinspection GradleDynamicVersion
implementation 'com.facebook.react:react-native:+' // From node_modules

implementation 'id.vouched.android:vouched-sdk:0.7.0'
implementation 'id.vouched.android:vouched-sdk:1.2.2'
implementation 'com.google.mlkit:face-detection:16.1.2'
implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
}
Expand Down
59 changes: 44 additions & 15 deletions android/src/main/java/id/vouched/rn/VouchedSessionModule.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

import com.facebook.react.bridge.NativeMap;
import com.facebook.react.bridge.Promise;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
Expand All @@ -11,6 +12,8 @@
import com.facebook.react.bridge.ReadableMap;
import com.facebook.react.bridge.ReadableType;

import java.util.Map;

import id.vouched.android.CardDetectResult;
import id.vouched.android.FaceDetectResult;
import id.vouched.android.VouchedSession;
Expand All @@ -24,9 +27,10 @@ public class VouchedSessionModule extends ReactContextBaseJavaModule {

private static final String SESSION_NOT_CONFIGURED = "SESSION_NOT_CONFIGURED";
private static final String POST_FRONT_ID_FAIL = "POST_FRONT_ID_FAIL";
private static final String POST_BACK_ID_FAIL = "POST_BACK_ID_FAIL";
private static final String POST_FACE_FAIL = "POST_FACE_FAIL";
private static final String CONFIRM_FAIL = "CONFIRM_FAIL";
private static final String POST_AUTHENTICATE_FAIL = "POST_AUTHENTICATE_FAIL";
private static final String POST_REVERIFY_FAIL = "POST_REVERIFY_FAIL";

private VouchedSession session;

Expand Down Expand Up @@ -101,6 +105,35 @@ public void onJobResponse(JobResponse jobResponse) {
}
}

@ReactMethod
public void postBackId(ReadableMap detectResult, final Promise promise) {
if (session == null) {
promise.reject(SESSION_NOT_CONFIGURED, "session must be configured");
return;
}

String distanceImage = detectResult.getString("distanceImage");
String image = detectResult.getString("image");

CardDetectResult cardDetectResult = new CardDetectResult(null, null, image, distanceImage, null, null);

try {
session.postBackId(getReactApplicationContext(), cardDetectResult, new Params.Builder(), new VouchedSession.OnJobResponseListener() {
@Override
public void onJobResponse(JobResponse jobResponse) {
VouchedError jobError = jobResponse.getError();
if (jobError != null) {
promise.reject(POST_BACK_ID_FAIL, jobError.getMessage());
} else {
promise.resolve(jobResponse.getJob().toJson());
}
}
});
} catch (Exception e) {
promise.reject(e);
}
}

@ReactMethod
public void postFace(ReadableMap detectResult, final Promise promise) {
if (session == null) {
Expand Down Expand Up @@ -131,32 +164,28 @@ public void onJobResponse(JobResponse jobResponse) {
}

@ReactMethod
public void postAuthenticate(ReadableMap authRequest, final Promise promise) {

public void postReverify(ReadableMap detectResult, final Promise promise) {
if (session == null) {
promise.reject(SESSION_NOT_CONFIGURED, "session must be configured");
return;
}

String image = authRequest.getString("image");
String jobId = authRequest.getString("jobId");
boolean matchId = authRequest.getBoolean("matchId");

FaceDetectResult faceDetectResult = new FaceDetectResult(null, null, image, null);
String jobId = detectResult.getString("jobId");
ReadableMap faceDetection = detectResult.getMap("faceDetectionResult");
String userPhoto = faceDetection.getString("image");

try {
session.postAuthenticate(getReactApplicationContext(), jobId, faceDetectResult, matchId, new VouchedSession.OnAuthenticationResponseListener() {
session.postReverification(getReactApplicationContext(), jobId, userPhoto, new Params.Builder(), new VouchedSession.OnJobResponseListener() {
@Override
public void onAuthenticationResponse(AuthenticationResponse response) {
VouchedError vouchedError = response.getError();
if (vouchedError != null) {
promise.reject(POST_AUTHENTICATE_FAIL, vouchedError.getMessage());
public void onJobResponse(JobResponse jobResponse) {
VouchedError jobError = jobResponse.getError();
if (jobError != null) {
promise.reject(POST_REVERIFY_FAIL, jobError.getMessage());
} else {
promise.resolve(response.getResult().toJson());
promise.resolve(jobResponse.getJob().toJson());
}
}
});

} catch (Exception e) {
promise.reject(e);
}
Expand Down
6 changes: 6 additions & 0 deletions example/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { createStackNavigator } from '@react-navigation/stack';

import HomeScreen from 'components/HomeScreen';
import IDScreen from 'components/IDScreen';
import BackIDScreen from 'components/BackIDScreen';
import FaceScreen from 'components/FaceScreen';
import DoneScreen from 'components/DoneScreen';
import AuthScreen from 'components/AuthScreen';
Expand Down Expand Up @@ -69,6 +70,11 @@ const App = () => {
component={IDScreen}
options={{ headerLeft: null }}
/>
<Stack.Screen
name="BackID"
component={BackIDScreen}
options={{ headerLeft: null }}
/>
<Stack.Screen
name="Face"
component={FaceScreen}
Expand Down
2 changes: 2 additions & 0 deletions example/ios/example.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -537,6 +537,7 @@
"-ObjC",
"-lc++",
);
PRODUCT_BUNDLE_IDENTIFIER = id.vouched.rndemo;
PRODUCT_NAME = example;
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 5.0;
Expand Down Expand Up @@ -601,6 +602,7 @@
"-ObjC",
"-lc++",
);
PRODUCT_BUNDLE_IDENTIFIER = id.vouched.rndemo;
PRODUCT_NAME = example;
SWIFT_VERSION = 5.0;
VERSIONING_SYSTEM = "apple-generic";
Expand Down
2 changes: 1 addition & 1 deletion example/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
"@react-native-community/masked-view": "^0.1.10",
"@react-navigation/native": "^5.9.2",
"@react-navigation/stack": "^5.14.2",
"@vouched.id/vouched-react-native": "0.6.0",
"@vouched.id/vouched-react-native": "1.0.0",
"patch-package": "^6.4.7",
"react": "16.13.0",
"react-native": "0.67.2",
Expand Down
20 changes: 20 additions & 0 deletions example/patches/react-native+0.67.2.patch
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
diff --git a/node_modules/react-native/React/CoreModules/RCTActionSheetManager.mm b/node_modules/react-native/React/CoreModules/RCTActionSheetManager.mm
index 5cd782e..15d33e2 100644
--- a/node_modules/react-native/React/CoreModules/RCTActionSheetManager.mm
+++ b/node_modules/react-native/React/CoreModules/RCTActionSheetManager.mm
@@ -95,7 +95,7 @@ - (void)presentViewController:(UIViewController *)alertController
NSNumber *anchor = [RCTConvert NSNumber:options.anchor() ? @(*options.anchor()) : nil];
UIColor *tintColor = [RCTConvert UIColor:options.tintColor() ? @(*options.tintColor()) : nil];
UIColor *cancelButtonTintColor =
- [RCTConvert UIColor:options.cancelButtonTintColor() ? @(*options.cancelButtonTintColor()) : nil];
+ [RCTConvert UIColor:options.tintColor() ? @(*options.tintColor()) : nil];

if (controller == nil) {
RCTLogError(
diff --git a/node_modules/react-native/scripts/.packager.env b/node_modules/react-native/scripts/.packager.env
new file mode 100644
index 0000000..361f5fb
--- /dev/null
+++ b/node_modules/react-native/scripts/.packager.env
@@ -0,0 +1 @@
+export RCT_METRO_PORT=8081
2 changes: 1 addition & 1 deletion example/src/components/AuthInputScreen.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ const AuthInputScreen = ({ navigation, route }) => {
return (
<View style={styles.container}>
<View style={styles.inputContainer}>
<Text style={{ color: 'white' }}> Enter the Vouched job id to authenticate </Text>
<Text style={{ color: 'white' }}> Enter the Vouched job id to re-verify </Text>
<TextInput
style={styles.input}
autoCapitalize='none'
Expand Down
13 changes: 6 additions & 7 deletions example/src/components/AuthScreen.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,16 +27,15 @@ const AuthScreen = ({ navigation, route }) => {
cameraRef.current.stop();
setMessage("Processing");
try {
const authResult = await session.postAuthenticate(faceDetectionResult, jobId, matchId);
if (authResult.match < 0.9) {
setMessage("Unable to Authenticate. Please try again");
setShowTryAgain(true);
let authResult = await session.postReverify(faceDetectionResult, jobId);
if (authResult.result.success == true) {
setMessage("Successfully reverified. Please continue to next step");
} else {
setMessage("Authenticated. Please continue to next step");
setMessage("Unable to Reverify. Please try again");
setShowTryAgain(true);
}
} catch (e) {
console.error(e)
setMessage("Unable to Authenticate. Please try again");
setMessage("Unable to Reverify. Please check the job ID and try again");
setShowTryAgain(true);
}
} else {
Expand Down
92 changes: 92 additions & 0 deletions example/src/components/BackIDScreen.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
import React, { useState, useRef, useEffect } from 'react';
import { Button } from 'react-native';
import { StyleSheet, View } from 'react-native';
import Footer from 'components/Footer';
import { VouchedIdCamera, VouchedUtils } from '@vouched.id/vouched-react-native';
import { getSession} from '../common/session'

const BackIDScreen = ({ navigation, route }) => {
const cameraRef = useRef(null);
const [message, setMessage] = useState('loading...');
const [showNextButton, setShowNextButton] = useState(false);
const [session] = useState(getSession());
const [params] = useState({});

const messageByInsight = (insight) => {
switch (insight) {
case "NON_GLARE":
return "image has glare";
case "QUALITY":
return "image is blurry";
case "BRIGHTNESS":
return "image needs to be brighter";
case "FACE":
return "image is missing required visual markers";
case "GLASSES":
return "please take off your glasses";
case "UNKNOWN":
default:
return "Unknown Error";
}
}

return (
<View style={styles.container}>
<View style={styles.camera}>
<VouchedIdCamera
ref={cameraRef}
enableDistanceCheck={false}
onIdStream={async (cardDetectionResult) => {
const { instruction, step } = cardDetectionResult;

if (step === "POSTABLE") {
cameraRef.current.stop();
setMessage("Processing");
try {
let job = await session.postBackId(cardDetectionResult, params);
let insights = await VouchedUtils.extractInsights(job);
if (insights != null && insights.length > 0) {
setMessage(messageByInsight(insights[0]));
setTimeout(() => {
cameraRef.current.restart();
}, 5000);
} else {
setMessage("Please continue to next step");
setShowNextButton(true);
}
} catch (e) {
console.error(e)
}
} else {
setMessage(instruction)
}
}}
/>
</View>
{ showNextButton &&
<View style={styles.nextButton}>
<Button title="Next Step: Face" onPress={() => navigation.navigate('Face')} />
</View>
}
<Footer message={message} showHome={true} navigation={navigation} />
</View>
);
};

const styles = StyleSheet.create({
camera: {
flex: 3,
flexDirection: 'column'
},
nextButton: {
flex: 1,
flexDirection: 'column'
},
container: {
flex: 1,
justifyContent: 'center',
flexDirection: 'column'
}
});

export default BackIDScreen;
4 changes: 2 additions & 2 deletions example/src/components/HomeScreen.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ const HomeScreen = ({ navigation }) => {
const [hasCameraPermissions, setPermissions] = useState(undefined);

useEffect(() => {
// assume all iOS users except permissions
// assume all iOS users accept permissions
if (Platform.OS === 'ios') {
setPermissions(true);
return;
Expand Down Expand Up @@ -43,7 +43,7 @@ const [hasCameraPermissions, setPermissions] = useState(undefined);
<View style={styles.startButton}>
<Button
disabled={!hasCameraPermissions}
title="Start Authentication"
title="Start Re-verification"
onPress={() => navigation.navigate('AuthInput')}
/>
</View>
Expand Down
Loading

0 comments on commit be47d99

Please sign in to comment.