Skip to content

Commit

Permalink
Added biometric login for ios
Browse files Browse the repository at this point in the history
  • Loading branch information
suleymanCel committed Feb 13, 2023
1 parent ec28161 commit 150c904
Show file tree
Hide file tree
Showing 21 changed files with 431 additions and 41 deletions.
12 changes: 12 additions & 0 deletions ios/Podfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,15 @@ PODS:
- React-Core
- EXKeepAwake (10.0.2):
- ExpoModulesCore
- EXLocalAuthentication (12.1.1):
- ExpoModulesCore
- Expo (44.0.6):
- ExpoModulesCore
- ExpoModulesCore (0.6.5):
- React-Core
- ReactCommon/turbomodule/core
- EXSecureStore (11.1.1):
- ExpoModulesCore
- EXSplashScreen (0.14.2):
- ExpoModulesCore
- React-Core
Expand Down Expand Up @@ -423,8 +427,10 @@ DEPENDENCIES:
- EXFont (from `../node_modules/expo-font/ios`)
- EXImageLoader (from `../node_modules/expo-image-loader/ios`)
- EXKeepAwake (from `../node_modules/expo-keep-awake/ios`)
- EXLocalAuthentication (from `../node_modules/expo-local-authentication/ios`)
- Expo (from `../node_modules/expo/ios`)
- ExpoModulesCore (from `../node_modules/expo-modules-core/ios`)
- EXSecureStore (from `../node_modules/expo-secure-store/ios`)
- EXSplashScreen (from `../node_modules/expo-splash-screen/ios`)
- FBLazyVector (from `../node_modules/react-native/Libraries/FBLazyVector`)
- FBReactNativeSpec (from `../node_modules/react-native/React/FBReactNativeSpec`)
Expand Down Expand Up @@ -535,10 +541,14 @@ EXTERNAL SOURCES:
:path: "../node_modules/expo-image-loader/ios"
EXKeepAwake:
:path: "../node_modules/expo-keep-awake/ios"
EXLocalAuthentication:
:path: "../node_modules/expo-local-authentication/ios"
Expo:
:path: "../node_modules/expo/ios"
ExpoModulesCore:
:path: "../node_modules/expo-modules-core/ios"
EXSecureStore:
:path: "../node_modules/expo-secure-store/ios"
EXSplashScreen:
:path: "../node_modules/expo-splash-screen/ios"
FBLazyVector:
Expand Down Expand Up @@ -643,8 +653,10 @@ SPEC CHECKSUMS:
EXFont: 2597c10ac85a69d348d44d7873eccf5a7576ef5e
EXImageLoader: 347b72c2ec2df65120ccec40ea65a4c4f24317ff
EXKeepAwake: bf48d7f740a5cd2befed6cf9a49911d385c6c47d
EXLocalAuthentication: 3c5f368ee954b79c3778158eb8000cbce4e2f8a2
Expo: 534e51e607aba8229293297da5585f4b26f50fa1
ExpoModulesCore: 32c0ccb47f477d330ee93db72505380adf0de09a
EXSecureStore: b80c74c5ee29d0160c2aace3fd9a24a5edc20015
EXSplashScreen: 21669e598804ee810547dbb6692c8deb5dd8dbf3
FBLazyVector: e5569e42a1c79ca00521846c223173a57aca1fe1
FBReactNativeSpec: fe08c1cd7e2e205718d77ad14b34957cce949b58
Expand Down
2 changes: 2 additions & 0 deletions ios/TeliosMobile/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -65,5 +65,7 @@
</array>
<key>UIViewControllerBasedStatusBarAppearance</key>
<false/>
<key>NSFaceIDUsageDescription</key>
<string>Allow $(PRODUCT_NAME) to use FaceID</string>
</dict>
</plist>
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@
"expo-barcode-scanner": "~11.2.0",
"expo-clipboard": "~2.1.0",
"expo-constants": "~13.0.1",
"expo-local-authentication": "~12.1.0",
"expo-secure-store": "~11.1.0",
"formik": "^2.2.9",
"hypercore": "^10.0.0-alpha.18",
"lodash": "^4.17.21",
Expand Down
38 changes: 38 additions & 0 deletions src/hooks/useFirstLogin.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { useEffect } from 'react';
import { Alert } from 'react-native';
import { useSelector } from 'react-redux';
import { useNavigation } from '@react-navigation/native';
import { selectIsFirstSignIn } from '../store/selectors/account';
import { isBiometricSupported } from '../util/biometric';

