Skip to content

Commit

Permalink
Merge pull request #250 from idpass/feat/187-use-real-sdk
Browse files Browse the repository at this point in the history
feat: use real face-matching SDK
  • Loading branch information
pmigueld authored Nov 1, 2022
2 parents 0bc7a53 + c83731a commit 7655316
Show file tree
Hide file tree
Showing 33 changed files with 886 additions and 385 deletions.
1 change: 1 addition & 0 deletions components/FaceScanner.strings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{}
140 changes: 140 additions & 0 deletions components/FaceScanner.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
import React, { useCallback, useContext, useEffect, useRef } from 'react';
import Icon from 'react-native-vector-icons/MaterialIcons';
import { Camera } from 'expo-camera';
import { StyleSheet } from 'react-native';
import { Colors } from './ui/styleUtils';
import { Button, Centered, Column, Row, Text } from './ui';
import { useInterpret, useSelector } from '@xstate/react';
import { useTranslation } from 'react-i18next';
import {
FaceScannerEvents,
selectIsCheckingPermission,
selectIsValid,
selectIsPermissionDenied,
selectIsScanning,
selectWhichCamera,
createFaceScannerMachine,
selectIsInvalid,
selectIsCapturing,
selectIsVerifying,
} from '../machines/faceScanner';
import { GlobalContext } from '../shared/GlobalContext';
import { selectIsActive } from '../machines/app';
import { RotatingIcon } from './RotatingIcon';

export const FaceScanner: React.FC<FaceScannerProps> = (props) => {
const { t } = useTranslation('FaceScanner');
const { appService } = useContext(GlobalContext);
const isActive = useSelector(appService, selectIsActive);

const machine = useRef(createFaceScannerMachine(props.vcImage));
const service = useInterpret(machine.current);

const whichCamera = useSelector(service, selectWhichCamera);

const isPermissionDenied = useSelector(service, selectIsPermissionDenied);
const isValid = useSelector(service, selectIsValid);
const isInvalid = useSelector(service, selectIsInvalid);
const isCheckingPermission = useSelector(service, selectIsCheckingPermission);
const isScanning = useSelector(service, selectIsScanning);
const isCapturing = useSelector(service, selectIsCapturing);
const isVerifying = useSelector(service, selectIsVerifying);

const setCameraRef = useCallback(
(node: Camera) => {
if (node != null && !isScanning) {
service.send(FaceScannerEvents.READY(node));
}
},
[isScanning]
);

useEffect(() => {
if (isValid) {
props.onValid();
} else if (isInvalid) {
props.onInvalid();
}
}, [isValid, isInvalid]);

useEffect(() => {
if (isActive) {
service.send(FaceScannerEvents.APP_FOCUSED());
}
}, [isActive]);

if (isCheckingPermission) {
return <Column></Column>;
} else if (isPermissionDenied) {
return (
<Column padding="24" fill align="space-between">
<Text align="center" color={Colors.Red}>
{t('missingPermissionText')}
</Text>
<Button
title={t('allowCameraButton')}
onPress={() => service.send(FaceScannerEvents.OPEN_SETTINGS())}
/>
</Column>
);
}

return (
<Column crossAlign="center">
<Column style={[styles.scannerContainer]}>
<Camera
ratio="4:3"
style={styles.scanner}
type={whichCamera}
ref={setCameraRef}
/>
</Column>
<Centered margin="24 0">
{isCapturing || isVerifying ? (
<RotatingIcon name="sync" size={64} />
) : (
<Row crossAlign="center">
<Icon
name="flip-camera-ios"
color={Colors.Black}
size={64}
onPress={() => service.send(FaceScannerEvents.FLIP_CAMERA())}
style={{ margin: 8, marginEnd: 32 }}
/>
<Icon
name="photo-camera"
color={Colors.Black}
size={64}
onPress={() => service.send(FaceScannerEvents.CAPTURE())}
style={{ margin: 8, marginTop: 12, marginStart: 32 }}
/>
</Row>
)}
</Centered>
</Column>
);
};

