2020-05-08 17:04:37 +00:00
import * as LocalAuthentication from 'expo-local-authentication' ;
import RNBootSplash from 'react-native-bootsplash' ;
import AsyncStorage from '@react-native-community/async-storage' ;
import { sha256 } from 'js-sha256' ;
2022-02-21 16:38:49 +00:00
import moment from 'moment' ;
2020-05-08 17:04:37 +00:00
2020-08-19 17:14:22 +00:00
import UserPreferences from '../lib/userPreferences' ;
2022-02-09 21:16:20 +00:00
import { store } from '../lib/auxStore' ;
2020-05-08 17:04:37 +00:00
import database from '../lib/database' ;
2022-02-21 16:38:49 +00:00
import { getServerTimeSync } from '../lib/rocketchat/services/getServerTimeSync' ;
2020-05-08 17:04:37 +00:00
import {
2021-09-13 20:41:05 +00:00
ATTEMPTS_KEY ,
2022-02-08 16:44:34 +00:00
BIOMETRY_ENABLED_KEY ,
2021-09-13 20:41:05 +00:00
CHANGE_PASSCODE_EMITTER ,
LOCAL_AUTHENTICATE_EMITTER ,
LOCKED_OUT_TIMER_KEY ,
PASSCODE_KEY
2020-05-08 17:04:37 +00:00
} from '../constants/localAuthentication' ;
import I18n from '../i18n' ;
2020-06-09 20:19:54 +00:00
import { setLocalAuthenticated } from '../actions/login' ;
2022-01-12 12:54:04 +00:00
import { TServerModel } from '../definitions/IServer' ;
2021-09-13 20:41:05 +00:00
import EventEmitter from './events' ;
import { isIOS } from './deviceInfo' ;
2020-05-08 17:04:37 +00:00
2022-02-21 16:38:49 +00:00
export const saveLastLocalAuthenticationSession = async (
server : string ,
serverRecord? : TServerModel ,
timesync? : number | null
) : Promise < void > = > {
if ( ! timesync ) {
timesync = new Date ( ) . getTime ( ) ;
}
2020-05-08 17:04:37 +00:00
const serversDB = database . servers ;
2021-02-26 16:25:51 +00:00
const serversCollection = serversDB . get ( 'servers' ) ;
2022-01-12 12:54:04 +00:00
await serversDB . write ( async ( ) = > {
2020-05-08 17:04:37 +00:00
try {
if ( ! serverRecord ) {
2022-02-21 16:38:49 +00:00
serverRecord = await serversCollection . find ( server ) ;
2020-05-08 17:04:37 +00:00
}
2022-02-21 16:38:49 +00:00
const time = timesync || 0 ;
2021-09-13 20:41:05 +00:00
await serverRecord . update ( record = > {
2022-02-21 16:38:49 +00:00
record . lastLocalAuthenticatedSession = new Date ( time ) ;
2020-05-08 17:04:37 +00:00
} ) ;
} catch ( e ) {
// Do nothing
}
} ) ;
} ;
2022-01-12 12:54:04 +00:00
export const resetAttempts = ( ) : Promise < void > = > AsyncStorage . multiRemove ( [ LOCKED_OUT_TIMER_KEY , ATTEMPTS_KEY ] ) ;
2020-05-08 17:04:37 +00:00
2022-01-12 12:54:04 +00:00
const openModal = ( hasBiometry : boolean ) = >
new Promise < void > ( resolve = > {
2021-09-13 20:41:05 +00:00
EventEmitter . emit ( LOCAL_AUTHENTICATE_EMITTER , {
submit : ( ) = > resolve ( ) ,
hasBiometry
} ) ;
2020-05-08 17:04:37 +00:00
} ) ;
2022-01-12 12:54:04 +00:00
const openChangePasscodeModal = ( { force } : { force : boolean } ) = >
new Promise < string > ( ( resolve , reject ) = > {
2021-09-13 20:41:05 +00:00
EventEmitter . emit ( CHANGE_PASSCODE_EMITTER , {
2022-01-12 12:54:04 +00:00
submit : ( passcode : string ) = > resolve ( passcode ) ,
2021-09-13 20:41:05 +00:00
cancel : ( ) = > reject ( ) ,
force
} ) ;
2020-05-08 17:04:37 +00:00
} ) ;
2022-01-12 12:54:04 +00:00
export const changePasscode = async ( { force = false } : { force : boolean } ) : Promise < void > = > {
2020-05-08 17:04:37 +00:00
const passcode = await openChangePasscodeModal ( { force } ) ;
2022-03-09 19:41:26 +00:00
UserPreferences . setString ( PASSCODE_KEY , sha256 ( passcode ) ) ;
2020-05-08 17:04:37 +00:00
} ;
2022-01-12 12:54:04 +00:00
export const biometryAuth = ( force? : boolean ) : Promise < LocalAuthentication.LocalAuthenticationResult > = >
2021-09-13 20:41:05 +00:00
LocalAuthentication . authenticateAsync ( {
disableDeviceFallback : true ,
cancelLabel : force ? I18n . t ( 'Dont_activate' ) : I18n . t ( 'Local_authentication_biometry_fallback' ) ,
promptMessage : I18n.t ( 'Local_authentication_biometry_title' )
} ) ;
2020-05-08 17:04:37 +00:00
/ *
* It ' ll help us to get the permission to use FaceID
* and enable / disable the biometry when user put their first passcode
2021-09-13 20:41:05 +00:00
* /
2022-02-08 16:44:34 +00:00
const checkBiometry = async ( ) = > {
2020-05-08 17:04:37 +00:00
const result = await biometryAuth ( true ) ;
2022-02-08 16:44:34 +00:00
const isBiometryEnabled = ! ! result ? . success ;
2022-03-09 19:41:26 +00:00
UserPreferences . setBool ( BIOMETRY_ENABLED_KEY , isBiometryEnabled ) ;
2022-02-08 16:44:34 +00:00
return isBiometryEnabled ;
2020-05-08 17:04:37 +00:00
} ;
2022-02-08 16:44:34 +00:00
export const checkHasPasscode = async ( { force = true } : { force? : boolean } ) : Promise < { newPasscode? : boolean } | void > = > {
2022-03-09 19:41:26 +00:00
const storedPasscode = UserPreferences . getString ( PASSCODE_KEY ) ;
2020-05-08 17:04:37 +00:00
if ( ! storedPasscode ) {
await changePasscode ( { force } ) ;
2022-02-08 16:44:34 +00:00
await checkBiometry ( ) ;
2020-05-08 17:04:37 +00:00
return Promise . resolve ( { newPasscode : true } ) ;
}
return Promise . resolve ( ) ;
} ;
2022-01-12 12:54:04 +00:00
export const localAuthenticate = async ( server : string ) : Promise < void > = > {
2020-05-08 17:04:37 +00:00
const serversDB = database . servers ;
2021-02-26 16:25:51 +00:00
const serversCollection = serversDB . get ( 'servers' ) ;
2020-05-08 17:04:37 +00:00
2022-01-12 12:54:04 +00:00
let serverRecord : TServerModel ;
2020-05-08 17:04:37 +00:00
try {
2022-01-12 12:54:04 +00:00
serverRecord = ( await serversCollection . find ( server ) ) as TServerModel ;
2020-05-08 17:04:37 +00:00
} catch ( error ) {
return Promise . reject ( ) ;
}
// if screen lock is enabled
if ( serverRecord ? . autoLock ) {
2022-02-21 16:38:49 +00:00
// Get time from server
const timesync = await getServerTimeSync ( server ) ;
2020-05-08 17:04:37 +00:00
// Make sure splash screen has been hidden
2021-09-14 18:31:36 +00:00
try {
await RNBootSplash . hide ( ) ;
} catch {
// Do nothing
}
2020-05-08 17:04:37 +00:00
// Check if the app has passcode
2022-02-08 16:44:34 +00:00
const result = await checkHasPasscode ( { } ) ;
2020-05-08 17:04:37 +00:00
// `checkHasPasscode` results newPasscode = true if a passcode has been set
if ( ! result ? . newPasscode ) {
// diff to last authenticated session
2022-02-21 16:38:49 +00:00
const diffToLastSession = moment ( timesync ) . diff ( serverRecord ? . lastLocalAuthenticatedSession , 'seconds' ) ;
2020-05-08 17:04:37 +00:00
2022-02-21 16:38:49 +00:00
// if it was not possible to get `timesync` from server or the last authenticated session is older than the configured auto lock time, authentication is required
if ( ! timesync || ( serverRecord ? . autoLockTime && diffToLastSession >= serverRecord . autoLockTime ) ) {
2021-03-05 16:10:21 +00:00
// set isLocalAuthenticated to false
store . dispatch ( setLocalAuthenticated ( false ) ) ;
2022-02-08 16:44:34 +00:00
// let hasBiometry = false;
2022-03-09 19:41:26 +00:00
let hasBiometry = UserPreferences . getBool ( BIOMETRY_ENABLED_KEY ) ? ? false ;
2020-05-08 17:04:37 +00:00
// if biometry is enabled on the app
2022-02-08 16:44:34 +00:00
if ( hasBiometry ) {
2020-05-08 17:04:37 +00:00
const isEnrolled = await LocalAuthentication . isEnrolledAsync ( ) ;
hasBiometry = isEnrolled ;
}
// Authenticate
await openModal ( hasBiometry ) ;
2020-06-09 20:19:54 +00:00
// set isLocalAuthenticated to true
store . dispatch ( setLocalAuthenticated ( true ) ) ;
2020-05-08 17:04:37 +00:00
}
}
await resetAttempts ( ) ;
2022-02-21 16:38:49 +00:00
await saveLastLocalAuthenticationSession ( server , serverRecord , timesync ) ;
2020-05-08 17:04:37 +00:00
}
} ;
2022-01-12 12:54:04 +00:00
export const supportedBiometryLabel = async ( ) : Promise < string | null > = > {
2020-05-08 17:04:37 +00:00
try {
const enrolled = await LocalAuthentication . isEnrolledAsync ( ) ;
if ( ! enrolled ) {
return null ;
}
const supported = await LocalAuthentication . supportedAuthenticationTypesAsync ( ) ;
if ( supported . includes ( LocalAuthentication . AuthenticationType . FACIAL_RECOGNITION ) ) {
return isIOS ? 'FaceID' : I18n . t ( 'Local_authentication_facial_recognition' ) ;
}
if ( supported . includes ( LocalAuthentication . AuthenticationType . FINGERPRINT ) ) {
return isIOS ? 'TouchID' : I18n . t ( 'Local_authentication_fingerprint' ) ;
}
} catch {
// Do nothing
}
return null ;
} ;