export default () => {
const isFirstSignIn = useSelector(selectIsFirstSignIn);
const navigation = useNavigation<any>();

useEffect(() => {
(async () => {
const isBiometricSupport = await isBiometricSupported();
if (isBiometricSupport && isFirstSignIn) {
Alert.alert(
'Login with Biometric',
'Would you want to use FaceID for login?',
[
{
text: 'No',
style: 'default',
},
{
text: 'Yes',
onPress: () => handleUseFaceID(),
style: 'default',
},
],
);
}
})();
}, [isFirstSignIn]);

const handleUseFaceID = () => {
navigation.navigate('biometricSettings');
};
};
16 changes: 16 additions & 0 deletions src/navigators/Navigator.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ import { AliasInfoScreen } from '../screens/AliasInfo';
import NewAliasRandom from '../screens/NewAliasRandom';
import ForgotPassword, { ForgotPasswordStackParams } from './ForgotPassword';
import { ProfileRoot } from './Profile';
import BiometricSettings from '../screens/BiometricSettings/BiometricSettings';
import Sync, { SyncStackParams } from './Sync';
import backArrow from './utils/backArrow';
import { selectIsSignedIn } from '../store/selectors/account';
Expand Down Expand Up @@ -82,6 +83,7 @@ export type RootStackParams = {
folderId: number;
isUnread: boolean;
};
biometricSettings: undefined;
};

