Skip to content

Commit 27f7457

Browse files
committed
basic biometric authentication setup
1 parent b73d890 commit 27f7457

File tree

5 files changed

+207
-10
lines changed

5 files changed

+207
-10
lines changed

.expo-shared/README.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
> Why do I have a folder named ".expo-shared" in my project?
2+
3+
The ".expo-shared" folder is created when running commands that produce state that is intended to be shared with all developers on the project. For example, "npx expo-optimize".
4+
5+
> What does the "assets.json" file contain?
6+
7+
The "assets.json" file describes the assets that have been optimized through "expo-optimize" and do not need to be processed again.
8+
9+
> Should I commit the ".expo-shared" folder?
10+
11+
Yes, you should share the ".expo-shared" folder with your collaborators.

App.js

Lines changed: 178 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,192 @@
11
import { StatusBar } from 'expo-status-bar';
2-
import React from 'react';
3-
import { StyleSheet, Text, View } from 'react-native';
2+
import React, { useState, useCallback } from 'react';
3+
import {
4+
StyleSheet,
5+
Text,
6+
TextInput,
7+
View,
8+
ScrollView,
9+
SafeAreaView,
10+
RefreshControl,
11+
Button,
12+
Image,
13+
TouchableOpacity,
14+
TouchableHighlight,
15+
Alert,
16+
BackHandler,
17+
Platform,
18+
Dimensions,
19+
} from 'react-native';
20+
import { useDeviceOrientation } from '@react-native-community/hooks';
21+
import * as LocalAuthentication from 'expo-local-authentication';
22+
23+
const wait = (timeout) => {
24+
return new Promise((resolve) => setTimeout(resolve, timeout));
25+
};
426

527
export default function App() {
28+
const [refreshing, setRefreshing] = useState(false);
29+
const { portrait, landscape } = useDeviceOrientation();
30+
31+
const onRefresh = useCallback(() => {
32+
setRefreshing(true);
33+
wait(2000).then(() => setRefreshing(false));
34+
}, []);
35+
36+
const onPressLearnMore = () => {
37+
alert('Learn More Pressed', Dimensions.get('screen'));
38+
};
39+
40+
const fallBackToDefaultAuth = () => {
41+
console.log('fall back to password authentication');
42+
};
43+
44+
const alertComponent = (title, mess, btnTxt, btnFunc) => {
45+
return Alert.alert(title, mess, [
46+
{
47+
text: btnTxt,
48+
onPress: btnFunc,
49+
},
50+
]);
51+
};
52+
53+
const handleBiometricAuth = async () => {
54+
// Check if hardware supports biometrics
55+
const isBiometricAvailable = await LocalAuthentication.hasHardwareAsync();
56+
57+
// Fallback to default authentication method (password) if Fingerprint is not available
58+
if (!isBiometricAvailable)
59+
return alertComponent(
60+
'Please enter your password',
61+
'Biometric Authentication not supported',
62+
'OK',
63+
() => fallBackToDefaultAuth()
64+
);
65+
66+
// Check Biometrics types available (Fingerprint, Facial recognition, Iris recognition)
67+
let supportedBiometrics;
68+
if (isBiometricAvailable)
69+
supportedBiometrics = await LocalAuthentication.supportedAuthenticationTypesAsync();
70+
71+
// Check Biometrics are saved locally in user's device
72+
const savedBiometrics = await LocalAuthentication.isEnrolledAsync();
73+
if (!savedBiometrics)
74+
return alertComponent(
75+
'Biometric not registered',
76+
'Please login with your password',
77+
'OK',
78+
() => fallBackToDefaultAuth()
79+
);
80+
81+
// promptMessage?: string;
82+
// cancelLabel?: string;
83+
// disableDeviceFallback?: boolean;
84+
// fallbackLabel ?: string;
85+
86+
// Authenticate use with Biometrics (Fingerprint, Facial recognition, Iris recognition)
87+
const biometricPrompt = await LocalAuthentication.authenticateAsync(
88+
'Touch the screen to'
89+
);
90+
91+
console.log({ isBiometricAvailable });
92+
console.log({ supportedBiometrics });
93+
console.log({ savedBiometrics });
94+
console.log({ biometricPrompt });
95+
};
96+
697
return (
7-
<View style={styles.container}>
8-
<Text>Open up App.js to start working on your app!</Text>
9-
<StatusBar style="auto" />
10-
</View>
98+
<SafeAreaView style={styles.container}>
99+
<ScrollView
100+
contentContainerStyle={styles.scrollView}
101+
refreshControl={
102+
<RefreshControl refreshing={refreshing} onRefresh={onRefresh} />
103+
}
104+
>
105+
<View style={styles.container}>
106+
{/* <Text
107+
accessibilityLabel="Welcome note"
108+
numberOfLines={1}
109+
onLongPress={() => alert('Text pressed')}
110+
>
111+
Lorem ipsum dolor, sit amet consectetur adipisicing elit. Unde
112+
perspiciatis nobis non voluptatibus tenetur fuga magni accusamus.
113+
Atque minima laboriosam
114+
</Text>
115+
<TouchableOpacity onPress={() => alert('Image pressed')}>
116+
<Image
117+
resizeMode="contain"
118+
loadingIndicatorSource={require('./assets/adaptive-icon.png')}
119+
fadeDuration={1299}
120+
source={{
121+
width: 200,
122+
height: 200,
123+
uri: 'https://picsum.photos/200',
124+
}}
125+
/>
126+
</TouchableOpacity>
127+
<TouchableHighlight
128+
underlayColor="white"
129+
onPress={() => alert('Image pressed')}
130+
>
131+
<Image
132+
resizeMode="contain"
133+
loadingIndicatorSource={require('./assets/adaptive-icon.png')}
134+
fadeDuration={1299}
135+
source={{
136+
width: 200,
137+
height: 200,
138+
uri: 'https://picsum.photos/200',
139+
}}
140+
/>
141+
</TouchableHighlight>
142+
<TextInput style={styles.textInput} placeholder="Type here" />
143+
<Button
144+
onPress={onPressLearnMore}
145+
title="Learn More"
146+
color="#841584"
147+
accessibilityLabel="Learn more about this purple button"
148+
/>
149+
<Button
150+
title="Press me"
151+
onPress={() =>
152+
Alert.alert('Simple Button pressed', 'Hello', [
153+
{
154+
text: 'Cancel',
155+
},
156+
{
157+
text: 'OK',
158+
onPress: () => BackHandler.exitApp(),
159+
},
160+
])
161+
}
162+
/>
163+
<Text>ipsum dolor, sit amet consectetu</Text>
164+
<View
165+
style={{ backgroundColor: 'dodgerblue', width: '50%', height: 70 }}
166+
></View> */}
167+
<Button
168+
title="Login with Biometrics"
169+
color="#000"
170+
onPress={handleBiometricAuth}
171+
/>
172+
<StatusBar style="auto" />
173+
</View>
174+
</ScrollView>
175+
</SafeAreaView>
11176
);
12177
}
13178