interface FaceScannerProps {
vcImage: string;
onValid: () => void;
onInvalid: () => void;
}

const styles = StyleSheet.create({
scannerContainer: {
borderWidth: 4,
borderColor: Colors.Black,
borderRadius: 32,
justifyContent: 'center',
height: 400,
width: 300,
overflow: 'hidden',
},

scanner: {
height: '100%',
width: '100%',
margin: 'auto',
},
});
4 changes: 4 additions & 0 deletions components/VcDetails.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,10 @@ const VerifiedIcon: React.FC = () => {
export const VcDetails: React.FC<VcDetailsProps> = (props) => {
const { t, i18n } = useTranslation('VcDetails');

if (props.vc?.verifiableCredential == null) {
return <Text align="center">Loading details...</Text>;
}

return (
<Column>
<Row pY={16} pX={8} align="space-between">
Expand Down
1 change: 1 addition & 0 deletions components/VcItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import { Column, Row, Text } from './ui';
import { Colors } from './ui/styleUtils';
import { RotatingIcon } from './RotatingIcon';
import { GlobalContext } from '../shared/GlobalContext';
import { LocalizedField } from '../types/vc';

const styles = StyleSheet.create({
title: {
Expand Down
6 changes: 6 additions & 0 deletions ios/Podfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,8 @@ PODS:
- GoogleSymbolUtilities (1.1.2)
- GoogleUtilitiesLegacy (1.3.2):
- GoogleSymbolUtilities (~> 1.1)
- mosip-inji-face-sdk (0.1.1):
- React-Core
- NearbyMessages (1.1.1):
- GoogleInterchangeUtilities (~> 1.2)
- GoogleNetworkingUtilities (~> 1.2)
Expand Down Expand Up @@ -393,6 +395,7 @@ DEPENDENCIES:
- FBLazyVector (from `../node_modules/react-native/Libraries/FBLazyVector`)
- FBReactNativeSpec (from `../node_modules/react-native/React/FBReactNativeSpec`)
- glog (from `../node_modules/react-native/third-party-podspecs/glog.podspec`)
- mosip-inji-face-sdk (from `../node_modules/mosip-inji-face-sdk`)
- Permission-BluetoothPeripheral (from `../node_modules/react-native-permissions/ios/BluetoothPeripheral`)
- Permission-Camera (from `../node_modules/react-native-permissions/ios/Camera`)
- Permission-LocationAccuracy (from `../node_modules/react-native-permissions/ios/LocationAccuracy`)
Expand Down Expand Up @@ -495,6 +498,8 @@ EXTERNAL SOURCES:
:path: "../node_modules/react-native/React/FBReactNativeSpec"
glog:
:podspec: "../node_modules/react-native/third-party-podspecs/glog.podspec"
mosip-inji-face-sdk:
:path: "../node_modules/mosip-inji-face-sdk"
Permission-BluetoothPeripheral:
:path: "../node_modules/react-native-permissions/ios/BluetoothPeripheral"
Permission-Camera:
Expand Down Expand Up @@ -609,6 +614,7 @@ SPEC CHECKSUMS:
GoogleNetworkingUtilities: 3edd3a8161347494f2da60ea0deddc8a472d94cb
GoogleSymbolUtilities: 631ee17048aa5e9ab133470d768ea997a5ef9b96
GoogleUtilitiesLegacy: 5501bedec1646bd284286eb5fc9453f7e23a12f4
mosip-inji-face-sdk: f0e765373b50324243d904e45eb3ce899db951ac
NearbyMessages: bd9e88f2df7fbab78be58fed58580d5d5bd62cbf
Permission-BluetoothPeripheral: 2a5154a9dfdb1cfcf1d546650ced9671904a02af
Permission-Camera: 0a0fb4341f50ab242f496fb2f73380e0ec454fe7
Expand Down
4 changes: 4 additions & 0 deletions lib/mosip-inji-face-sdk/faceAuth.ios.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export default async function faceAuth(capturedImage: string, vcImage: string) {
// TODO: iOS implementation
return Promise.resolve(true);
}
3 changes: 3 additions & 0 deletions lib/mosip-inji-face-sdk/faceAuth.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { faceAuth } from 'mosip-inji-face-sdk';

export default faceAuth;
16 changes: 14 additions & 2 deletions locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
"deviceRefNumber": "Device reference number",
"name": "Name"
},
"FaceScanner": {},
"OIDcAuth": {
"title": "OIDC Authentication",
"text": "To be replaced with the OIDC provider UI",
Expand Down Expand Up @@ -153,6 +154,7 @@
"ReceiveVcScreen": {
"header": "{{vcLabel}} details",
"acceptRequest": "Accept request and receive {{vcLabel}}",
"acceptRequestAndVerify": "Accept request and verify",
"reject": "Reject"
},
"RequestScreen": {
Expand Down Expand Up @@ -231,7 +233,11 @@
"rejected": {
"title": "Notice",
"message": "Your {{vcLabel}} was rejected by {{receiver}}"
},
}
}
},
"VerifyIdentityOverlay": {
"status": {
"verifyingIdentity": "Verifying identity..."
},
"errors": {
Expand All @@ -250,6 +256,12 @@
"cancel": "Cancel",
"save": "Save",
"editLabel": "Edit {{label}}",
"tryAgain": "Try again"
"tryAgain": "Try again",
"camera": {
"errors": {
"missingPermission": "This app uses the camera to scan the QR code of another device."
},
"allowAccess": "Allow access to camera"
}
}
}
6 changes: 3 additions & 3 deletions machines/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import {
getDeviceName,
getDeviceNameSync,
} from 'react-native-device-info';
import { EventFrom, spawn, StateFrom, send, assign } from 'xstate';
import { EventFrom, spawn, StateFrom, send, assign, AnyState } from 'xstate';
import { createModel } from 'xstate/lib/model';
import { authMachine, createAuthMachine } from './auth';
import { createSettingsMachine, settingsMachine } from './settings';
Expand Down Expand Up @@ -344,7 +344,7 @@ export function selectIsFocused(state: State) {
return state.matches('ready.focus');
}

export function logState(state) {
export function logState(state: AnyState) {
const data = JSON.stringify(
state.event,
(key, value) => {
Expand All @@ -360,7 +360,7 @@ export function logState(state) {
`[${getDeviceNameSync()}] ${state.machine.id}: ${
state.event.type
} -> ${state.toStrings().pop()}\n${
data.length > 1000 ? data.slice(0, 1000) + '...' : data
data.length > 300 ? data.slice(0, 300) + '...' : data
}
`
);
Expand Down
10 changes: 4 additions & 6 deletions machines/auth.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { ContextFrom, EventFrom, send, StateFrom } from 'xstate';
import { log } from 'xstate/lib/actions';
import { createModel } from 'xstate/lib/model';
import { AppServices } from '../shared/GlobalContext';
import { StoreEvents, StoreResponseEvent } from './store';
Expand All @@ -14,7 +13,7 @@ const model = createModel(
{
events: {
SETUP_PASSCODE: (passcode: string) => ({ passcode }),
SETUP_BIOMETRICS: (biometrics: string) => ({ biometrics }),
SETUP_BIOMETRICS: (biometrics: string) => ({ biometrics }),
LOGOUT: () => ({}),
LOGIN: () => ({}),
STORE_RESPONSE: (response?: unknown) => ({ response }),
Expand All @@ -24,7 +23,6 @@ const model = createModel(

export const AuthEvents = model.events;


type SetupBiometricsEvent = EventFrom<typeof model, 'SETUP_BIOMETRICS'>;

export const authMachine = model.createMachine(
Expand Down Expand Up @@ -125,11 +123,11 @@ export const authMachine = model.createMachine(
hasData: (_, event: StoreResponseEvent) => event.response != null,

hasPasscodeSet: (context) => {
return context.passcode !== ''
return context.passcode !== '';
},
hasBiometricSet: (context) => {
return context.biometrics !== ''
}
return context.biometrics !== '';
},
},
}
);
Expand Down
Loading

0 comments on commit 7655316

Please sign in to comment.