export type RegisterStackParams = {
Expand Down Expand Up @@ -115,6 +117,7 @@ export type ProfileStackParams = {
statistics: undefined;
syncNewDevice: undefined;
security: undefined;
biometricSettings: undefined;
planAndUsage: undefined;
};

Expand Down Expand Up @@ -267,6 +270,19 @@ function CoreScreen() {
})}
/>
</Stack.Group>
<Stack.Screen
name="biometricSettings"
component={BiometricSettings}
options={({ navigation }) => ({
title: 'Biometric Settings',
headerLeft: () => (
<NavIconButton
icon={{ name: 'chevron-back', size: 28 }}
onPress={() => navigation.goBack()}
/>
),
})}
/>
</>
) : (
<>
Expand Down
22 changes: 17 additions & 5 deletions src/navigators/Profile.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import { NewContact } from '../screens/NewContact/NewContact';
import Security from '../screens/Security/Security';
import { ContactScreen } from '../screens/Contacts/Contacts';
import PlanAndUsage from '../screens/PlanAndUsage/PlanAndUsage';
import backArrow from './utils/backArrow';
import { colors } from '../util/colors';

export const ProfileStack = createNativeStackNavigator<ProfileStackParams>();
export const ProfileRoot = () => (
Expand All @@ -24,10 +26,11 @@ export const ProfileRoot = () => (
<ProfileStack.Screen
name={'contacts'}
component={ContactScreen}
options={{
options={({ navigation }) => ({
title: '',
...backArrow({ navigation, color: colors.primaryDark }),
headerTransparent: true,
}}
})}
/>
<ProfileStack.Screen
name={'contactDetail'}
Expand All @@ -51,17 +54,26 @@ export const ProfileRoot = () => (
<ProfileStack.Screen
name={'planAndUsage'}
component={PlanAndUsage}
options={{ title: 'Plan & Usage' }}
options={({ navigation }) => ({
title: 'Plan & Usage',
...backArrow({ navigation, color: colors.primaryDark }),
})}
/>
<ProfileStack.Screen
name={'security'}
component={Security}
options={{ title: 'Security' }}
options={({ navigation }) => ({
title: 'Security',
...backArrow({ navigation, color: colors.primaryDark }),
})}
/>
<ProfileStack.Screen
name={'syncNewDevice'}
component={SyncNewDevice}
options={{ title: 'Sync New Device' }}
options={({ navigation }) => ({
title: 'Sync New Device',
...backArrow({ navigation, color: colors.primaryDark }),
})}
/>
</ProfileStack.Navigator>
);
95 changes: 95 additions & 0 deletions src/screens/BiometricSettings/BiometricSettings.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
import React, { useEffect, useLayoutEffect, useState } from 'react';
import { Switch, Text, View } from 'react-native';
import * as LocalAuthentication from 'expo-local-authentication';
import cloneDeep from 'lodash/cloneDeep';
import backArrow from '../../navigators/utils/backArrow';
import { updateBiometricUseStatus } from '../../store/account';
import {
selectBiometricUseStatus,
selectLastLoggedUsername,
} from '../../store/selectors/account';
import { useAppDispatch, useAppSelector } from '../../hooks';
import { storeAsyncStorageBiometricUseStatus } from '../../util/asyncStorage';
import { isBiometricSupported } from '../../util/biometric';
import { colors } from '../../util/colors';
import styles from './styles';

const BiometricSettings = ({ navigation }) => {
const [isBiometricSupport, setIsBiometricSupport] = useState<boolean>(false);
const [usingStatus, setUsingStatus] = useState<boolean>(false);
const dispatch = useAppDispatch();
const lastLoggedUsername = useAppSelector(selectLastLoggedUsername);
const biometricUseStatus = useAppSelector(selectBiometricUseStatus);

useLayoutEffect(() => {
navigation.setOptions({
...backArrow({
navigation: navigation,
color: colors.primaryDark,
}),
});
}, []);

useEffect(() => {
(async () => {
const isBiometricSupportResult = await isBiometricSupported();
setIsBiometricSupport(isBiometricSupportResult);
if (lastLoggedUsername) {
setUsingStatus(!!biometricUseStatus?.[lastLoggedUsername]);
}
})();
}, [biometricUseStatus, lastLoggedUsername]);

const handleUsingStatusChange = async (updatedStatus: boolean) => {
setUsingStatus(updatedStatus);
if ((isBiometricSupport && lastLoggedUsername) || 1) {
await LocalAuthentication.authenticateAsync({
promptMessage:
'To change Telios uses settings, you must have identity verification.',
cancelLabel: 'Cancel',
disableDeviceFallback: false,
}).then(async res => {
if (res?.success && lastLoggedUsername) {
const newBiometricUseStatus = cloneDeep(biometricUseStatus);
newBiometricUseStatus[lastLoggedUsername] = updatedStatus;
dispatch(updateBiometricUseStatus(newBiometricUseStatus));
await storeAsyncStorageBiometricUseStatus(
lastLoggedUsername,
updatedStatus,
);
}
});
}
};

return (
<View style={styles.container}>
<View style={styles.settingContainer}>
<View>
<Text style={styles.settingText}>Using status</Text>
{!isBiometricSupport && (
<Text style={styles.errorText}>
{'Your device does not include this feature.'}
</Text>
)}
</View>
<Switch
value={usingStatus}
trackColor={{
false: colors.inkLighter,
true: colors.primaryBase,
}}
disabled={!isBiometricSupport}
onValueChange={handleUsingStatusChange}
/>
</View>
<Text style={styles.description}>
{
'Allows you to login to your Telios account with biometric verification. \n\nWhen you want to log in to your account after closing the Telios application, you must log in with biometric verification again. \n\n *Biometric verification allows you to log into your account using whatever security method is defined on your phone.'
}
</Text>
</View>
);
};

export default BiometricSettings;
31 changes: 31 additions & 0 deletions src/screens/BiometricSettings/styles.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { StyleSheet } from 'react-native';
import { colors } from '../../util/colors';
import { fonts } from '../../util/fonts';
import { spacing } from '../../util/spacing';

export default StyleSheet.create({
container: {
flex: 1,
padding: spacing.lg,
backgroundColor: colors.white,
},
settingContainer: {
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
paddingBottom: spacing.md,
borderBottomWidth: 1,
borderColor: colors.skyBase,
},
settingText: {
...fonts.large.bold,
},
description: {
...fonts.tiny.regular,
marginTop: spacing.md,
},
errorText: {
...fonts.tiny.regular,
color: colors.error,
},
});
2 changes: 2 additions & 0 deletions src/screens/Inbox/InboxScreen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import {
getMailByFolderUnread,
} from '../../store/thunks/email';
import ComposeButton from '../../components/ComposeButton/ComposeButton';
import useFirstLogin from '../../hooks/useFirstLogin';

export type InboxScreenProps = CompositeScreenProps<
NativeStackScreenProps<MainStackParams, 'inbox'>,
Expand All @@ -28,6 +29,7 @@ export const InboxScreen = () => {
const mailboxAddress = useAppSelector(selectMailBoxAddress);
const dispatch = useAppDispatch();
const folderId = FoldersId.inbox;
useFirstLogin();

return (
<>
Expand Down
Loading

0 comments on commit 150c904

Please sign in to comment.