├── assets ├── icon.png ├── favicon.png ├── splash.png └── adaptive-icon.png ├── babel.config.js ├── .expo-shared ├── assets.json └── README.md ├── .gitignore ├── package.json ├── app.json └── App.js /assets/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ejirocodes/React-Native-Local-Authentication-using-Biometrics/HEAD/assets/icon.png -------------------------------------------------------------------------------- /assets/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ejirocodes/React-Native-Local-Authentication-using-Biometrics/HEAD/assets/favicon.png -------------------------------------------------------------------------------- /assets/splash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ejirocodes/React-Native-Local-Authentication-using-Biometrics/HEAD/assets/splash.png -------------------------------------------------------------------------------- /assets/adaptive-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ejirocodes/React-Native-Local-Authentication-using-Biometrics/HEAD/assets/adaptive-icon.png -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = function(api) { 2 | api.cache(true); 3 | return { 4 | presets: ['babel-preset-expo'], 5 | }; 6 | }; 7 | -------------------------------------------------------------------------------- /.expo-shared/assets.json: -------------------------------------------------------------------------------- 1 | { 2 | "12bb71342c6255bbf50437ec8f4441c083f47cdb74bd89160c15e4f43e52a1cb": true, 3 | "40b842e832070c58deac6aa9e08fa459302ee3f9da492c7e77d93d2fbf4a56fd": true 4 | } 5 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/**/* 2 | .expo/* 3 | npm-debug.* 4 | *.jks 5 | *.p8 6 | *.p12 7 | *.key 8 | *.mobileprovision 9 | *.orig.* 10 | web-build/ 11 | 12 | # macOS 13 | .DS_Store 14 | -------------------------------------------------------------------------------- /.expo-shared/README.md: -------------------------------------------------------------------------------- 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. 12 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "main": "node_modules/expo/AppEntry.js", 3 | "scripts": { 4 | "start": "expo start", 5 | "android": "expo start --android", 6 | "ios": "expo start --ios", 7 | "web": "expo start --web", 8 | "eject": "expo eject" 9 | }, 10 | "dependencies": { 11 | "expo": "~40.0.0", 12 | "expo-local-authentication": "~9.5.0", 13 | "expo-status-bar": "~1.0.3", 14 | "react": "16.13.1", 15 | "react-dom": "16.13.1", 16 | "react-native": "https://github.com/expo/react-native/archive/sdk-40.0.1.tar.gz", 17 | "react-native-web": "~0.13.12" 18 | }, 19 | "devDependencies": { 20 | "@babel/core": "^7.9.0" 21 | }, 22 | "private": true 23 | } 24 | -------------------------------------------------------------------------------- /app.json: -------------------------------------------------------------------------------- 1 | { 2 | "expo": { 3 | "name": "expo-app", 4 | "slug": "expo-app", 5 | "version": "1.0.0", 6 | "orientation": "default", 7 | "icon": "./assets/icon.png", 8 | "splash": { 9 | "image": "./assets/splash.png", 10 | "resizeMode": "contain", 11 | "backgroundColor": "#ffffff" 12 | }, 13 | "updates": { 14 | "fallbackToCacheTimeout": 0 15 | }, 16 | "assetBundlePatterns": [ 17 | "**/*" 18 | ], 19 | "ios": { 20 | "supportsTablet": true 21 | }, 22 | "android": { 23 | "adaptiveIcon": { 24 | "foregroundImage": "./assets/adaptive-icon.png", 25 | "backgroundColor": "#FFFFFF" 26 | } 27 | }, 28 | "web": { 29 | "favicon": "./assets/favicon.png" 30 | }, 31 | "description": "" 32 | } 33 | } -------------------------------------------------------------------------------- /App.js: -------------------------------------------------------------------------------- 1 | import { StatusBar } from 'expo-status-bar'; 2 | import React, { useState, useEffect } from 'react'; 3 | import { 4 | StyleSheet, 5 | Text, 6 | View, 7 | SafeAreaView, 8 | Button, 9 | TouchableHighlight, 10 | Alert, 11 | StatusBar as RnStatusBar, 12 | } from 'react-native'; 13 | import * as LocalAuthentication from 'expo-local-authentication'; 14 | 15 | export default function App() { 16 | const [isBiometricSupported, setIsBiometricSupported] = useState(false); 17 | 18 | // Check if hardware supports biometrics 19 | useEffect(() => { 20 | (async () => { 21 | const compatible = await LocalAuthentication.hasHardwareAsync(); 22 | setIsBiometricSupported(compatible); 23 | })(); 24 | }); 25 | 26 | const fallBackToDefaultAuth = () => { 27 | console.log('fall back to password authentication'); 28 | }; 29 | 30 | const alertComponent = (title, mess, btnTxt, btnFunc) => { 31 | return Alert.alert(title, mess, [ 32 | { 33 | text: btnTxt, 34 | onPress: btnFunc, 35 | }, 36 | ]); 37 | }; 38 | 39 | const handleBiometricAuth = async () => { 40 | // Check if hardware supports biometrics 41 | const isBiometricAvailable = await LocalAuthentication.hasHardwareAsync(); 42 | 43 | // Fallback to default authentication method (password) if Fingerprint is not available 44 | if (!isBiometricAvailable) 45 | return alertComponent( 46 | 'Please enter your password', 47 | 'Biometric Authentication not supported', 48 | 'OK', 49 | () => fallBackToDefaultAuth() 50 | ); 51 | 52 | // Check Biometrics types available (Fingerprint, Facial recognition, Iris recognition) 53 | let supportedBiometrics; 54 | if (isBiometricAvailable) 55 | supportedBiometrics = await LocalAuthentication.supportedAuthenticationTypesAsync(); 56 | 57 | // Check Biometrics are saved locally in user's device 58 | const savedBiometrics = await LocalAuthentication.isEnrolledAsync(); 59 | if (!savedBiometrics) 60 | return alertComponent( 61 | 'Biometric record not found', 62 | 'Please login with your password', 63 | 'OK', 64 | () => fallBackToDefaultAuth() 65 | ); 66 | 67 | // Authenticate use with Biometrics (Fingerprint, Facial recognition, Iris recognition) 68 | 69 | const biometricAuth = await LocalAuthentication.authenticateAsync({ 70 | promptMessage: 'Login with Biometrics', 71 | cancelLabel: 'Cancel', 72 | disableDeviceFallback: true, 73 | }); 74 | // Log the user in on success 75 | if (biometricAuth) console.log('success'); 76 | 77 | console.log({ isBiometricAvailable }); 78 | console.log({ supportedBiometrics }); 79 | console.log({ savedBiometrics }); 80 | console.log({ biometricAuth }); 81 | }; 82 | return ( 83 | 84 | 85 | 86 | {isBiometricSupported 87 | ? 'Your device is compatible with Biometrics' 88 | : 'Face or Fingerprint scanner is available on this device'} 89 | 90 | 91 | 96 |