14179
const styles = StyleSheet.create({
180+
scrollView: {
181+
flex: 1,
182+
alignItems: 'center',
183+
justifyContent: 'center',
184+
},
15185
container: {
16186
flex: 1,
17-
backgroundColor: '#fff',
187+
backgroundColor: '#eee',
18188
alignItems: 'center',
19189
justifyContent: 'center',
20190
},
191+
textInput: {},
21192
});

app.json

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
"name": "expo-app",
44
"slug": "expo-app",
55
"version": "1.0.0",
6-
"orientation": "portrait",
6+
"orientation": "default",
77
"icon": "./assets/icon.png",
88
"splash": {
99
"image": "./assets/splash.png",
@@ -27,6 +27,7 @@
2727
},
2828
"web": {
2929
"favicon": "./assets/favicon.png"
30-
}
30+
},
31+
"description": ""
3132
}
32-
}
33+
}

package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,9 @@
88
"eject": "expo eject"
99
},
1010
"dependencies": {
11+
"@react-native-community/hooks": "^2.6.0",
1112
"expo": "~40.0.0",
13+
"expo-local-authentication": "~9.5.0",
1214
"expo-status-bar": "~1.0.3",
1315
"react": "16.13.1",
1416
"react-dom": "16.13.1",

yarn.lock

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1730,6 +1730,11 @@
17301730
sudo-prompt "^9.0.0"
17311731
wcwidth "^1.0.1"
17321732

1733+
"@react-native-community/hooks@^2.6.0":
1734+
version "2.6.0"
1735+
resolved "https://registry.yarnpkg.com/@react-native-community/hooks/-/hooks-2.6.0.tgz#dd5f19601eb3684c6bcdd3df3d0c04cf44c24cff"
1736+
integrity sha512-emBGKvhJ0h++lLJQ5ejsj+od9G67nEaihjvfSx7/JWvNrQGAhP9U0OZqgb9dkKzor9Ufaj9SGt8RNY97cGzttw==
1737+
17331738
"@types/istanbul-lib-coverage@*", "@types/istanbul-lib-coverage@^2.0.0":
17341739
version "2.0.3"
17351740
resolved "https://registry.yarnpkg.com/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.3.tgz#4ba8ddb720221f432e443bd5f9117fd22cfd4762"
@@ -3024,6 +3029,13 @@ expo-linking@~2.0.1:
30243029
qs "^6.5.0"
30253030
url-parse "^1.4.4"
30263031

3032+
expo-local-authentication@~9.5.0:
3033+
version "9.5.0"
3034+
resolved "https://registry.yarnpkg.com/expo-local-authentication/-/expo-local-authentication-9.5.0.tgz#541167186539cb4af288cdeaf8460053922b42eb"
3035+
integrity sha512-d7T8cfXNig8rpyiw4u/2vHj2zhtThOTxrLUwx4GqFxDYqFyOJ5q5Q3lXFd10Tr5paMQvnskS0fAj70aG7Eb76g==
3036+
dependencies:
3037+
invariant "^2.2.4"
3038+
30273039
expo-location@~10.0.0:
30283040
version "10.0.0"
30293041
resolved "https://registry.yarnpkg.com/expo-location/-/expo-location-10.0.0.tgz#2923411649434f2f079343b163b13c5c9eee8b2d"

0 commit comments

Comments
 (0)