Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add face validation when sharing VC #237

Merged
merged 1 commit into from
Oct 10, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions components/MessageOverlay.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,14 +39,15 @@ export const MessageOverlay: React.FC<MessageOverlayProps> = (props) => {
{props.hint}
</Text>
)}
{props.children}
</Column>
{props.onCancel && (
{!props.children && props.onCancel ? (
<Button
title={t('cancel')}
onPress={props.onCancel}
styles={styles.button}
/>
)}
) : null}
</Column>
</Overlay>
);
Expand Down
9 changes: 2 additions & 7 deletions components/VcDetails.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import * as DateFnsLocale from '../lib/date-fns/locale';
import { useTranslation } from 'react-i18next';
import { Image } from 'react-native';
import { Icon, ListItem } from 'react-native-elements';
import { VC, CredentialSubject } from '../types/vc';
import { VC, CredentialSubject, LocalizedField } from '../types/vc';
import { Column, Row, Text } from './ui';
import { Colors } from './ui/styleUtils';
import { TextItem } from './ui/TextItem';
Expand Down Expand Up @@ -149,11 +149,6 @@ interface VcDetailsProps {
vc: VC;
}

interface LocalizedField {
language: string;
value: string;
}

function getFullAddress(credential: CredentialSubject) {
if (!credential) {
return '';
Expand All @@ -175,7 +170,7 @@ function getFullAddress(credential: CredentialSubject) {
.join(', ');
}

function getLocalizedField(rawField: string | LocalizedField) {
function getLocalizedField(rawField: string | LocalizedField[]) {
if (typeof rawField === 'string') {
return rawField;
}
Expand Down
47 changes: 2 additions & 45 deletions ios/MOSIPResidentApp.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -250,52 +250,9 @@
files = (
);
inputPaths = (
"${PODS_ROOT}/Target Support Files/Pods-MOSIPResidentApp/Pods-MOSIPResidentApp-resources.sh",
"${PODS_CONFIGURATION_BUILD_DIR}/EXConstants/EXConstants.bundle",
"${PODS_CONFIGURATION_BUILD_DIR}/EXUpdates/EXUpdates.bundle",
"${PODS_ROOT}/NearbyMessages/Resources/resources/GNSSharedResources.bundle",
"${PODS_ROOT}/NearbyMessages/Resources/resources/ic_expand_more.xcassets",
"${PODS_ROOT}/NearbyMessages/Resources/resources/ic_nearby_48pt.xcassets",
"${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/AntDesign.ttf",
"${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/Entypo.ttf",
"${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/EvilIcons.ttf",
"${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/Feather.ttf",
"${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/FontAwesome.ttf",
"${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/FontAwesome5_Brands.ttf",
"${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/FontAwesome5_Regular.ttf",
"${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/FontAwesome5_Solid.ttf",
"${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/Fontisto.ttf",
"${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/Foundation.ttf",
"${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/Ionicons.ttf",
"${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/MaterialCommunityIcons.ttf",
"${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/MaterialIcons.ttf",
"${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/Octicons.ttf",
"${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/SimpleLineIcons.ttf",
"${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/Zocial.ttf",
"${PODS_CONFIGURATION_BUILD_DIR}/React-Core/AccessibilityResources.bundle",
);
name = "[CP] Copy Pods Resources";
outputPaths = (
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/EXConstants.bundle",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/EXUpdates.bundle",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/GNSSharedResources.bundle",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/AntDesign.ttf",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/Entypo.ttf",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/EvilIcons.ttf",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/Feather.ttf",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/FontAwesome.ttf",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/FontAwesome5_Brands.ttf",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/FontAwesome5_Regular.ttf",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/FontAwesome5_Solid.ttf",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/Fontisto.ttf",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/Foundation.ttf",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/Ionicons.ttf",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/MaterialCommunityIcons.ttf",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/MaterialIcons.ttf",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/Octicons.ttf",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/SimpleLineIcons.ttf",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/Zocial.ttf",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/AccessibilityResources.bundle",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
Expand Down Expand Up @@ -345,7 +302,7 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = MOSIPResidentApp/MOSIPResidentApp.entitlements;
CURRENT_PROJECT_VERSION = 8.0;
CURRENT_PROJECT_VERSION = 8.2;
DEVELOPMENT_TEAM = 9L83VVTX8B;
ENABLE_BITCODE = NO;
GCC_PREPROCESSOR_DEFINITIONS = (
Expand Down Expand Up @@ -378,7 +335,7 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = MOSIPResidentApp/MOSIPResidentApp.entitlements;
CURRENT_PROJECT_VERSION = 8.0;
CURRENT_PROJECT_VERSION = 8.2;
DEVELOPMENT_TEAM = 9L83VVTX8B;
INFOPLIST_FILE = MOSIPResidentApp/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
Expand Down
2 changes: 2 additions & 0 deletions ios/Podfile
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
install! 'cocoapods', :disable_input_output_paths => true

require File.join(File.dirname(`node --print "require.resolve('expo/package.json')"`), "scripts/autolinking")
require File.join(File.dirname(`node --print "require.resolve('react-native/package.json')"`), "scripts/react_native_pods")
require File.join(File.dirname(`node --print "require.resolve('@react-native-community/cli-platform-ios/package.json')"`), "native_modules")
Expand Down
2 changes: 1 addition & 1 deletion ios/Podfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -654,6 +654,6 @@ SPEC CHECKSUMS:
Yoga: e6ecf3fa25af9d4c87e94ad7d5d292eedef49749
ZXingObjC: fdbb269f25dd2032da343e06f10224d62f537bdb

PODFILE CHECKSUM: 6f59e2d150c75c5b66954a85de8e13faafad41d3
PODFILE CHECKSUM: 24c315d126fc2b5fef6a2828f16ec02b341154fe

COCOAPODS: 1.11.3
38 changes: 24 additions & 14 deletions locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -212,24 +212,33 @@
"SelectVcOverlay": {
"header": "Share {{vcLabel}}",
"chooseVc": "Choose the {{vcLabel}} you'd like to share with",
"cancel": "Cancel",
"share": "Share"
"share": "Share",
"verifyAndShare": "Verify Identity & Share"
},
"SendVcModal": {
"SendVcScreen": {
"reasonForSharing": "Reason for sharing (optional)",
"acceptRequest": "Accept request and choose {{vcLabel}}",
"reject": "Reject",
"statusSharing": {
"title": "Sharing...",
"timeoutHint": "It's taking a while to share VC. There could be a problem with the connection."
},
"statusAccepted": {
"title": "Success!",
"message": "Your {{vcLabel}} has been successfully shared with {{receiver}}"
"status": {
"sharing": {
"title": "Sharing...",
"timeoutHint": "It's taking a while to share VC. There could be a problem with the connection."
},
"accepted": {
"title": "Success!",
"message": "Your {{vcLabel}} has been successfully shared with {{receiver}}"
},
"rejected": {
"title": "Notice",
"message": "Your {{vcLabel}} was rejected by {{receiver}}"
},
"verifyingIdentity": "Verifying identity..."
},
"statusRejected": {
"title": "Notice",
"message": "Your {{vcLabel}} was rejected by {{receiver}}"
"errors": {
"invalidIdentity": {
"title": "Unable to verify identity",
"message": "An error occured and we couldn't scan your portrait. Try again, make sure your face is visible, devoid of any accessories."
}
}
},
"WelcomeScreen": {
Expand All @@ -240,6 +249,7 @@
"common": {
"cancel": "Cancel",
"save": "Save",
"editLabel": "Edit {{label}}"
"editLabel": "Edit {{label}}",
"tryAgain": "Try again"
}
}
50 changes: 45 additions & 5 deletions machines/scan.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { createModel } from 'xstate/lib/model';
import { EmitterSubscription, Linking, Platform } from 'react-native';
import { DeviceInfo } from '../components/DeviceInfoList';
import { getDeviceNameSync } from 'react-native-device-info';
import { VC } from '../types/vc';
import { VC, VerifiablePresentation } from '../types/vc';
import { AppServices } from '../shared/GlobalContext';
import { ActivityLogEvents } from './activityLog';
import {
Expand All @@ -27,6 +27,7 @@ import {
} from '../shared/smartshare';
import { check, PERMISSIONS, PermissionStatus } from 'react-native-permissions';
import { checkLocation, requestLocation } from '../shared/location';
import { CameraCapturedPicture } from 'expo-camera';

const findingConnectionId = '#scan.findingConnection';
const checkingLocationServiceId = '#checkingLocationService';
Expand All @@ -44,6 +45,7 @@ const model = createModel(
reason: '',
loggers: [] as EmitterSubscription[],
vcName: '',
verificationImage: {} as CameraCapturedPicture,
sharingProtocol: 'OFFLINE' as SharingProtocol,
scannedQrParams: {} as ConnectionParams,
},
Expand All @@ -69,6 +71,11 @@ const model = createModel(
UPDATE_VC_NAME: (vcName: string) => ({ vcName }),
STORE_RESPONSE: (response: unknown) => ({ response }),
APP_ACTIVE: () => ({}),
VERIFY_AND_SELECT_VC: (vc: VC) => ({ vc }),
FACE_VALID: () => ({}),
FACE_INVALID: () => ({}),
RETRY_VERIFICATION: () => ({}),
VP_CREATED: (vp: VerifiablePresentation) => ({ vp }),
},
}
);
Expand Down Expand Up @@ -252,6 +259,10 @@ export const scanMachine = model.createMachine(
target: 'sendingVc',
actions: ['setSelectedVc'],
},
VERIFY_AND_SELECT_VC: {
target: 'verifyingUserIdentity',
actions: ['setSelectedVc'],
},
CANCEL: 'idle',
},
},
Expand Down Expand Up @@ -291,6 +302,23 @@ export const scanMachine = model.createMachine(
rejected: {},
cancelled: {},
navigatingToHome: {},
verifyingUserIdentity: {
on: {
FACE_VALID: {
target: 'sendingVc',
},
FACE_INVALID: {
target: 'invalidUserIdentity',
},
CANCEL: 'selectingVc',
},
},
invalidUserIdentity: {
on: {
DISMISS: 'selectingVc',
RETRY_VERIFICATION: 'verifyingUserIdentity',
},
},
},
exit: ['disconnect', 'clearReason'],
},
Expand Down Expand Up @@ -531,7 +559,6 @@ export const scanMachine = model.createMachine(
};

if (context.sharingProtocol === 'OFFLINE') {
console.log('OFFLINE?!');
const event: SendVcEvent = {
type: 'send-vc',
data: { isChunked: false, vc },
Expand Down Expand Up @@ -600,6 +627,10 @@ export function selectVcName(state: State) {
return state.context.vcName;
}

export function selectSelectedVc(state: State) {
return state.context.selectedVc;
}

export function selectIsScanning(state: State) {
return state.matches('findingConnection');
}
Expand Down Expand Up @@ -656,11 +687,22 @@ export function selectIsLocationDisabled(state: State) {
return state.matches('checkingLocationService.disabled');
}

export function selectIsDone(state: State) {
return state.matches('reviewing.navigatingToHome');
}

export function selectIsVerifyingUserIdentity(state: State) {
return state.matches('reviewing.verifyingUserIdentity');
}

export function selectIsInvalidUserIdentity(state: State) {
return state.matches('reviewing.invalidUserIdentity');
}

async function sendVc(vc: VC, callback: (status: SendVcStatus) => void) {
const rawData = JSON.stringify(vc);
const chunks = chunkString(rawData, GNM_MESSAGE_LIMIT);
if (chunks.length > 1) {
console.log('CHUNKED!', chunks.length);
let chunk = 0;
const vcChunk = {
total: chunks.length,
Expand All @@ -679,7 +721,6 @@ async function sendVc(vc: VC, callback: (status: SendVcStatus) => void) {
SendVcResponseType,
async (status) => {
if (typeof status === 'number' && chunk < event.data.vcChunk.total) {
console.log(SendVcResponseType, chunk, chunks[chunk].length);
chunk += 1;
await GoogleNearbyMessages.unpublish();
await onlineSend({
Expand All @@ -702,7 +743,6 @@ async function sendVc(vc: VC, callback: (status: SendVcStatus) => void) {
);
await onlineSend(event);
} else {
console.log('UNCHUNKED');
const event: SendVcEvent = {
type: 'send-vc',
data: { isChunked: false, vc },
Expand Down
14 changes: 11 additions & 3 deletions machines/scan.typegen.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ export interface Typegen0 {
setReason: 'UPDATE_REASON';
setReceiverInfo: 'EXCHANGE_DONE';
setScannedQrParams: 'SCAN';
setSelectedVc: 'SELECT_VC';
setSelectedVc: 'SELECT_VC' | 'VERIFY_AND_SELECT_VC';
setSenderInfo: 'RECEIVE_DEVICE_INFO';
};
'eventsCausingServices': {
Expand All @@ -74,15 +74,19 @@ export interface Typegen0 {
discoverDevice: 'RECEIVE_DEVICE_INFO';
exchangeDeviceInfo: 'CONNECTED';
monitorConnection: 'SCREEN_BLUR' | 'SCREEN_FOCUS' | 'xstate.init';
sendVc: 'SELECT_VC';
sendVc: 'FACE_VALID' | 'SELECT_VC';
};
'eventsCausingGuards': {
isQrOffline: 'SCAN';
isQrOnline: 'SCAN';
};
'eventsCausingDelays': {
CLEAR_DELAY: 'LOCATION_ENABLED';
CONNECTION_TIMEOUT: 'CONNECTED' | 'RECEIVE_DEVICE_INFO' | 'SELECT_VC';
CONNECTION_TIMEOUT:
| 'CONNECTED'
| 'FACE_VALID'
| 'RECEIVE_DEVICE_INFO'
| 'SELECT_VC';
};
'matchesStates':
| 'checkingLocationService'
Expand All @@ -107,12 +111,14 @@ export interface Typegen0 {
| 'reviewing.accepted'
| 'reviewing.cancelled'
| 'reviewing.idle'
| 'reviewing.invalidUserIdentity'
| 'reviewing.navigatingToHome'
| 'reviewing.rejected'
| 'reviewing.selectingVc'
| 'reviewing.sendingVc'
| 'reviewing.sendingVc.inProgress'
| 'reviewing.sendingVc.timeout'
| 'reviewing.verifyingUserIdentity'
| {
checkingLocationService?:
| 'checkingPermission'
Expand All @@ -126,10 +132,12 @@ export interface Typegen0 {
| 'accepted'
| 'cancelled'
| 'idle'
| 'invalidUserIdentity'
| 'navigatingToHome'
| 'rejected'
| 'selectingVc'
| 'sendingVc'
| 'verifyingUserIdentity'
| { sendingVc?: 'inProgress' | 'timeout' };
};
'tags': never;
Expand Down
2 changes: 1 addition & 1 deletion machines/vcItem.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import {
} from '../types/vc';
import { StoreEvents } from './store';
import { ActivityLogEvents } from './activityLog';
import { verifyCredential } from '../shared/verifyCredential';
import { verifyCredential } from '../shared/vcjs/verifyCredential';
import { log } from 'xstate/lib/actions';

const model = createModel(
Expand Down
Loading