Passcode: Save last passcode timeout (#2876)

This commit is contained in:
Alexander Zinchuk 2023-03-30 18:25:40 -05:00
parent 7845ed4e1e
commit e3dd98882b
6 changed files with 26 additions and 13 deletions

View File

@ -30,8 +30,6 @@ type StateProps = {
passcodeSettings: GlobalState['passcode'];
};
const MAX_INVALID_ATTEMPTS = 5;
const TIMEOUT_RESET_INVALID_ATTEMPTS_MS = 180000; // 3 minutes
const ICON_SIZE = 160;
const LockScreen: FC<OwnProps & StateProps> = ({
@ -47,6 +45,7 @@ const LockScreen: FC<OwnProps & StateProps> = ({
const {
invalidAttemptsCount,
timeoutUntil,
isLoading,
} = passcodeSettings;
@ -56,19 +55,14 @@ const LockScreen: FC<OwnProps & StateProps> = ({
const [isSignOutDialogOpen, openSignOutConfirmation, closeSignOutConfirmation] = useFlag(false);
const { shouldRender } = useShowTransition(isLocked);
useTimeout(
resetInvalidUnlockAttempts,
invalidAttemptsCount && invalidAttemptsCount >= MAX_INVALID_ATTEMPTS
? TIMEOUT_RESET_INVALID_ATTEMPTS_MS
: undefined,
);
useTimeout(resetInvalidUnlockAttempts, timeoutUntil ? timeoutUntil - Date.now() : undefined);
const handleClearError = useCallback(() => {
setValidationError('');
}, []);
const handleSubmit = useCallback((passcode: string) => {
if (invalidAttemptsCount && invalidAttemptsCount >= MAX_INVALID_ATTEMPTS) {
if (timeoutUntil !== undefined) {
setValidationError(lang('FloodWait'));
return;
}
@ -78,15 +72,15 @@ const LockScreen: FC<OwnProps & StateProps> = ({
logInvalidUnlockAttempt();
setValidationError(lang('lng_passcode_wrong'));
});
}, [invalidAttemptsCount, lang, logInvalidUnlockAttempt, unlockScreen]);
}, [lang, timeoutUntil]);
useEffect(() => {
if (invalidAttemptsCount && invalidAttemptsCount >= MAX_INVALID_ATTEMPTS) {
if (timeoutUntil !== undefined) {
setValidationError(lang('FloodWait'));
} else if (invalidAttemptsCount === 0) {
setValidationError('');
}
}, [invalidAttemptsCount, lang]);
}, [timeoutUntil, lang, invalidAttemptsCount]);
const handleSignOutMessage = useCallback(() => {
closeSignOutConfirmation();

View File

@ -252,6 +252,7 @@ addActionHandler('lockScreen', async (global): Promise<void> => {
{
isScreenLocked: true,
invalidAttemptsCount: 0,
timeoutUntil: undefined,
},
);
setGlobal(global);

View File

@ -90,15 +90,23 @@ addActionHandler('decryptSession', (global, actions, payload): ActionReturnType
});
});
const MAX_INVALID_ATTEMPTS = 5;
const TIMEOUT_RESET_INVALID_ATTEMPTS_MS = 1000 * 15;// 180000; // 3 minutes
addActionHandler('logInvalidUnlockAttempt', (global): ActionReturnType => {
const invalidAttemptsCount = (global.passcode?.invalidAttemptsCount ?? 0) + 1;
return updatePasscodeSettings(global, {
invalidAttemptsCount: (global.passcode?.invalidAttemptsCount ?? 0) + 1,
invalidAttemptsCount,
timeoutUntil: (invalidAttemptsCount >= MAX_INVALID_ATTEMPTS
? Date.now() + TIMEOUT_RESET_INVALID_ATTEMPTS_MS : undefined),
});
});
addActionHandler('resetInvalidUnlockAttempts', (global): ActionReturnType => {
return updatePasscodeSettings(global, {
invalidAttemptsCount: 0,
timeoutUntil: undefined,
});
});

View File

@ -357,6 +357,7 @@ export function serializeGlobal<T extends GlobalState>(global: T) {
'isScreenLocked',
'hasPasscode',
'invalidAttemptsCount',
'timeoutUntil',
]),
};

View File

@ -592,6 +592,7 @@ export type GlobalState = {
isScreenLocked?: boolean;
hasPasscode?: boolean;
error?: string;
timeoutUntil?: number;
invalidAttemptsCount?: number;
invalidAttemptError?: string;
isLoading?: boolean;

View File

@ -20,6 +20,8 @@ export async function setupPasscode(passcode: string) {
export async function encryptSession(sessionJson?: string, globalJson?: string) {
if (!currentPasscodeHash) {
// eslint-disable-next-line no-console
console.error('[api/passcode] Missing current passcode');
throw new Error('[api/passcode] Missing current passcode');
}
@ -41,6 +43,8 @@ export async function encryptSession(sessionJson?: string, globalJson?: string)
export async function decryptSessionByCurrentHash() {
if (!currentPasscodeHash) {
// eslint-disable-next-line no-console
console.error('[api/passcode] Missing current passcode');
throw new Error('[api/passcode] Missing current passcode');
}
@ -50,6 +54,8 @@ export async function decryptSessionByCurrentHash() {
]);
if (!sessionEncrypted || !globalEncrypted) {
// eslint-disable-next-line no-console
console.error('[api/passcode] Missing required stored fields');
throw new Error('[api/passcode] Missing required stored fields');
}
@ -70,6 +76,8 @@ export async function decryptSession(passcode: string) {
]);
if (!sessionEncrypted || !globalEncrypted) {
// eslint-disable-next-line no-console
console.error('[api/passcode] Missing required stored fields');
throw new Error('[api/passcode] Missing required stored fields');
}