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

Added biometric login for ios #120

Open
wants to merge 1 commit into
base: default
Choose a base branch
from
Open
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
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 { checkIsBiometricAvailable } from '../util/biometric';

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

useEffect(() => {
(async () => {
const isBiometricAvailable = await checkIsBiometricAvailable();
if ((isBiometricAvailable && isFirstSignIn) || 1) {
Alert.alert(
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we implement a custom modal with Telios styling not Alert?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I get the reference from our "Save Draft" alert.
-> But If you wish I can create a Modal as Telios styling and we can use where we need it instead of 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');
};
};
11 changes: 11 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,14 @@ function CoreScreen() {
})}
/>
</Stack.Group>
<Stack.Screen
name="biometricSettings"
component={BiometricSettings}
options={({ navigation }) => ({
title: 'Biometric Settings',
...backArrow({ navigation, color: colors.primaryDark }),
})}
/>
</>
) : (
<>
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>
);
86 changes: 86 additions & 0 deletions src/screens/BiometricSettings/BiometricSettings.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
import React, { useEffect, useState } from 'react';
import { Switch, Text, View } from 'react-native';
import * as LocalAuthentication from 'expo-local-authentication';
import cloneDeep from 'lodash/cloneDeep';
import { updateBiometricUseStatus } from '../../store/account';
import {
selectBiometricUseStatus,
selectLastLoggedUsername,
} from '../../store/selectors/account';
import { useAppDispatch, useAppSelector } from '../../hooks';
import { storeAsyncStorageBiometricUseStatus } from '../../util/asyncStorage';
import { checkIsBiometricAvailable } from '../../util/biometric';
import { colors } from '../../util/colors';
import styles from './styles';

const BiometricSettings = () => {
const [isBiometricAvailable, setIsBiometricAvailable] =
useState<boolean>(false);
const [usingStatus, setUsingStatus] = useState<boolean>(false);
suleymanCel marked this conversation as resolved.
Show resolved Hide resolved
const dispatch = useAppDispatch();
const lastLoggedUsername = useAppSelector(selectLastLoggedUsername);
const biometricUseStatus = useAppSelector(selectBiometricUseStatus);

useEffect(() => {
(async () => {
const isBiometricAvailableResult = await checkIsBiometricAvailable();
setIsBiometricAvailable(isBiometricAvailableResult);
if (lastLoggedUsername) {
setUsingStatus(!!biometricUseStatus?.[lastLoggedUsername]);
}
})();
}, [biometricUseStatus, lastLoggedUsername]);

const handleUsingStatusChange = async (updatedStatus: boolean) => {
setUsingStatus(updatedStatus);
if (isBiometricAvailable && lastLoggedUsername) {
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}>Use biometrics</Text>
{!isBiometricAvailable && (
<Text style={styles.errorText}>
{'Your device does not include this feature.'}
</Text>
)}
</View>
<Switch
value={usingStatus}
trackColor={{
false: colors.inkLighter,
true: colors.primaryBase,
}}
disabled={!isBiometricAvailable}
onValueChange={handleUsingStatusChange}
/>
</View>
<Text style={styles.description}>
{
'You will be able to login to your Telios account us gin TouchID or Face ID without entering the password'
}
</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