Skip to content
Merged
Show file tree
Hide file tree
Changes from 10 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
1 change: 1 addition & 0 deletions app/constants/localAuthentication.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
export const PASSCODE_KEY = 'kPasscode';
export const LOCKED_OUT_TIMER_KEY = 'kLockedOutTimer';
export const ATTEMPTS_KEY = 'kAttempts';
export const BIOMETRY_ENABLED_KEY = 'kBiometryEnabled';

export const LOCAL_AUTHENTICATE_EMITTER = 'LOCAL_AUTHENTICATE';
export const CHANGE_PASSCODE_EMITTER = 'CHANGE_PASSCODE';
Expand Down
2 changes: 1 addition & 1 deletion app/lib/database/schema/servers.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ export default appSchema({
{ name: 'last_local_authenticated_session', type: 'number', isOptional: true },
{ name: 'auto_lock', type: 'boolean', isOptional: true },
{ name: 'auto_lock_time', type: 'number', isOptional: true },
{ name: 'biometry', type: 'boolean', isOptional: true },
{ name: 'biometry', type: 'boolean', isOptional: true }, // deprecated
{ name: 'unique_id', type: 'string', isOptional: true },
{ name: 'enterprise_modules', type: 'string', isOptional: true },
{ name: 'e2e_enable', type: 'boolean', isOptional: true }
Expand Down
15 changes: 15 additions & 0 deletions app/sagas/init.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { put, takeLatest } from 'redux-saga/effects';
import RNBootSplash from 'react-native-bootsplash';

import { BIOMETRY_ENABLED_KEY } from '../constants/localAuthentication';
import UserPreferences from '../lib/userPreferences';
import { selectServerRequest } from '../actions/server';
import { setAllPreferences } from '../actions/sortPreferences';
Expand All @@ -16,11 +17,25 @@ export const initLocalSettings = function* initLocalSettings() {
yield put(setAllPreferences(sortPreferences));
};

const BIOMETRY_MIGRATION_KEY = 'kBiometryMigration';

const restore = function* restore() {
try {
const server = yield UserPreferences.getStringAsync(RocketChat.CURRENT_SERVER);
let userId = yield UserPreferences.getStringAsync(`${RocketChat.TOKEN_KEY}-${server}`);

// Migration biometry setting from WatermelonDB to MMKV
// TODO: remove it after a few versions
Comment thread
reinaldonetof marked this conversation as resolved.
const hasMigratedBiometry = yield UserPreferences.getBoolAsync(BIOMETRY_MIGRATION_KEY);
if (!hasMigratedBiometry) {
const serversDB = database.servers;
const serversCollection = serversDB.get('servers');
const servers = yield serversCollection.query().fetch();
const isBiometryEnabled = servers.some(server => !!server.biometry);
yield UserPreferences.setBoolAsync(BIOMETRY_ENABLED_KEY, isBiometryEnabled);
yield UserPreferences.setBoolAsync(BIOMETRY_MIGRATION_KEY, true);
}

if (!server) {
yield put(appStart({ root: ROOT_OUTSIDE }));
} else if (!userId) {
Expand Down
34 changes: 11 additions & 23 deletions app/utils/localAuthentication.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import store from '../lib/createStore';
import database from '../lib/database';
import {
ATTEMPTS_KEY,
BIOMETRY_ENABLED_KEY,
CHANGE_PASSCODE_EMITTER,
LOCAL_AUTHENTICATE_EMITTER,
LOCKED_OUT_TIMER_KEY,
Expand Down Expand Up @@ -72,32 +73,18 @@ export const biometryAuth = (force?: boolean): Promise<LocalAuthentication.Local
* It'll help us to get the permission to use FaceID
* and enable/disable the biometry when user put their first passcode
*/
const checkBiometry = async (serverRecord: TServerModel) => {
const serversDB = database.servers;

const checkBiometry = async () => {
const result = await biometryAuth(true);
await serversDB.write(async () => {
try {
await serverRecord.update(record => {
record.biometry = !!result?.success;
});
} catch {
// Do nothing
}
});
const isBiometryEnabled = !!result?.success;
await UserPreferences.setBoolAsync(BIOMETRY_ENABLED_KEY, isBiometryEnabled);
return isBiometryEnabled;
};

export const checkHasPasscode = async ({
force = true,
serverRecord
}: {
force?: boolean;
serverRecord: TServerModel;
}): Promise<{ newPasscode?: boolean } | void> => {
export const checkHasPasscode = async ({ force = true }: { force?: boolean }): Promise<{ newPasscode?: boolean } | void> => {
const storedPasscode = await UserPreferences.getStringAsync(PASSCODE_KEY);
if (!storedPasscode) {
await changePasscode({ force });
await checkBiometry(serverRecord);
await checkBiometry();
return Promise.resolve({ newPasscode: true });
}
return Promise.resolve();
Expand All @@ -124,7 +111,7 @@ export const localAuthenticate = async (server: string): Promise<void> => {
}

// Check if the app has passcode
const result = await checkHasPasscode({ serverRecord });
const result = await checkHasPasscode({});

// `checkHasPasscode` results newPasscode = true if a passcode has been set
if (!result?.newPasscode) {
Expand All @@ -136,10 +123,11 @@ export const localAuthenticate = async (server: string): Promise<void> => {
// set isLocalAuthenticated to false
store.dispatch(setLocalAuthenticated(false));

let hasBiometry = false;
// let hasBiometry = false;
let hasBiometry = (await UserPreferences.getBoolAsync(BIOMETRY_ENABLED_KEY)) ?? false;

// if biometry is enabled on the app
if (serverRecord.biometry) {
if (hasBiometry) {
const isEnrolled = await LocalAuthentication.isEnrolledAsync();
hasBiometry = isEnrolled;
}
Expand Down
31 changes: 11 additions & 20 deletions app/views/ScreenLockConfigView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,11 @@ import StatusBar from '../containers/StatusBar';
import * as List from '../containers/List';
import database from '../lib/database';
import { changePasscode, checkHasPasscode, supportedBiometryLabel } from '../utils/localAuthentication';
import { DEFAULT_AUTO_LOCK } from '../constants/localAuthentication';
import { BIOMETRY_ENABLED_KEY, DEFAULT_AUTO_LOCK } from '../constants/localAuthentication';
import SafeAreaView from '../containers/SafeAreaView';
import { events, logEvent } from '../utils/log';
import { TServerModel } from '../definitions/IServer';
import userPreferences from '../lib/userPreferences';

const DEFAULT_BIOMETRY = false;

Expand All @@ -34,7 +35,7 @@ interface IScreenLockConfigViewProps {
interface IScreenLockConfigViewState {
autoLock: boolean;
autoLockTime?: number | null;
biometry?: boolean;
biometry: boolean;
biometryLabel: string | null;
}

Expand Down Expand Up @@ -91,43 +92,30 @@ class ScreenLockConfigView extends React.Component<IScreenLockConfigViewProps, I
const { server } = this.props;
const serversDB = database.servers;
const serversCollection = serversDB.get('servers');
const hasBiometry = (await userPreferences.getBoolAsync(BIOMETRY_ENABLED_KEY)) ?? DEFAULT_BIOMETRY;
try {
this.serverRecord = await serversCollection.find(server);
this.setState({
autoLock: this.serverRecord?.autoLock,
autoLockTime: this.serverRecord?.autoLockTime === null ? DEFAULT_AUTO_LOCK : this.serverRecord?.autoLockTime,
biometry: this.serverRecord.biometry === null ? DEFAULT_BIOMETRY : this.serverRecord.biometry
biometry: hasBiometry
});
} catch (error) {
// Do nothing
}

const biometryLabel = await supportedBiometryLabel();
this.setState({ biometryLabel });

this.observe();
};

/*
* We should observe biometry value
* because it can be changed by PasscodeChange
* when the user set his first passcode
*/
observe = () => {
this.observable = this.serverRecord?.observe()?.subscribe(({ biometry }) => {
this.setState({ biometry: !!biometry });
});
};

save = async () => {
logEvent(events.SLC_SAVE_SCREEN_LOCK);
const { autoLock, autoLockTime, biometry } = this.state;
const { autoLock, autoLockTime } = this.state;
const serversDB = database.servers;
await serversDB.write(async () => {
await this.serverRecord?.update(record => {
record.autoLock = autoLock;
record.autoLockTime = autoLockTime === null ? DEFAULT_AUTO_LOCK : autoLockTime;
record.biometry = biometry === null ? DEFAULT_BIOMETRY : biometry;
});
});
};
Expand All @@ -145,7 +133,7 @@ class ScreenLockConfigView extends React.Component<IScreenLockConfigViewProps, I
const { autoLock } = this.state;
if (autoLock) {
try {
await checkHasPasscode({ force: false, serverRecord: this.serverRecord! });
await checkHasPasscode({ force: false });
} catch {
this.toggleAutoLock();
}
Expand All @@ -159,7 +147,10 @@ class ScreenLockConfigView extends React.Component<IScreenLockConfigViewProps, I
logEvent(events.SLC_TOGGLE_BIOMETRY);
this.setState(
({ biometry }) => ({ biometry: !biometry }),
() => this.save()
async () => {
const { biometry } = this.state;
await userPreferences.setBoolAsync(BIOMETRY_ENABLED_KEY, biometry);
}
);
};

Expand Down